Skip to content

Commit

Permalink
Pages: integrate with invenio-administration
Browse files Browse the repository at this point in the history
  • Loading branch information
Pineirin committed Feb 14, 2023
1 parent 918f6dc commit 7e4c2f0
Show file tree
Hide file tree
Showing 13 changed files with 509 additions and 24 deletions.
119 changes: 119 additions & 0 deletions invenio_pages/administration/views/pages.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2023 CERN.
#
# invenio-administration is free software; you can redistribute it and/or
# modify it under the terms of the MIT License; see LICENSE file for more
# details.

"""Invenio administration pages view module."""
from flask_babelex import lazy_gettext as _
from invenio_administration.views.base import (
AdminResourceDetailView,
AdminResourceEditView,
AdminResourceListView,
)


class PageListView(AdminResourceListView):
"""Configuration for pages list view."""

api_endpoint = "/pages"
name = "Pages"
resource_config = "pages_resource"
search_request_headers = {"Accept": "application/json"}
title = "Pages"
category = "Pages"
pid_path = "id"
icon = "file alternate outline"
template = "invenio_pages/administration/page-search.html"

display_search = True
display_delete = False
display_create = False
display_edit = True

item_field_list = {
"url": {"text": _("Url"), "order": 1},
"title": {"text": _("Title"), "order": 2},
"content": {"text": _("Content"), "order": 3},
"template_name": {"text": _("Template Name"), "order": 4},
"description": {"text": _("Description"), "order": 5},
"created": {"text": _("Created"), "order": 6},
"updated": {"text": _("Updated"), "order": 7},
}

search_config_name = "PAGES_SEARCH"
search_facets_config_name = "PAGES_FACETS"
search_sort_config_name = "PAGES_SORT_OPTIONS"


class PageEditView(AdminResourceEditView):
"""Configuration for page edit view."""

name = "pages_edit"
url = "/pages/<pid_value>/edit"
resource_config = "pages_resource"
pid_path = "id"
api_endpoint = "/pages"
title = "Edit Page set"

list_view_name = "Pages"

form_fields = {
"url": {
"order": 1,
"text": _("URL"),
"description": _("Relative path to the page."),
},
"title": {
"order": 2,
"text": _("Title"),
"description": _("Title of the page."),
},
"content": {
"order": 3,
"text": _("Content"),
"description": _("Content displayed by the page."),
"rows": 10,
},
"template_name": {
"order": 4,
"text": _("Template name"),
"description": _("Jinja template used to display the page."),
},
"description": {
"order": 5,
"text": _("Description"),
"description": _("Description of the page"),
},
"created": {"order": 6},
"updated": {"order": 7},
}


class PageDetailView(AdminResourceDetailView):
"""Configuration for page detail view."""

url = "/pages/<pid_value>"
api_endpoint = "/pages"
name = "page-details"
resource_config = "pages_resource"
title = "Page"

display_edit = True
display_delete = False

list_view_name = "Pages"
pid_path = "id"

