Skip to content

Commit

Permalink
Fix #1847 Implement the client UI for the Resource management settings (
Browse files Browse the repository at this point in the history
#1848)

* Fix #1847 Implement the client UI for the Resource management settings

* review labels

* review labels translations

* fix casing
  • Loading branch information
allyoucanmap authored Sep 16, 2024
1 parent ccf6811 commit c25cfaa
Show file tree
Hide file tree
Showing 28 changed files with 287 additions and 50 deletions.
4 changes: 3 additions & 1 deletion geonode_mapstore_client/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,9 @@ def run_setup_hooks(*args, **kwargs):
"temporal_extent_start",
"thumbnail_url",
"title",
"uuid"
"uuid",
"metadata_uploaded_preserve",
"featured"
],
}
settings.REST_API_PRESETS["map_viewer"] = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ import FaIcon from '@js/components/FaIcon';
function DetailsAssets({ fields }) {
return (
<div className="gn-details-assets">
{fields.map((field, idx) => {
const asset = field?.extras?.content || {};
return (
<div key={idx} className="gn-details-info-fields">
<div className="gn-details-info-row linked-resources">
<div className="gn-details-info-fields">
{fields.map((field, idx) => {
const asset = field?.extras?.content || {};
return (
<div key={idx} className="gn-details-info-row gn-details-flex-field">
<FaIcon name="file" />
{asset.download_url ? <a
download
Expand All @@ -17,9 +17,9 @@ function DetailsAssets({ fields }) {
{asset.title}{' '}<FaIcon name="download" />
</a> : asset.title}
</div>
</div>
);
})}
);
})}
</div>
</div>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,7 @@ import moment from 'moment';

import Button from '@js/components/Button';
import Tabs from '@js/components/Tabs';
import DetailsAttributeTable from '@js/components/DetailsPanel/DetailsAttributeTable';
import DetailsLinkedResources from '@js/components/DetailsPanel/DetailsLinkedResources';
import Message from '@mapstore/framework/components/I18N/Message';
import DetailsLocations from '@js/components/DetailsPanel/DetailsLocations';
import DetailsAssets from '@js/components/DetailsPanel/DetailsAssets';

const replaceTemplateString = (properties, str) => {
return Object.keys(properties).reduce((updatedStr, key) => {
Expand Down Expand Up @@ -143,12 +139,8 @@ function DetailsInfoFields({ fields, formatHref }) {
</div>);
}

const tabTypes = {
'attribute-table': DetailsAttributeTable,
'linked-resources': DetailsLinkedResources,
'locations': DetailsLocations,
'tab': DetailsInfoFields,
'assets': DetailsAssets
const defaultTabComponents = {
'tab': DetailsInfoFields
};

const parseTabItems = (items) => {
Expand All @@ -160,15 +152,22 @@ const isDefaultTabType = (type) => type === 'tab';

function DetailsInfo({
tabs = [],
tabComponents: tabComponentsProp,
...props
}) {

const tabComponents = {
...tabComponentsProp,
...defaultTabComponents
};

const filteredTabs = tabs
.filter((tab) => !tab?.disableIf)
.map((tab) =>
({
...tab,
items: isDefaultTabType(tab.type) ? parseTabItems(tab?.items) : tab?.items,
Component: tabTypes[tab.type] || tabTypes.tab
Component: tabComponents[tab.type] || tabComponents.tab
}))
.filter(tab => !isEmpty(tab?.items));
const [selectedTabId, onSelect] = useState(filteredTabs?.[0]?.id);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,19 @@ import Message from '@mapstore/framework/components/I18N/Message';

const DetailLinkedResource = ({resources, type}) => {
return !isEmpty(resources) && (
<>
<div className="gn-details-info-fields">
<Message msgId={`gnviewer.linkedResources.${type}`} />
{resources.map((field, key) => {
return (<div key={key} className="gn-details-info-fields">
<div className="gn-details-info-row linked-resources">
return (
<div key={key} className="gn-details-info-row gn-details-flex-field">
{field.icon && <FaIcon name={field.icon} />}
<a key={field.pk} href={field.detail_url}>
{field.title}
</a>
</div>
</div>);
);
})}
</>
</div>
);
};

Expand Down Expand Up @@ -54,7 +54,7 @@ const DetailsLinkedResources = ({ fields, resourceTypesInfo }) => {
];

return (
<div className="linked-resources">
<div className="gn-details-linked-resources">
{
linkedResources.map(({resources, type})=> <DetailLinkedResource resources={resources} type={type}/>)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import React from 'react';
import { Checkbox } from 'react-bootstrap';
import Message from '@mapstore/framework/components/I18N/Message';
import { RESOURCE_MANAGEMENT_PROPERTIES } from '@js/utils/ResourceUtils';

function DetailsManagement({ resource, onChange }) {
const perms = resource?.perms || [];
return (
<div className="gn-details-management">
<div className="gn-details-info-fields">
<Message msgId={"gnviewer.resourceManagement"} />
{Object.keys(RESOURCE_MANAGEMENT_PROPERTIES).map((key) => {
const { labelId, disabled } = RESOURCE_MANAGEMENT_PROPERTIES[key];
return (
<div key={key} className="gn-details-info-row gn-details-flex-field">
<Checkbox
style={{ margin: 0 }}
disabled={disabled(perms)}
checked={!!resource?.[key]}
onChange={(event) => onChange({ [key]: !!event.target.checked })}
>
<Message msgId={labelId} />
</Checkbox>
</div>
);
})}
</div>
</div>
);
}

export default DetailsManagement;
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,8 @@ function DetailsPanel({
tabs,
pathname,
toolbarItems,
onSetExtent
onSetExtent,
tabComponents
}) {
const detailsContainerNode = useRef();
const [titleNodeRef, titleInView] = useInView();
Expand Down Expand Up @@ -292,7 +293,7 @@ function DetailsPanel({
: null}
</div>
</div>
<DetailsInfo tabs={tabs} formatHref={formatHref} allowEdit={activeEditMode} resourceTypesInfo={types} resource={resource} onSetExtent={onSetExtent}/>
<DetailsInfo tabs={tabs} tabComponents={tabComponents} formatHref={formatHref} allowEdit={activeEditMode} resourceTypesInfo={types} resource={resource} onSetExtent={onSetExtent}/>
</section>
</div>
);
Expand Down
11 changes: 9 additions & 2 deletions geonode_mapstore_client/client/js/epics/gnsave.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,13 +72,16 @@ import {
import {
ResourceTypes,
cleanCompactPermissions,
toGeoNodeMapConfig
toGeoNodeMapConfig,
RESOURCE_MANAGEMENT_PROPERTIES
} from '@js/utils/ResourceUtils';
import {
ProcessTypes,
ProcessStatus
} from '@js/utils/ResourceServiceUtils';

const RESOURCE_MANAGEMENT_PROPERTIES_KEYS = Object.keys(RESOURCE_MANAGEMENT_PROPERTIES);

function parseMapBody(body) {
const geoNodeMap = toGeoNodeMapConfig(body.data);
return {
Expand Down Expand Up @@ -142,13 +145,17 @@ export const gnSaveContent = (action$, store) =>
const contentType = state.gnresource?.type || 'map';
const data = getDataPayload(state, contentType);
const extent = getExtentPayload(state, contentType);
const currentResource = getResourceData(state);
const body = {
'title': action.metadata.name,
...(RESOURCE_MANAGEMENT_PROPERTIES_KEYS.reduce((acc, key) => {
acc[key] = !!currentResource?.[key];
return acc;
}, {})),
...(action.metadata.description && { 'abstract': action.metadata.description }),
...(data && { 'data': JSON.parse(JSON.stringify(data)) }),
...(extent && { extent })
};
const currentResource = getResourceData(state);
return Observable.defer(() => SaveAPI[contentType](state, action.id, body, action.reload))
.switchMap((resource) => {
if (action.reload) {
Expand Down
4 changes: 3 additions & 1 deletion geonode_mapstore_client/client/js/plugins/DetailViewer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ import { mapSelector } from '@mapstore/framework/selectors/map';
import { parsePluginConfigExpressions } from '@js/utils/MenuUtils';
import usePluginItems from '@js/hooks/usePluginItems';
import { getResourceTypesInfo } from '@js/utils/ResourceUtils';
import tabComponents from '@js/plugins/detailviewer/tabComponents';

const ConnectedDetailsPanel = connect(
createSelector([
Expand All @@ -71,7 +72,8 @@ const ConnectedDetailsPanel = connect(
initialBbox: mapData?.bbox,
enableMapViewer: showMapThumbnail,
downloading,
resourceId: resource.pk
resourceId: resource.pk,
tabComponents
})),
{
closePanel: setControlProperty.bind(null, 'rightOverlay', 'enabled', false),
Expand Down
4 changes: 3 additions & 1 deletion geonode_mapstore_client/client/js/plugins/ResourcesGrid.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ import FaIcon from '@js/components/FaIcon';
import Button from '@js/components/Button';
import useLocalStorage from '@js/hooks/useLocalStorage';
import MainLoader from '@js/components/MainLoader';
import tabComponents from '@js/plugins/detailviewer/tabComponents';

const ConnectedDetailsPanel = connect(
createSelector([
Expand All @@ -65,7 +66,8 @@ const ConnectedDetailsPanel = connect(
favorite: favorite,
downloading,
canDownload: resourceHasPermission(resource, 'download_resourcebase'),
resourceId: resource?.pk
resourceId: resource?.pk,
tabComponents
})),
{
onFavorite: setFavoriteResource,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* Copyright 2024, GeoSolutions Sas.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/

import { connect } from 'react-redux';
import DetailsLocations from '@js/components/DetailsPanel/DetailsLocations';
import DetailsAssets from '@js/components/DetailsPanel/DetailsAssets';
import DetailsAttributeTable from '@js/components/DetailsPanel/DetailsAttributeTable';
import DetailsLinkedResources from '@js/components/DetailsPanel/DetailsLinkedResources';
import DetailsManagement from '@js/components/DetailsPanel/DetailsManagement';
import { updateResourceProperties } from '@js/actions/gnresource';

const tabComponents = {
'attribute-table': DetailsAttributeTable,
'linked-resources': DetailsLinkedResources,
'locations': DetailsLocations,
'assets': DetailsAssets,
'management': connect(() => ({}), { onChange: updateResourceProperties })(DetailsManagement)
};

export default tabComponents;
7 changes: 5 additions & 2 deletions geonode_mapstore_client/client/js/selectors/resource.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { compareMapChanges } from '@mapstore/framework/utils/MapUtils';
import { currentStorySelector } from '@mapstore/framework/selectors/geostory';
import { originalDataSelector } from '@mapstore/framework/selectors/dashboard';
import { widgetsConfig } from '@mapstore/framework/selectors/widgets';
import { ResourceTypes } from '@js/utils/ResourceUtils';
import { ResourceTypes, RESOURCE_MANAGEMENT_PROPERTIES } from '@js/utils/ResourceUtils';
import {
getCurrentResourceDeleteLoading,
getCurrentResourceCopyLoading
Expand All @@ -25,6 +25,9 @@ import pick from 'lodash/pick';
import isEmpty from 'lodash/isEmpty';
import get from 'lodash/get';
import { generateContextResource } from '@mapstore/framework/selectors/contextcreator';

const RESOURCE_MANAGEMENT_PROPERTIES_KEYS = Object.keys(RESOURCE_MANAGEMENT_PROPERTIES);

/**
* @module selectors/resource
*/
Expand Down Expand Up @@ -270,7 +273,7 @@ export const getResourceDirtyState = (state) => {
return null;
}
const resourceType = state?.gnresource?.type;
const metadataKeys = ['title', 'abstract', 'data', 'extent'];
const metadataKeys = ['title', 'abstract', 'data', 'extent', ...RESOURCE_MANAGEMENT_PROPERTIES_KEYS];
const { data: initialData = {}, ...resource } = pick(state?.gnresource?.initialResource || {}, metadataKeys);
const { compactPermissions, geoLimits } = getPermissionsPayload(state);
const currentData = JSON.parse(JSON.stringify(getDataPayload(state) || {})); // JSON stringify is needed to remove undefined values
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,12 @@ const hasDefaultSettings = (layer) => {
return true;
};

const canManageResourceSettings = (resource) => {
const { perms } = resource || {};
const settingsPerms = ['feature_resourcebase', 'approve_resourcebase', 'publish_resourcebase'];
return !!(perms || []).find(perm => settingsPerms.includes(perm));
};

export const getPluginsContext = () => ({
get,
getMetadataUrl,
Expand All @@ -60,5 +66,6 @@ export const getPluginsContext = () => ({
isDocumentExternalSource,
getCataloguePath,
getCreateNewMapLink,
hasDefaultSettings
hasDefaultSettings,
canManageResourceSettings
});
23 changes: 23 additions & 0 deletions geonode_mapstore_client/client/js/utils/ResourceUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,29 @@ export const GXP_PTYPES = {
'GN_WMS': 'gxp_geonodecataloguesource'
};

export const RESOURCE_MANAGEMENT_PROPERTIES = {
'metadata_uploaded_preserve': {
labelId: 'gnviewer.preserveUploadedMetadata',
disabled: (perms = []) => !perms.includes('change_resourcebase')
},
'is_approved': {
labelId: 'gnviewer.approveResource',
disabled: (perms = []) => !perms.includes('approve_resourcebase')
},
'is_published': {
labelId: 'gnviewer.publishResource',
disabled: (perms = []) => !perms.includes('publish_resourcebase')
},
'featured': {
labelId: 'gnviewer.featureResource',
disabled: (perms = []) => !perms.includes('feature_resourcebase')
},
'advertised': {
labelId: 'gnviewer.advertiseResource',
disabled: (perms = []) => !perms.includes('change_resourcebase')
}
};

export const isDefaultDatasetSubtype = (subtype) => !subtype || ['vector', 'raster', 'remote', 'vector_time'].includes(subtype);

export const FEATURE_INFO_FORMAT = 'TEMPLATE';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -456,7 +456,7 @@
}
.tab-content {
margin-top: 0.5rem;
.linked-resources {
.gn-details-flex-field {
> span {
font-size: @font-size-small;
}
Expand Down Expand Up @@ -506,10 +506,11 @@
white-space: nowrap;
}
}
&.linked-resources {
&.gn-details-flex-field {
display: flex;
align-items: center;
gap: 10px;
padding: 0.25rem;
}
&.link {
border-bottom: none;
Expand Down
Loading

0 comments on commit c25cfaa

Please sign in to comment.