Skip to content
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
35 changes: 35 additions & 0 deletions sass/internal-page/InternalPage.scss
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,41 @@

// sass-lint:enable force-element-nesting class-name-format nesting-depth

// sass-lint:disable class-name-format force-element-nesting nesting-depth no-important no-qualifying-elements quotes property-sort-order
.nav-pf-secondary-nav .list-group {
li.list-group-item.LegacyPlugins__group-header {
position: relative;
margin-top: 4px;
padding-left: 25px;
pointer-events: none;

&::before {
content: '';
position: absolute;
top: 0;
right: 20px;
left: 20px;
height: 1px;
background-color: $color-pf-black-600;
}

&:first-of-type {
margin-top: 0;

&::before {
display: none;
}
}

> a {
padding-left: 20px !important;
font-weight: 700 !important;
cursor: default;
}
}
}
// sass-lint:enable class-name-format force-element-nesting nesting-depth no-important no-qualifying-elements quotes property-sort-order

.NoAccessPage {
position: fixed;
top: 0;
Expand Down
7 changes: 7 additions & 0 deletions sass/widgets/config/WidgetConfigPage.scss
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,11 @@
& .panel-heading {
background-color: $color-white;
}

&__legacy-iframe {
width: 100%;
min-height: 600px;
border: 0;
overflow: hidden;
}
}
2 changes: 1 addition & 1 deletion src/api/system.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { makeRequest, METHODS } from '@entando/apimanager';

