Skip to content

Commit

Permalink
[Fixes #1308] Clone option in the list view is missing on Maps for no…
Browse files Browse the repository at this point in the history
…n admin users (#1352)
  • Loading branch information
allyoucanmap authored Dec 2, 2022
1 parent add5bf1 commit 827907d
Show file tree
Hide file tree
Showing 5 changed files with 65 additions and 44 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,21 @@
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
import React from 'react';
import React, { useRef } from 'react';
import PropTypes from 'prop-types';
import Dropdown from '@js/components/Dropdown';
import Message from '@mapstore/framework/components/I18N/Message';
import FaIcon from '@js/components/FaIcon';
import { canCopyResource } from '@js/utils/ResourceUtils';

// this is a workaround based on the current structure of actions in card options
// new version will centralize this logic inside the correspondent plugins
const checkAction = {
'delete': (resource) => !!resource?.perms?.includes('delete_resourcebase'),
// we assume tha the add_resource check has been checked in parent elements
'copy': (resource) => canCopyResource(resource, { perms: ['add_resource'] }),
'download': (resource) => !!(resource?.download_url && resource?.perms?.includes('download_resourcebase'))
};
function ActionButtons({
options,
actions,
Expand All @@ -20,16 +29,18 @@ function ActionButtons({
onDownload
}) {

// do not render if the options only contain download or copy or both without meeting their requirements
if (options?.every(({action}) => action !== 'delete')) {
if (options?.every(({action}) => (action === 'download' && !resource.download_url) || (['copy'].includes(action) && !resource.is_copyable & !resource.download_url))) {
return null;
}
}

const containerNode = useRef();
const dropdownClassName = 'gn-card-dropdown';
const dropdownNode = containerNode?.current?.querySelector(`.${dropdownClassName}`);
const isDropdownEmpty = (dropdownNode?.children?.length || 0) === 0;

return (
<div className="gn-resource-action-buttons">
<div
ref={containerNode}
className="gn-resource-action-buttons"
onClick={event => event.stopPropagation()}
style={isDropdownEmpty ? { display: 'none' } : {}}
>
<Dropdown className="gn-card-options" pullRight>
<Dropdown.Toggle
id={`gn-card-options-${resource.pk2 || resource.pk}`}
Expand All @@ -39,11 +50,12 @@ function ActionButtons({
>
<FaIcon name="ellipsis-v" />
</Dropdown.Toggle>
<Dropdown.Menu className={`gn-card-dropdown`}>
<Dropdown.Menu className={dropdownClassName}>
{options.map((opt) => {
if ((opt.type === 'button' && actions[opt.action]) || opt.action === 'download') {
return (
((opt.action === 'download' && resource.download_url) || (opt.action !== 'copy' && opt.action !== 'download') || (resource?.is_copyable && opt.action !== 'download')) && <Dropdown.Item
const checkFunc = checkAction[opt.action];
return (!checkFunc || checkFunc(resource))
? (<Dropdown.Item
key={opt.action}
onClick={() =>
opt.action !== 'download' ? onAction(actions[opt.action], [
Expand All @@ -53,8 +65,8 @@ function ActionButtons({
>
<FaIcon name={opt.icon} />{' '}
<Message msgId={opt.labelId} />
</Dropdown.Item>
);
</Dropdown.Item>)
: null;
}

return (
Expand Down
41 changes: 20 additions & 21 deletions geonode_mapstore_client/client/js/plugins/SaveAs.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,8 @@ import { createSelector } from 'reselect';
import { createPlugin } from '@mapstore/framework/utils/PluginsUtils';
import { setControlProperty } from '@mapstore/framework/actions/controls';
import Message from '@mapstore/framework/components/I18N/Message';
import { Glyphicon } from 'react-bootstrap';
import { mapInfoSelector } from '@mapstore/framework/selectors/map';
import { isLoggedIn } from '@mapstore/framework/selectors/security';
import { userSelector } from '@mapstore/framework/selectors/security';
import Button from '@js/components/Button';
import {
saveContent,
Expand All @@ -33,6 +32,7 @@ import {
getResourceDirtyState
} from '@js/selectors/resource';
import { ProcessTypes } from '@js/utils/ResourceServiceUtils';
import { canCopyResource } from '@js/utils/ResourceUtils';
import { processResources } from '@js/actions/gnresource';
import { getCurrentResourceCopyLoading } from '@js/selectors/resourceservice';

Expand Down Expand Up @@ -133,15 +133,28 @@ function SaveAsButton({
;
}

const canCopyResourceFunction = (state) => {
return (resource) => {
const user = userSelector(state);
if (!user) {
return false;
}
const isResourceNew = isNewResource(state);
const canAdd = canAddResource(state);
if (isResourceNew && canAdd) {
return true;
}
return canCopyResource(resource, user);
};
};

const ConnectedSaveAsButton = connect(
createSelector(
isLoggedIn,
canAddResource,
getResourceData,
getResourceDirtyState,
isNewResource,
(loggedIn, userCanAddResource, resource, dirtyState, isResourceNew) => ({
enabled: loggedIn && userCanAddResource && (resource?.is_copyable || isResourceNew),
canCopyResourceFunction,
(resource, dirtyState, canCopy) => ({
enabled: !!canCopy(resource),
resource,
disabled: !!dirtyState
})
Expand All @@ -154,20 +167,6 @@ const ConnectedSaveAsButton = connect(
export default createPlugin('SaveAs', {
component: SaveAsPlugin,
containers: {
BurgerMenu: {
name: 'saveAs',
position: 30,
text: <Message msgId="saveAs"/>,
icon: <Glyphicon glyph="floppy-open"/>,
action: setControlProperty.bind(null, 'saveAs', null),
selector: createSelector(
isLoggedIn,
canAddResource,
(loggedIn, userCanAddResource) => ({
style: (loggedIn && userCanAddResource) ? {} : { display: 'none' }
})
)
},
ActionNavbar: {
name: 'SaveAs',
Component: ConnectedSaveAsButton
Expand Down
10 changes: 8 additions & 2 deletions geonode_mapstore_client/client/js/utils/ResourceUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -585,13 +585,19 @@ export const parseMapConfig = (mapResponse, resource = {}) => {
};
};

/**
* Util to check if resosurce can be cloned (Save As)
/*
* Util to check if resource can be cloned (Save As)
* Requirements for copying are 'add_resource' permission and is_copyable property on resource
* the dataset and document need also the download_resourcebase permission
*/
export const canCopyResource = (resource, user) => {
const canAdd = user?.perms?.includes('add_resource');
const canCopy = resource?.is_copyable;
const resourceType = resource?.resource_type;
if ([ResourceTypes.DATASET, ResourceTypes.DOCUMENT].includes(resourceType)) {
const canDownload = !!resource?.perms?.includes('download_resourcebase');
return (canAdd && canCopy && canDownload) ? true : false;
}
return (canAdd && canCopy) ? true : false;
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -422,11 +422,19 @@ describe('Test Resource Utils', () => {
expect(pasrsedStyleName).toBe('test:testName');
});

it('should test canCopyResource', () => {
const resource = { is_copyable: true };
it('should test canCopyResource with different resource type', () => {
const user = { perms: ['add_resource'] };
expect(canCopyResource({ resource_type: 'dataset', perms: ['download_resourcebase'], is_copyable: true }, user)).toBe(true);
expect(canCopyResource({ resource_type: 'document', perms: ['download_resourcebase'], is_copyable: true }, user)).toBe(true);
expect(canCopyResource({ resource_type: 'map', perms: [], is_copyable: true }, user)).toBe(true);
expect(canCopyResource({ resource_type: 'geostory', perms: [], is_copyable: true }, user)).toBe(true);
expect(canCopyResource({ resource_type: 'dashboard', perms: [], is_copyable: true }, user)).toBe(true);

expect(canCopyResource(resource, user)).toEqual(true);
expect(canCopyResource({ resource_type: 'dataset', perms: [], is_copyable: true }, user)).toBe(false);
expect(canCopyResource({ resource_type: 'document', perms: [], is_copyable: true }, user)).toBe(false);
expect(canCopyResource({ resource_type: 'map', perms: [] }, user)).toBe(false);
expect(canCopyResource({ resource_type: 'geostory', perms: [] }, user)).toBe(false);
expect(canCopyResource({ resource_type: 'dashboard', perms: [] }, user)).toBe(false);
});

it('should test excludeDeletedResources', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -450,10 +450,6 @@
{
"type": "user",
"value": "add_resource"
},
{
"type": "resource",
"value": "download_resourcebase"
}
]
},
Expand Down

0 comments on commit 827907d

Please sign in to comment.