Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#1511 & #1512: Implement links to remote documents and source in details panel #1513

Merged
merged 7 commits into from
Aug 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions geonode_mapstore_client/client/.npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# add legacy peer dependency flag on npm install
legacy-peer-deps=true
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ function ActionButtons({
{options.map((opt) => {
if (opt.type === 'plugin') {
const { Component } = opt;
return <Component key={opt.action} resource={resource}/>;
return <Component key={opt.action} resource={resource} renderType="menuItem"/>;
}
return (
<Dropdown.Item
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,15 +74,14 @@ const DetailsPanelTools = ({
resource,
enableFavorite,
favorite,
downloadUrl,
onAction,
onFavorite,
detailUrl,
editThumbnail,
resourceCanPreviewed,
canView,
metadataDetailUrl,
name
name,
toolbarItems = []
}) => {

const isMounted = useRef();
Expand Down Expand Up @@ -120,12 +119,9 @@ const DetailsPanelTools = ({
onClick={debounce(() => handleFavorite(favorite), 500)}>
<FaIcon name={favorite ? 'star' : 'star-o'} />
</Button>}
{downloadUrl &&
<Button variant="default"
onClick={() => onAction(resource)} >
<FaIcon name="download" />
</Button>}

{toolbarItems.map(({ Component, name: toolbarItemName }, index) => {
return (<Component key={toolbarItemName || index} showIcon />);
})}
<CopyToClipboard
tooltipPosition="top"
tooltipId={
Expand Down Expand Up @@ -190,10 +186,9 @@ function DetailsPanel({
initialBbox,
enableMapViewer,
onClose,
onAction,
canDownload,
tabs,
pathname
pathname,
toolbarItems
}) {
const detailsContainerNode = useRef();
const [titleNodeRef, titleInView] = useInView();
Expand All @@ -212,23 +207,20 @@ function DetailsPanel({
const detailUrl = resource?.pk && formatDetailUrl(resource);
const resourceCanPreviewed = resource?.pk && canPreviewed && canPreviewed(resource);
const canView = resource?.pk && hasPermission && hasPermission(resource);
const downloadUrl = (resource?.href && resource?.href.includes('download')) ? resource?.href
: (resource?.download_url && canDownload) ? resource?.download_url : undefined;
const metadataDetailUrl = resource?.pk && getMetadataDetailUrl(resource);
const tools = (
<DetailsPanelTools
name={name}
resource={resource}
enableFavorite={enableFavorite}
favorite={favorite}
downloadUrl={downloadUrl}
onAction={onAction}
onFavorite={onFavorite}
detailUrl={detailUrl}
editThumbnail={editThumbnail}
resourceCanPreviewed={resourceCanPreviewed}
canView={canView}
metadataDetailUrl={metadataDetailUrl}
toolbarItems={toolbarItems}
/>
);
return (
Expand Down Expand Up @@ -311,8 +303,7 @@ DetailsPanel.defaultProps = {
onResourceThumbnail: () => '#',
width: 696,
getTypesInfo: getResourceTypesInfo,
isThumbnailChanged: false,
onAction: () => {}
isThumbnailChanged: false
};

export default DetailsPanel;
28 changes: 17 additions & 11 deletions geonode_mapstore_client/client/js/plugins/DetailViewer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,7 @@ import {
setFavoriteResource,
setMapThumbnail,
setResourceThumbnail,
enableMapThumbnailViewer,
downloadResource
enableMapThumbnailViewer
} from '@js/actions/gnresource';
import { processingDownload } from '@js/selectors/resourceservice';
import FaIcon from '@js/components/FaIcon/FaIcon';
Expand All @@ -43,9 +42,9 @@ import { hashLocationToHref } from '@js/utils/SearchUtils';
import Message from '@mapstore/framework/components/I18N/Message';
import { layersSelector } from '@mapstore/framework/selectors/layers';
import { mapSelector } from '@mapstore/framework/selectors/map';
import { resourceHasPermission } from '@js/utils/ResourceUtils';
import { parsePluginConfigExpressions } from '@js/utils/MenuUtils';
import detailViewerEpics from '@js/epics/detailviewer';
import usePluginItems from '@js/hooks/usePluginItems';

const ConnectedDetailsPanel = connect(
createSelector([
Expand All @@ -70,16 +69,14 @@ const ConnectedDetailsPanel = connect(
initialBbox: mapData?.bbox,
enableMapViewer: showMapThumbnail,
downloading,
canDownload: resourceHasPermission(resource, 'download_resourcebase'),
resourceId: resource.pk
})),
{
closePanel: setControlProperty.bind(null, 'rightOverlay', 'enabled', false),
onFavorite: setFavoriteResource,
onMapThumbnail: setMapThumbnail,
onResourceThumbnail: setResourceThumbnail,
onClose: enableMapThumbnailViewer,
onAction: downloadResource
onClose: enableMapThumbnailViewer
}
)(DetailsPanel);

Expand Down Expand Up @@ -181,8 +178,10 @@ function DetailViewer({
onClose,
monitoredState,
queryPathname = '/',
tabs = []
}) {
tabs = [],
items,
resourceId
}, context) {

const parsedConfig = parsePluginConfigExpressions(monitoredState, { tabs });

Expand Down Expand Up @@ -215,6 +214,10 @@ function DetailViewer({
return null;
}

const { loadedPlugins } = context;
const configuredItems = usePluginItems({ items, loadedPlugins }, [resourceId]);
const toolbarItems = configuredItems.filter(item => item.target === "toolbar");

return (
<OverlayContainer
enabled={enabled}
Expand All @@ -230,6 +233,7 @@ function DetailViewer({
formatHref={handleFormatHref}
tabs={parsedConfig.tabs}
pathname={queryPathname}
toolbarItems={toolbarItems}
/>
</OverlayContainer>
);
Expand All @@ -244,14 +248,16 @@ const DetailViewerPlugin = connect(
isNewResource,
getResourceId,
userSelector,
state => getMonitoredState(state, getConfigProp('monitorState'))
state => getMonitoredState(state, getConfigProp('monitorState')),
state => state?.gnresource?.data || null
],
(enabled, canEdit, isNew, resourcePk, user, monitoredState) => ({
(enabled, canEdit, isNew, resourcePk, user, monitoredState, resource) => ({
enabled,
canEdit,
hide: isNew || !resourcePk,
user,
monitoredState
monitoredState,
resourceId: resource?.pk
})
),
{
Expand Down
114 changes: 71 additions & 43 deletions geonode_mapstore_client/client/js/plugins/DownloadResource.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,68 +10,89 @@ import React from 'react';
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import { createPlugin } from '@mapstore/framework/utils/PluginsUtils';
import { isDocumentExternalSource } from '@js/utils/ResourceUtils';
import Message from '@mapstore/framework/components/I18N/Message';
import Button from '@js/components/Button';
import tooltip from '@mapstore/framework/components/misc/enhancers/tooltip';
import Dropdown from '@js/components/Dropdown';
import FaIcon from '@js/components/FaIcon';
import {
getResourceData
} from '@js/selectors/resource';
import { downloadResource } from '@js/actions/gnresource';
import { processingDownload } from '@js/selectors/resourceservice';

function DownloadDocumentButton({
resource,
variant,
size
}) {
return (
resource ? <Button
download={`${resource?.title}.${resource?.extension}`}
href={resource?.href}
variant={variant}
size={size}
>
<Message msgId="gnviewer.download" />
</Button> : null);
}
const ButtonWithTooltip = tooltip(Button);

const ConnectedDownloadResource = connect(
createSelector([
getResourceData
], (resource) => ({
resource
})),
{
}
)(DownloadDocumentButton);
const RENDER_TYPE = {
"button": ButtonWithTooltip,
"menuItem": Dropdown.Item
};

function DownloadMenuItem({
const DownloadButton = ({
resource,
onDownload
}) {
resourceData,
variant,
size,
onAction = () => {},
renderType = "button",
showIcon,
downloading
}) => {
const Component = RENDER_TYPE[renderType];
const isButton = renderType !== "menuItem";
const _resource = resource ?? resourceData;
const isExternalLink = isDocumentExternalSource(_resource);

if (!(resource?.download_url && resource?.perms?.includes('download_resourcebase'))) {
if (!(_resource?.download_url && _resource?.perms?.includes('download_resourcebase')) || (!isButton && isExternalLink)) {
return null;
}

if (isExternalLink) {
return (
<Component
{...isButton && { variant, size }}
{...showIcon && { tooltipId: "gnviewer.download" }}
download={`${_resource?.title}.${_resource?.extension}`}
href={_resource?.href}
target="_blank"
rel="noopener noreferrer"
>
{showIcon
? <FaIcon name="external-link" />
: <Message msgId="gnviewer.download" />
}
</Component>
);
}

return (
<Dropdown.Item
onClick={() =>
onDownload(resource)
}
<Component
disabled={!!downloading}
onClick={() => downloading ? null : onAction(_resource)}
{...isButton && { variant, size}}
{...showIcon && { tooltipId: "gnviewer.download" }}
>
<FaIcon name="download" />{' '}
<Message msgId="gnviewer.download" />
</Dropdown.Item>
{showIcon
? <FaIcon name="download" />
: <Message msgId="gnviewer.download" />
}
</Component>
);
}
};

const ConnectedMenuItem = connect(
createSelector([], () => ({})),
const DownloadResource = connect(
createSelector([
getResourceData,
processingDownload
], (resourceData, downloading) => ({
resourceData,
downloading
})),
{
onDownload: downloadResource
onAction: downloadResource
}
)((DownloadMenuItem));
)(DownloadButton);

/**
* @module DownloadResource
Expand All @@ -86,17 +107,24 @@ const ConnectedMenuItem = connect(
* }
*/
export default createPlugin('DownloadResource', {
component: ConnectedDownloadResource,
component: DownloadResource,
containers: {
ActionNavbar: {
name: 'DownloadResource',
Component: ConnectedDownloadResource,
Component: DownloadResource,
priority: 1
},
ResourcesGrid: {
name: 'downloadResource',
target: 'cardOptions',
Component: ConnectedMenuItem,
detailsToolbar: true,
Component: DownloadResource,
priority: 1
},
DetailViewer: {
name: 'DownloadResource',
target: 'toolbar',
Component: DownloadResource,
priority: 1
}
},
Expand Down
16 changes: 11 additions & 5 deletions geonode_mapstore_client/client/js/plugins/ResourcesGrid.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -456,11 +456,16 @@ function ResourcesGrid({
const { loadedPlugins } = context;
const configuredItems = usePluginItems({ items, loadedPlugins }, []);

const cardOptions = [...configuredItems.map(({ name, Component }) => ({
type: 'plugin',
Component,
action: name
}))].sort((a, b) => resourceCardActionsOrder.indexOf(a.action) - resourceCardActionsOrder.indexOf(b.action));
const cardOptions = [...configuredItems
.filter(item => item.target === 'cardOptions')
.map(({ name, Component }) => ({
type: 'plugin',
Component,
action: name
}))].sort((a, b) => resourceCardActionsOrder.indexOf(a.action) - resourceCardActionsOrder.indexOf(b.action));

const detailsToolbarItems = configuredItems
.filter(item => (item.target === "cardOptions" && item.detailsToolbar) || item.target === "detailsToolbar");

const updatedLocation = useRef();
updatedLocation.current = location;
Expand Down Expand Up @@ -667,6 +672,7 @@ function ResourcesGrid({
linkHref={closeDetailPanelHref}
formatHref={handleFormatHref}
tabs={parsedConfig.detailsTabs}
toolbarItems={detailsToolbarItems}
/>}
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ import {
getMetadataUrl,
getMetadataDetailUrl,
resourceHasPermission,
canCopyResource
canCopyResource,
isDocumentExternalSource
} from '@js/utils/ResourceUtils';
import get from 'lodash/get';

Expand Down Expand Up @@ -42,5 +43,6 @@ export const getPluginsContext = () => ({
canCopyResource,
userHasPermission: (user, perm) => user?.perms?.includes(perm),
getUserResourceName,
getUserResourceNames
getUserResourceNames,
isDocumentExternalSource
});
4 changes: 4 additions & 0 deletions geonode_mapstore_client/client/js/utils/ResourceUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -703,3 +703,7 @@ export const parseUploadFiles = (data) => {
export const getResourceImageSource = (image) => {
return image ? image : 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAPAAAADICAIAAABZHvsFAAAACXBIWXMAAC4jAAAuIwF4pT92AAABiklEQVR42u3SAQ0AAAjDMMC/5+MAAaSVsKyTFHwxEmBoMDQYGgyNocHQYGgwNBgaQ4OhwdBgaDA0hgZDg6HB0GBoDA2GBkODocHQGBoMDYYGQ4OhMTQYGgwNhgZDY2gwNBgaDI2hwdBgaDA0GBpDg6HB0GBoMDSGBkODocHQYGgMDYYGQ4OhwdAYGgwNhgZDg6ExNBgaDA2GBkNjaDA0GBoMDYbG0GBoMDQYGkODocHQYGgwNIYGQ4OhwdBgaAwNhgZDg6HB0BgaDA2GBkODoTE0GBoMDYYGQ2NoMDQYGgwNhsbQYGgwNBgaQ4OhwdBgaDA0hgZDg6HB0GBoDA2GBkODocHQGBoMDYYGQ4OhMTQYGgwNhgZDY2gwNBgaDA2GxtBgaDA0GBoMjaHB0GBoMDSGBkODocHQYGgMDYYGQ4OhwdAYGgwNhgZDg6ExNBgaDA2GBkNjaDA0GBoMDYbG0GBoMDQYGgyNocHQYGgwNIYGQ4OhwdBgaAwNhgZDg6HB0BgaDA2GBkPDbQH4OQSN0W8qegAAAABJRU5ErkJggg==';
};

export const isDocumentExternalSource = (resource) => {
return resource && resource.resource_type === ResourceTypes.DOCUMENT && resource.sourcetype === 'REMOTE';
};
Loading
Loading