diff --git a/invenio_administration/assets/semantic-ui/js/invenio_administration/src/actions/ActionModal.js b/invenio_administration/assets/semantic-ui/js/invenio_administration/src/actions/ActionModal.js index b9625f2..d4664b6 100644 --- a/invenio_administration/assets/semantic-ui/js/invenio_administration/src/actions/ActionModal.js +++ b/invenio_administration/assets/semantic-ui/js/invenio_administration/src/actions/ActionModal.js @@ -8,18 +8,19 @@ import PropTypes from "prop-types"; import React, { Component } from "react"; import { Modal } from "semantic-ui-react"; import Overridable from "react-overridable"; +import { buildUID } from "react-searchkit"; -class ActionModal extends Component { +export default class ActionModal extends Component { render() { - const { children, modalOpen, resource } = this.props; - + const { children, modalOpen, resource, appName } = this.props; return ( {children} @@ -33,11 +34,11 @@ ActionModal.propTypes = { children: PropTypes.object, modalOpen: PropTypes.bool, resource: PropTypes.object.isRequired, + appName: PropTypes.string, }; ActionModal.defaultProps = { modalOpen: false, children: null, + appName: "", }; - -export default Overridable.component("InvenioAdministration.ActionModal", ActionModal); diff --git a/invenio_administration/assets/semantic-ui/js/invenio_administration/src/actions/Actions.js b/invenio_administration/assets/semantic-ui/js/invenio_administration/src/actions/Actions.js index 9fbebd6..98bed02 100644 --- a/invenio_administration/assets/semantic-ui/js/invenio_administration/src/actions/Actions.js +++ b/invenio_administration/assets/semantic-ui/js/invenio_administration/src/actions/Actions.js @@ -21,12 +21,12 @@ export class Actions extends Component { editUrl, displayEdit, displayDelete, + appName, } = this.props; // if number of actions is greater than 3, we display all in a dropdown const displayAsDropdown = displayEdit && displayDelete && Object.keys(actions).length > 1; - if (displayAsDropdown) { return ( @@ -37,6 +37,7 @@ export class Actions extends Component { idKeyPath={idKeyPath} actions={actions} Element={Dropdown.Item} + appName={appName} trigger={ - - } - /> + + + + + } + /> + ); } } @@ -35,6 +53,7 @@ EditCmp.propTypes = { disable: PropTypes.func, disabledMessage: PropTypes.string, resource: PropTypes.object, + appName: PropTypes.string, }; EditCmp.defaultProps = { @@ -42,6 +61,5 @@ EditCmp.defaultProps = { disable: () => false, disabledMessage: i18next.t("Resource is not editable."), resource: undefined, + appName: "", }; - -export default Overridable.component("InvenioAdministration.EditAction", EditCmp); diff --git a/invenio_administration/assets/semantic-ui/js/invenio_administration/src/actions/ResourceActions.js b/invenio_administration/assets/semantic-ui/js/invenio_administration/src/actions/ResourceActions.js index 1cd9a4a..a59a703 100644 --- a/invenio_administration/assets/semantic-ui/js/invenio_administration/src/actions/ResourceActions.js +++ b/invenio_administration/assets/semantic-ui/js/invenio_administration/src/actions/ResourceActions.js @@ -51,7 +51,7 @@ export class ResourceActions extends Component { }; render() { - const { actions, Element, resource } = this.props; + const { actions, Element, resource, appName } = this.props; const { modalOpen, modalHeader, modalBody } = this.state; return ( <> @@ -68,7 +68,7 @@ export class ResourceActions extends Component { ); })} - + {modalHeader && {modalHeader}} {!_isEmpty(modalBody) && modalBody} @@ -86,9 +86,11 @@ ResourceActions.propTypes = { order: PropTypes.number.isRequired, }), Element: PropTypes.node, + appName: PropTypes.string, }; ResourceActions.defaultProps = { Element: Button, actions: undefined, + appName: "", }; diff --git a/invenio_administration/assets/semantic-ui/js/invenio_administration/src/components/BoolFormatter.js b/invenio_administration/assets/semantic-ui/js/invenio_administration/src/components/BoolFormatter.js index 8b90dc9..62863d4 100644 --- a/invenio_administration/assets/semantic-ui/js/invenio_administration/src/components/BoolFormatter.js +++ b/invenio_administration/assets/semantic-ui/js/invenio_administration/src/components/BoolFormatter.js @@ -10,14 +10,22 @@ import PropTypes from "prop-types"; import React from "react"; import { Icon } from "semantic-ui-react"; import Overridable from "react-overridable"; +import { buildUID } from "react-searchkit"; -class BoolFormatter extends React.Component { +export default class BoolFormatter extends React.Component { render() { - const { value, icon, color } = this.props; - if (!value) { - return null; - } - return ; + const { value, icon, color, appName } = this.props; + return ( + + {!value ? null : } + + ); } } @@ -25,14 +33,11 @@ BoolFormatter.propTypes = { value: PropTypes.string.isRequired, icon: PropTypes.string, color: PropTypes.string, + appName: PropTypes.string, }; BoolFormatter.defaultProps = { icon: "check", color: "green", + appName: "", }; - -export default Overridable.component( - "InvenioAdministration.BoolFormatter", - BoolFormatter -); diff --git a/invenio_administration/assets/semantic-ui/js/invenio_administration/src/components/ErrorPage.js b/invenio_administration/assets/semantic-ui/js/invenio_administration/src/components/ErrorPage.js index e52e2d0..9dd8cbf 100644 --- a/invenio_administration/assets/semantic-ui/js/invenio_administration/src/components/ErrorPage.js +++ b/invenio_administration/assets/semantic-ui/js/invenio_administration/src/components/ErrorPage.js @@ -3,12 +3,21 @@ import { Container, Header, Icon } from "semantic-ui-react"; import Overridable from "react-overridable"; import PropTypes from "prop-types"; import { i18next } from "@translations/invenio_administration/i18next"; +import { buildUID } from "react-searchkit"; -class ErrorPage extends Component { +export default class ErrorPage extends Component { render() { - const { errorCode, errorMessage, error, children } = this.props; + const { errorCode, errorMessage, error, children, appName } = this.props; return ( - + {error ? (
@@ -31,6 +40,7 @@ ErrorPage.propTypes = { errorMessage: PropTypes.string, error: PropTypes.bool, children: PropTypes.element, + appName: PropTypes.string, }; ErrorPage.defaultProps = { @@ -38,6 +48,5 @@ ErrorPage.defaultProps = { errorMessage: i18next.t("Server was not able to process your request."), error: false, children: undefined, + appName: "", }; - -export default Overridable.component("ErrorPage", ErrorPage); diff --git a/invenio_administration/assets/semantic-ui/js/invenio_administration/src/components/Formatter.js b/invenio_administration/assets/semantic-ui/js/invenio_administration/src/components/Formatter.js index 6600a12..bad9723 100644 --- a/invenio_administration/assets/semantic-ui/js/invenio_administration/src/components/Formatter.js +++ b/invenio_administration/assets/semantic-ui/js/invenio_administration/src/components/Formatter.js @@ -39,6 +39,11 @@ Formatter.propTypes = { resourceSchema: PropTypes.object.isRequired, result: PropTypes.object.isRequired, property: PropTypes.string.isRequired, + appName: PropTypes.string, +}; + +Formatter.defaultProps = { + appName: "", }; export default Formatter; diff --git a/invenio_administration/assets/semantic-ui/js/invenio_administration/src/components/Loader.js b/invenio_administration/assets/semantic-ui/js/invenio_administration/src/components/Loader.js index b58e140..95cd414 100644 --- a/invenio_administration/assets/semantic-ui/js/invenio_administration/src/components/Loader.js +++ b/invenio_administration/assets/semantic-ui/js/invenio_administration/src/components/Loader.js @@ -2,12 +2,19 @@ import React, { Component } from "react"; import Overridable from "react-overridable"; import { Loader as UILoader } from "semantic-ui-react"; import PropTypes from "prop-types"; +import { buildUID } from "react-searchkit"; -class Loader extends Component { +export default class Loader extends Component { render() { - const { isLoading, children } = this.props; + const { isLoading, children, appName } = this.props; return ( - + {isLoading ? ( ) : ( @@ -22,11 +29,11 @@ class Loader extends Component { Loader.propTypes = { isLoading: PropTypes.bool, children: PropTypes.node, + appName: PropTypes.string, }; Loader.defaultProps = { isLoading: false, children: null, + appName: "", }; - -export default Overridable.component("Loader", Loader); diff --git a/invenio_administration/assets/semantic-ui/js/invenio_administration/src/details/AdminDetailsView.js b/invenio_administration/assets/semantic-ui/js/invenio_administration/src/details/AdminDetailsView.js index eda30f5..25bbe0e 100644 --- a/invenio_administration/assets/semantic-ui/js/invenio_administration/src/details/AdminDetailsView.js +++ b/invenio_administration/assets/semantic-ui/js/invenio_administration/src/details/AdminDetailsView.js @@ -73,15 +73,17 @@ export default class AdminDetailsView extends Component { displayDelete, displayEdit, uiSchema, + appName, } = this.props; const { loading, data, error } = this.state; const sortedColumns = sortFields(resourceSchema); return ( - + @@ -101,13 +103,19 @@ export default class AdminDetailsView extends Component { idKeyPath={idKeyPath} successCallback={this.handleDelete} listUIEndpoint={listUIEndpoint} + appName={appName} /> - + {this.childrenWithData(data, columns)} @@ -131,9 +139,11 @@ AdminDetailsView.propTypes = { resourceSchema: PropTypes.object.isRequired, requestHeaders: PropTypes.object.isRequired, uiSchema: PropTypes.object.isRequired, + appName: PropTypes.string, }; AdminDetailsView.defaultProps = { actions: undefined, children: undefined, + appName: "", }; diff --git a/invenio_administration/assets/semantic-ui/js/invenio_administration/src/details/DetailsComponent.js b/invenio_administration/assets/semantic-ui/js/invenio_administration/src/details/DetailsComponent.js index 9f612b0..178b79a 100644 --- a/invenio_administration/assets/semantic-ui/js/invenio_administration/src/details/DetailsComponent.js +++ b/invenio_administration/assets/semantic-ui/js/invenio_administration/src/details/DetailsComponent.js @@ -9,10 +9,11 @@ import React, { Component } from "react"; import Overridable from "react-overridable"; import { Table } from "semantic-ui-react"; import Formatter from "../components/Formatter"; +import { buildUID } from "react-searchkit"; -class DetailsTable extends Component { +export default class DetailsTable extends Component { render() { - const { schema, data, uiSchema } = this.props; + const { schema, data, uiSchema, appName } = this.props; let fields = uiSchema ? uiSchema : schema; const tableRows = Object.entries(fields).map(([field, fieldSchema]) => { @@ -25,14 +26,25 @@ class DetailsTable extends Component { {" "} - + ); }); return ( - + {tableRows}
@@ -45,6 +57,9 @@ DetailsTable.propTypes = { data: PropTypes.object.isRequired, schema: PropTypes.object.isRequired, uiSchema: PropTypes.object.isRequired, + appName: PropTypes.string, }; -export default Overridable.component("DetailsTable", DetailsTable); +DetailsTable.defaultProps = { + appName: "", +}; diff --git a/invenio_administration/assets/semantic-ui/js/invenio_administration/src/details/details.js b/invenio_administration/assets/semantic-ui/js/invenio_administration/src/details/details.js index e0a90eb..903fa2b 100644 --- a/invenio_administration/assets/semantic-ui/js/invenio_administration/src/details/details.js +++ b/invenio_administration/assets/semantic-ui/js/invenio_administration/src/details/details.js @@ -24,6 +24,7 @@ const listUIEndpoint = domContainer.dataset.listEndpoint; const resourceSchema = JSON.parse(domContainer.dataset?.resourceSchema); const requestHeaders = JSON.parse(domContainer.dataset?.requestHeaders); const uiSchema = JSON.parse(domContainer.dataset?.uiConfig); +const appName = JSON.parse(domContainer.dataset?.appId); domContainer && ReactDOM.render( @@ -41,6 +42,7 @@ domContainer && resourceSchema={resourceSchema} requestHeaders={requestHeaders} uiSchema={uiSchema} + appName={appName} />, domContainer ); diff --git a/invenio_administration/assets/semantic-ui/js/invenio_administration/src/search/SearchComponents.js b/invenio_administration/assets/semantic-ui/js/invenio_administration/src/search/SearchComponents.js index 96d1ffb..5ad94e6 100644 --- a/invenio_administration/assets/semantic-ui/js/invenio_administration/src/search/SearchComponents.js +++ b/invenio_administration/assets/semantic-ui/js/invenio_administration/src/search/SearchComponents.js @@ -20,8 +20,9 @@ import { ContribBucketAggregationValuesElement, } from "@js/invenio_search_ui/components"; import { SearchBar } from "./SearchBar"; +import _mapKeys from "lodash/mapKeys"; -export const initDefaultSearchComponents = (domContainer) => { +export const initDefaultSearchComponents = (domContainer, appId = "") => { const sortColumns = (columns) => Object.entries(columns).sort((a, b) => a[1].order - b[1].order); const title = JSON.parse(domContainer.dataset.title); @@ -61,13 +62,14 @@ export const initDefaultSearchComponents = (domContainer) => { idKeyPath: idKeyPath, listUIEndpoint: listUIEndpoint, resourceSchema: resourceSchema, + appName: appId, }); - return { + const components = { "ResultsList.item": SearchResultItemWithConfig, "BucketAggregation.element": ContribBucketAggregationElement, "BucketAggregationValues.element": ContribBucketAggregationValuesElement, - "ResultsGrid.item": () => {}, + "ResultsGrid.item": () => null, "SearchApp.results": SearchResultsWithConfig, "ResultsList.container": ResultsContainerWithConfig, "EmptyResults.element": SearchEmptyResults, @@ -77,4 +79,13 @@ export const initDefaultSearchComponents = (domContainer) => { "SearchApp.resultOptions": () => null, "SearchBar.element": displaySearch ? SearchBarElement : () => null, }; + + if (appId) { + const nameSpacedComponents = _mapKeys(components, function (value, key) { + return `${appId}.${key}`; + }); + return nameSpacedComponents; + } + + return components; }; diff --git a/invenio_administration/assets/semantic-ui/js/invenio_administration/src/search/SearchResultItem.js b/invenio_administration/assets/semantic-ui/js/invenio_administration/src/search/SearchResultItem.js index 4cfdd6f..745528f 100644 --- a/invenio_administration/assets/semantic-ui/js/invenio_administration/src/search/SearchResultItem.js +++ b/invenio_administration/assets/semantic-ui/js/invenio_administration/src/search/SearchResultItem.js @@ -33,6 +33,7 @@ class SearchResultItemComponent extends Component { idKeyPath, resourceSchema, listUIEndpoint, + appName, } = this.props; const resourceHasActions = displayEdit || displayDelete || !isEmpty(actions); @@ -51,6 +52,7 @@ class SearchResultItemComponent extends Component { result={result} resourceSchema={resourceSchema} property={property} + appName={appName} /> )} @@ -59,6 +61,7 @@ class SearchResultItemComponent extends Component { result={result} resourceSchema={resourceSchema} property={property} + appName={appName} /> )} @@ -77,6 +80,7 @@ class SearchResultItemComponent extends Component { idKeyPath={idKeyPath} successCallback={this.refreshAfterAction} listUIEndpoint={listUIEndpoint} + appName={appName} /> )} @@ -98,12 +102,14 @@ SearchResultItemComponent.propTypes = { idKeyPath: PropTypes.string.isRequired, resourceSchema: PropTypes.object.isRequired, listUIEndpoint: PropTypes.string.isRequired, + appName: PropTypes.string, }; SearchResultItemComponent.defaultProps = { displayDelete: true, displayEdit: true, actions: {}, + appName: "", }; export const SearchResultItem = withState(SearchResultItemComponent); diff --git a/invenio_administration/errors.py b/invenio_administration/errors.py index 031d0db..8a989b4 100644 --- a/invenio_administration/errors.py +++ b/invenio_administration/errors.py @@ -89,3 +89,17 @@ def __init__(self, name): ) ) ) + + +class MissingAppId(Exception): + """Exception for missing application id.""" + + def __init__(self, name): + """Initialise error.""" + super().__init__( + _( + "Cannot instantiate resource view {name} without an application id.".format( + name=name + ) + ) + ) diff --git a/invenio_administration/templates/semantic-ui/invenio_administration/details.html b/invenio_administration/templates/semantic-ui/invenio_administration/details.html index c6f8937..775cabd 100644 --- a/invenio_administration/templates/semantic-ui/invenio_administration/details.html +++ b/invenio_administration/templates/semantic-ui/invenio_administration/details.html @@ -30,6 +30,7 @@ data-list-endpoint='{{ list_ui_endpoint }}' data-resource-name='{{ resource_name | tojson }}' data-pid-path='{{ pid_path | tojson }}' + data-app-id='{{ app_id | tojson }}' data-request-headers='{{ request_headers | tojson }}' > diff --git a/invenio_administration/templates/semantic-ui/invenio_administration/search.html b/invenio_administration/templates/semantic-ui/invenio_administration/search.html index 04bde36..8004fe1 100644 --- a/invenio_administration/templates/semantic-ui/invenio_administration/search.html +++ b/invenio_administration/templates/semantic-ui/invenio_administration/search.html @@ -38,7 +38,7 @@

{{ title or name }}

id="invenio-search-config" data-title='{{ title | tojson }}' data-resource-name='{{ resource_name | tojson }}' - data-invenio-search-config='{{ search_config() | tojson }}' + data-invenio-search-config='{{ search_config(app_id=app_id) | tojson }}' data-fields='{{ fields | tojson }}' data-display-search='{{ display_search | tojson }}' data-display-read='{{ display_read | tojson }}' diff --git a/invenio_administration/views/base.py b/invenio_administration/views/base.py index c6d6c13..95b86c3 100644 --- a/invenio_administration/views/base.py +++ b/invenio_administration/views/base.py @@ -20,6 +20,7 @@ MissingDefaultGetView, MissingExtensionName, MissingResourceConfiguration, + MissingAppId, ) from invenio_administration.marshmallow_utils import jsonify_schema from invenio_administration.permissions import administration_permission @@ -134,6 +135,7 @@ class AdminResourceBaseView(AdminView): pid_path = "pid" title = None resource_name = None + app_id = None create_view_name = None list_view_name = None @@ -156,6 +158,8 @@ def __init__( raise MissingExtensionName(self.__class__.__name__) if self.resource_config is None: raise MissingResourceConfiguration(self.__class__.__name__) + if self.app_id is None: + raise MissingAppId(self.__class__.__name__) @classmethod def set_schema(cls): @@ -262,6 +266,7 @@ def get_context(self, pid_value=None): "ui_config": self.item_field_list, "pid": pid_value, "api_endpoint": self.get_api_endpoint(), + "app_id": self.app_id, "title": self.title, "list_endpoint": self.get_list_view_endpoint(), "actions": self.serialize_actions(), @@ -387,6 +392,7 @@ def get(self): "api_endpoint": self.get_api_endpoint(), "title": self.title, "name": self.name, + "app_id": self.app_id, "resource_schema": serialized_schema, "fields": self.item_field_list, "display_search": self.display_search,