item_field_list = {
"id": {"text": _("Id"), "order": 1},
"url": {"text": _("Url"), "order": 2},
"title": {"text": _("Title"), "order": 3},
"content": {"text": _("Content"), "order": 4},
"template_name": {"text": _("Template Name"), "order": 5},
"description": {"text": _("Description"), "order": 6},
"created": {"text": _("Created"), "order": 7},
"updated": {"text": _("Updated"), "order": 8},
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
/*
* This file is part of Invenio.
* Copyright (C) 2023 CERN.
*
* Invenio is free software; you can redistribute it and/or modify it
* under the terms of the MIT License; see LICENSE file for more details.
*/

import PropTypes from "prop-types";
import React, { Component } from "react";
import { Table } from "semantic-ui-react";
import isEmpty from "lodash/isEmpty";
import { Actions } from "@js/invenio_administration";
import { withState } from "react-searchkit";
import { AdminUIRoutes } from "@js/invenio_administration";
import { Formatter } from "@js/invenio_administration";
import _truncate from "lodash/truncate";

class SearchResultItemComponent extends Component {
refreshAfterAction = () => {
const { updateQueryState, currentQueryState } = this.props;
updateQueryState(currentQueryState);
};

render() {
const {
title,
resourceName,
result,
columns,
displayEdit,
displayDelete,
actions,
idKeyPath,
resourceSchema,
listUIEndpoint,
} = this.props;
const resourceHasActions =
displayEdit || displayDelete || !isEmpty(actions);
result.content = _truncate(result.content, { length: 250 });

return (
<Table.Row>
{columns.map(([property, { text, order }], index) => {
return (
<Table.Cell
key={`${text}-${order}`}
data-label={text}
className="word-break-all"
>
{index === 0 && (
<a
href={AdminUIRoutes.detailsView(
listUIEndpoint,
result,
idKeyPath
)}
>
{result[property]}
</a>
)}
{index !== 0 && (
<Formatter
result={result}
resourceSchema={resourceSchema}
property={property}
/>
)}
</Table.Cell>
);
})}
{resourceHasActions && (
<Table.Cell collapsing>
<Actions
title={title}
resourceName={resourceName}
editUrl={AdminUIRoutes.editView(
listUIEndpoint,
result,
idKeyPath
)}
displayEdit={displayEdit}
displayDelete={displayDelete}
actions={actions}
resource={result}
idKeyPath={idKeyPath}
successCallback={this.refreshAfterAction}
listUIEndpoint={listUIEndpoint}
/>
</Table.Cell>
)}
</Table.Row>
);
}
}

SearchResultItemComponent.propTypes = {
title: PropTypes.string.isRequired,
resourceName: PropTypes.string.isRequired,
result: PropTypes.object.isRequired,
columns: PropTypes.array.isRequired,
displayDelete: PropTypes.bool,
displayEdit: PropTypes.bool,
actions: PropTypes.object,
updateQueryState: PropTypes.func.isRequired,
currentQueryState: PropTypes.object.isRequired,
idKeyPath: PropTypes.string.isRequired,
resourceSchema: PropTypes.object.isRequired,
listUIEndpoint: PropTypes.string.isRequired,
};

SearchResultItemComponent.defaultProps = {
displayDelete: false,
displayEdit: true,
actions: {},
};

export const SearchResultItem = withState(SearchResultItemComponent);
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* This file is part of Invenio.
* Copyright (C) 2023 CERN.
*
* Invenio is free software; you can redistribute it and/or modify it
* under the terms of the MIT License; see LICENSE file for more details.
*/

import PropTypes from "prop-types";
import React from "react";
import { Table } from "semantic-ui-react";
import isEmpty from "lodash/isEmpty";
import { i18next } from "@translations/invenio_administration/i18next";

export const SearchResultsContainer = ({
results,
columns,
displayEdit,
displayDelete,
actions,
}) => {
const resourceHasActions = displayEdit || displayDelete || !isEmpty(actions);

const columnWidth = {
"Url": 1,
"Title": 2,
"Content": 5,
"Template Name": 3,
"Description": 2,
"Created": 1,
"Updated": 1
}

return (
<Table>
<Table.Header>
<Table.Row>
{columns.map(([property, { text, order }], index) => {
const width = columnWidth[text];
return (
<Table.HeaderCell key={property + order} width={width}>
{text}
</Table.HeaderCell>
);
})}
{resourceHasActions && (
<Table.HeaderCell collapsing>{i18next.t("Actions")}</Table.HeaderCell>
)}
</Table.Row>
</Table.Header>
<Table.Body>{results}</Table.Body>
</Table>
);
};

SearchResultsContainer.propTypes = {
results: PropTypes.array.isRequired,
columns: PropTypes.array.isRequired,
displayEdit: PropTypes.bool,
displayDelete: PropTypes.bool,
actions: PropTypes.object.isRequired,
};

SearchResultsContainer.defaultProps = {
displayDelete: false,
displayEdit: true,
};
68 changes: 68 additions & 0 deletions invenio_pages/assets/semantic-ui/js/invenio_pages/search/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
* This file is part of Invenio.
* Copyright (C) 2023 CERN.
*
* Invenio is free software; you can redistribute it and/or modify it
* under the terms of the MIT License; see LICENSE file for more details.
*/

import { initDefaultSearchComponents } from "@js/invenio_administration";
import { createSearchAppInit } from "@js/invenio_search_ui";
import { SearchResultItem } from "./SearchResultItem";
import { SearchResultsContainer } from "./SearchResultsContainer";
import { parametrize } from "react-overridable";
import _get from "lodash/get";
import { NotificationController } from "@js/invenio_administration";

const domContainer = document.getElementById("invenio-search-config");

const sortColumns = (columns) =>
Object.entries(columns).sort((a, b) => a[1].order - b[1].order);
const title = JSON.parse(domContainer.dataset.title);
const resourceName = JSON.parse(domContainer.dataset.resourceName);
const columns = JSON.parse(domContainer.dataset.fields);
const sortedColumns = sortColumns(columns);
const displayEdit = JSON.parse(domContainer.dataset.displayEdit);
const displayDelete = JSON.parse(domContainer.dataset.displayDelete);
const displayRead = JSON.parse(domContainer.dataset.displayRead);
const actions = JSON.parse(domContainer.dataset.actions);
const apiEndpoint = _get(domContainer.dataset, "apiEndpoint");
const idKeyPath = JSON.parse(_get(domContainer.dataset, "pidPath", "pid"));
const listUIEndpoint = domContainer.dataset.listEndpoint;
const resourceSchema = JSON.parse(domContainer.dataset.resourceSchema);

const defaultComponents = initDefaultSearchComponents(domContainer);
const SearchResultItemWithConfig = parametrize(SearchResultItem, {
title: title,
resourceName: resourceName,
columns: sortedColumns,
displayRead: displayRead,
displayEdit: displayEdit,
displayDelete: displayDelete,
actions: actions,
apiEndpoint: apiEndpoint,
idKeyPath: idKeyPath,
listUIEndpoint: listUIEndpoint,
resourceSchema: resourceSchema,
});

const ResultsContainerWithConfig = parametrize(SearchResultsContainer, {
columns: sortedColumns,
displayEdit: displayEdit,
displayDelete: displayDelete,
actions: actions,
});

const overridenComponents = {
...defaultComponents,
"ResultsList.item": SearchResultItemWithConfig,
"ResultsList.container": ResultsContainerWithConfig,
};

createSearchAppInit(
overridenComponents,
true,
"invenio-search-config",
false,
NotificationController
);
Loading

0 comments on commit 7e4c2f0

Please sign in to comment.