// eslint-disable-next-line import/prefer-default-export
export const getSystemReport = () => makeRequest({
uri: '/api/system/report',
uri: '/api/system/legacy-components-menu',
method: METHODS.GET,
mockResponse: {},
useAuthentication: true,
Expand Down
20 changes: 20 additions & 0 deletions src/helpers/legacyWidget.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import getAppBuilderWidgetForm from 'helpers/getAppBuilderWidgetForm';
import { adminConsoleUrl } from 'helpers/urlUtils';

export const LEGACY_CONFIG_CUSTOM_ELEMENT = 'LEGACY_CONFIG';

// A widget is "legacy" when its configUi.customElement is set to LEGACY_CONFIG
// and it has no internal app-builder form registered in getAppBuilderWidgetForm.
export const isLegacyWidget = (widget) => {
if (!widget || !widget.configUi || widget.configUi.customElement !== 'LEGACY_CONFIG') {
return false;
}
return !getAppBuilderWidgetForm(widget);
};

export const buildLegacyConfigUrl = (widget, pageCode, frameId) => {
const actionConf = widget.configUiName || 'configSimpleParameter';
const path =
`do/Page/SpecialWidget/${actionConf}?pageCode=${pageCode}&widgetTypeCode=${widget.code}&frame=${frameId}&entandoHeadless=true`;
return adminConsoleUrl(path);
};
43 changes: 43 additions & 0 deletions src/helpers/permissions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/**
* Evaluates a boolean permission expression against user permissions.
* Supports & (AND), | (OR), and parentheses for grouping.
* Example: "(editContents|validateContents)&superuser"
*/
// eslint-disable-next-line import/prefer-default-export
export const checkPermission = (expr, userPermissions) => {
if (!expr) return false;
let i = 0;

const parseOr = () => {
// eslint-disable-next-line no-use-before-define
let result = parseAnd();
// eslint-disable-next-line no-plusplus,no-use-before-define
while (expr[i] === '|') { i++; result = parseAnd() || result; }
return result;
};

const parseAnd = () => {
// eslint-disable-next-line no-use-before-define
let result = parseAtom();
// eslint-disable-next-line no-plusplus,no-use-before-define
while (expr[i] === '&') { i++; result = parseAtom() && result; }
return result;
};

const parseAtom = () => {
if (expr[i] === '(') {
// eslint-disable-next-line no-plusplus
i++;
const result = parseOr();
// eslint-disable-next-line no-plusplus
i++;
return result;
}
let name = '';
// eslint-disable-next-line no-plusplus
while (i < expr.length && /\w/.test(expr[i])) name += expr[i++];
return userPermissions.includes(name);
};

return parseOr();
};
2 changes: 2 additions & 0 deletions src/locales/en.js
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,7 @@ export default {
'menu.scheduler': 'Scheduler',
'menu.versioning': 'Versioning',
'menu.apps': 'Apps',
'menu.legacyPlugins': 'Legacy Plugins',
'menu.settings': 'Administration',
'menu.emailConfig': 'Email Configuration',
'dashboard.cards.noPermission': 'You have no permission to visualize this data',
Expand Down Expand Up @@ -411,6 +412,7 @@ export default {
'widget.page.config.error': 'Unable to load widget configuration',
'widget.page.config.goToConfig': 'Go to widget default configuration',
'widget.page.config.readOnlyMessage': 'This is the default configuration for this widget. Do you want to change it?',
'widget.page.config.legacySaveTooltip': 'Use the Save button inside the configuration form below',
'widget.icon.description': 'Upload your Icon from the Icon Library or your Computer',
'widget.icon.iconLibrary': 'Icon Library',
'widget.icon.upload': 'Upload',
Expand Down
2 changes: 2 additions & 0 deletions src/locales/it.js
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,7 @@ export default {
'menu.scheduler': 'Scheduler',
'menu.versioning': 'Versionamento',
'menu.apps': 'App',
'menu.legacyPlugins': 'Plugin Legacy',
'menu.settings': 'Amministrazione',
'menu.emailConfig': 'Configurazione Email',
'dashboard.cards.noPermission': 'Non hai il permesso di visualizzare questi dati',
Expand Down Expand Up @@ -411,6 +412,7 @@ export default {
'widget.page.config.error': 'Impossibile caricare la configurazione del widget',
'widget.page.config.goToConfig': 'Vai alla configurazione di default',
'widget.page.config.readOnlyMessage': 'Questa è la configurazione di default del widget. Vuoi modificarla?',
'widget.page.config.legacySaveTooltip': 'Usa il pulsante Salva nel modulo di configurazione qui sotto',
'widget.icon.description': 'Carica la tua icona dalla libreria delle icone o dal tuo computer',
'widget.icon.iconLibrary': 'Icon Library',
'widget.icon.upload': 'Carica',
Expand Down
1 change: 1 addition & 0 deletions src/locales/pt.js
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,7 @@ export default {
'menu.apiManagement.resources': 'Recursos',
'menu.apiManagement.services': 'serviços',
'menu.apiManagement.consumers': 'Consumer',
'menu.legacyPlugins': 'Plugins Legados',
'menu.settings': 'Administração',
'menu.emailConfig': 'Configuração de Email',
'menu.apps': 'Apps',
Expand Down
5 changes: 4 additions & 1 deletion src/state/page-config/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { initialize } from 'redux-form';
import { routeConverter } from '@entando/utils';
import { addToast, addErrors, clearErrors, TOAST_ERROR } from '@entando/messages';

import { isLegacyWidget } from 'helpers/legacyWidget';
import { loadSelectedPageTemplate } from 'state/page-templates/actions';
import { getSelectedPageTemplateMainFrame, getSelectedPageTemplateDefaultConfig } from 'state/page-templates/selectors';
import { loadSelectedPage, setSelectedPage } from 'state/pages/actions';
Expand Down Expand Up @@ -254,10 +255,12 @@ export const configOrUpdatePageWidget = (sourceWidgetId, sourceFrameId, targetFr
if (widget.hasConfig && !isAlreadyConfigured) {
const nextStep = widgetNextSteps[sourceWidgetId];
const appTourProgress = getAppTourProgress(getState());

history.push(routeConverter(
ROUTE_WIDGET_CONFIG,
{ pageCode, widgetCode: sourceWidgetId, framePos: targetFrameId },
));

if (nextStep && appTourProgress === APP_TOUR_STARTED) {
dispatch(setAppTourLastStep(nextStep));
}
Expand All @@ -277,7 +280,7 @@ export const editWidgetConfig = (frameId, pageCode) =>
const widget = getWidgetsMap(getState())[pageConfigItem.code];
const isConfigurableWidget = widget && widget.hasConfig;

if (isConfigurableWidget || pageConfigItem.config) {
if (isLegacyWidget(widget) || isConfigurableWidget || pageConfigItem.config) {
dispatch(initialize('widgetConfigForm', pageConfigItem.config));
history.push(routeConverter(
ROUTE_WIDGET_CONFIG,
Expand Down
1 change: 1 addition & 0 deletions src/state/page-config/selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ export const makeGetPageConfigCellMap = params => createSelector(
relatedCell.widgetTitle = widget.titles[locale] || widget.name;
relatedCell.widgetHasConfig = widget.hasConfig;
relatedCell.configUiName = widget.configUiName;
relatedCell.configUi = widget.configUi || null;
relatedCell.widgetHasConfigForm = !!isMicrofrontendWidgetForm(widget);
}
}
Expand Down
84 changes: 65 additions & 19 deletions src/ui/internal-page/VerticalMenuContainer.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { routeConverter, hasAccess } from '@entando/utils';
import { clearAppTourProgress, setAppTourLastStep, setWizardEnabled } from 'state/app-tour/actions';

import { adminConsoleUrl } from 'helpers/urlUtils';
import { checkPermission } from 'helpers/permissions';
import UserMenuContainer from 'ui/internal-page/UserMenuContainer';
import LanguageSelectContainer from 'ui/internal-page/LanguageSelectContainer';

Expand Down Expand Up @@ -54,6 +55,66 @@ const {

const publicUrl = process.env.PUBLIC_URL;

const getReportEntries = systemReport => (Array.isArray(systemReport) ? systemReport : []);

const getGroupItems = group =>
(Array.isArray(group['appBuilderMenu.items']) ? group['appBuilderMenu.items'] : []);

const renderMenuItemsByHook = (systemReport, hook) =>
getReportEntries(systemReport).filter(e => e['appBuilderMenu.hook'] === hook);

const renderDynamicItems = (items, intl, userPermissions) =>
items
.filter(item => checkPermission(item.requiredPermission, userPermissions))
.map(item => (
<SecondaryItem
key={item.id}
id={item.id}
title={intl.formatMessage({
id: item.labelId || item.id,
defaultMessage: item.defaultLabel,
})}
href={adminConsoleUrl(item.href)}
/>
));

const renderLegacyPluginsMenu = (intl, userPermissions, systemReport) => {
const pluginGroups = renderMenuItemsByHook(systemReport, 'legacyPlugins')
.filter(g => getGroupItems(g)
.some(item => checkPermission(item.requiredPermission, userPermissions)));

if (pluginGroups.length === 0) return null;

return (
<Item
id="menu-legacy-plugins"
key="legacyPlugins"
onClick={() => {}}
iconClass="fa fa-puzzle-piece"
title={intl.formatMessage({ id: 'menu.legacyPlugins', defaultMessage: 'Legacy Plugins' })}
>
{pluginGroups.flatMap((group) => {
const items = getGroupItems(group)
.filter(item => checkPermission(item.requiredPermission, userPermissions));
const pluginId = group['appBuilderMenu.pluginId'] || '';
const pluginLabel = group['appBuilderMenu.pluginLabel'] || pluginId;
const header = pluginId ? [
<SecondaryItem
key={`header-${pluginId}`}
id={`header-${pluginId}`}
className="LegacyPlugins__group-header"
title={pluginLabel}
/>,
] : [];
return [
...header,
...renderDynamicItems(items, intl, userPermissions),
];
})}
</Item>
);
};

const renderCmsMenuItems = (intl, userPermissions, systemReport, currSysConfigAdvancedSearchOn) => {
const hasMenuContentsAccess = hasAccess([
CRUD_CONTENTS_PERMISSION,
Expand All @@ -74,7 +135,8 @@ const renderCmsMenuItems = (intl, userPermissions, systemReport, currSysConfigAd
], userPermissions);
const hasMenuContentSettingsAccess = hasAccess(SUPERUSER_PERMISSION, userPermissions);

const { contentSchedulerPluginInstalled, contentWorkFlowPluginInstalled } = systemReport;
const cmsGroups = renderMenuItemsByHook(systemReport, 'cms');
const cmsItems = cmsGroups.length > 0 ? getGroupItems(cmsGroups[0]) : [];

return (
<Item
Expand Down Expand Up @@ -129,24 +191,7 @@ const renderCmsMenuItems = (intl, userPermissions, systemReport, currSysConfigAd
/>
)
}
{
hasMenuContentsAccess && contentSchedulerPluginInstalled && (
<SecondaryItem
id="menu-scheduler"
title={intl.formatMessage({ id: 'cms.menu.scheduler', defaultMessage: 'Scheduler' })}
href={adminConsoleUrl('do/jpcontentscheduler/config/viewItem.action')}
/>
)
}
{
hasMenuContentsAccess && contentWorkFlowPluginInstalled && (
<SecondaryItem
id="menu-workflow"
title={intl.formatMessage({ id: 'cms.menu.workflow', defaultMessage: 'WorkFlow' })}
href={adminConsoleUrl('do/jpcontentworkflow/Workflow/list.action')}
/>
)
}
{renderDynamicItems(cmsItems, intl, userPermissions)}
{
hasMenuContentTypeAccess && (
<SecondaryItem
Expand Down Expand Up @@ -337,6 +382,7 @@ const EntandoMenu = ({
], userPermissions) &&
renderCmsMenuItems(intl, userPermissions, systemReport, currSystemConfigAdvancedSearchOn)
}
{renderLegacyPluginsMenu(intl, userPermissions, systemReport)}
{

hasAccess(
Expand Down
4 changes: 4 additions & 0 deletions src/ui/pages/config/PageConfigGridCol.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ const PageConfigGridCol = ({ cellMap, cellKey, gridWidth }) => {
widgetHasConfig={col.widgetHasConfig}
widgetStatus={col.widgetStatus}
widgetHasConfigForm={col.widgetHasConfigForm}
configUi={col.configUi}
/>
);
} else {
Expand Down Expand Up @@ -92,6 +93,9 @@ PageConfigGridCol.propTypes = {
widgetCode: PropTypes.string,
widgetStatus: PropTypes.string,
configUiName: PropTypes.string,
configUi: PropTypes.shape({
customElement: PropTypes.string,
}),
frameDescr: PropTypes.string,
widgetTitle: PropTypes.string,
widgetHasConfig: PropTypes.bool,
Expand Down
9 changes: 7 additions & 2 deletions src/ui/pages/config/WidgetFrame.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { routeConverter } from '@entando/utils';

import { ROUTE_WIDGET_DETAIL, ROUTE_WIDGET_EDIT } from 'app-init/router';
import { WIDGET_STATUS_MATCH, WIDGET_STATUS_DIFF, WIDGET_STATUS_REMOVED } from 'state/page-config/const';
import { LEGACY_CONFIG_CUSTOM_ELEMENT } from 'helpers/legacyWidget';
import WidgetIcon from 'ui/widgets/common/WidgetIcon';


Expand All @@ -15,12 +16,14 @@ class WidgetFrame extends Component {
const {
widgetId, widgetName, widgetHasConfig, widgetStatus, frameId, frameName, frameIsMainFrame,
onClickDelete, connectDragSource, connectDropTarget, isOver, onClickSettings, onClickSaveAs,
configUiName, widgetHasConfigForm,
configUiName, widgetHasConfigForm, configUi,
} = this.props;

const isLegacy = configUi && configUi.customElement === LEGACY_CONFIG_CUSTOM_ELEMENT;

let actionsMenu = null;
if (widgetStatus !== WIDGET_STATUS_REMOVED) {
const configMenuItems = widgetHasConfig && (configUiName || widgetHasConfigForm) ?
const configMenuItems = widgetHasConfig && (configUiName || widgetHasConfigForm || isLegacy) ?
[
(
<MenuItem
Expand Down Expand Up @@ -137,6 +140,7 @@ WidgetFrame.propTypes = {
widgetStatus: PropTypes.oneOf([WIDGET_STATUS_MATCH, WIDGET_STATUS_DIFF, WIDGET_STATUS_REMOVED]),
configUiName: PropTypes.string,
widgetHasConfigForm: PropTypes.bool,
configUi: PropTypes.shape({ customElement: PropTypes.string }),

/* eslint-disable react/no-unused-prop-types */
frameId: PropTypes.number, // needed when it's droppable
Expand Down Expand Up @@ -166,6 +170,7 @@ WidgetFrame.defaultProps = {
isOver: false,
configUiName: null,
widgetHasConfigForm: false,
configUi: null,
};


Expand Down
Loading
Loading