From 257dd3de8940ffa29883686a0f182a750581b7a6 Mon Sep 17 00:00:00 2001 From: Boubaker Khanfir Date: Mon, 29 Apr 2024 11:10:03 +0100 Subject: [PATCH] feat: Allow to access the complete list of applications - MEED-6266 - Meeds-io/meeds#1917 (#46) This change will give the ability to access the complete list of available portlets to be able to add it in a page. This feature will be disabled by default and can be enabled using a dedicated feature flag (`exo.feature.LayoutAllAppsDrawer.enabled`). --- .../locale/portlet/LayoutEditor_en.properties | 18 ++++++ .../webapp/WEB-INF/conf/configuration.xml | 1 + .../conf/layout/features-configuration.xml | 35 +++++++++++ .../main/webapp/WEB-INF/jsp/layoutEditor.jsp | 14 +++++ .../src/main/webapp/WEB-INF/portlet.xml | 2 +- .../src/main/webapp/html/layoutEditor.html | 7 --- .../layout-editor/components/LayoutEditor.vue | 15 ++++- .../components/content/Content.vue | 9 +-- .../content/common/ApplicationCard.vue | 14 +++-- .../common/ApplicationCategoryCard.vue | 6 +- .../drawer/AddApplicationDrawer.vue | 60 ++++++++++++++++++- .../components/drawer/PageTemplateDrawer.vue | 2 +- .../components/toolbar/actions/SaveButton.vue | 2 +- .../vue-app/layout-editor/js/LayoutUtils.js | 10 ++-- 14 files changed, 163 insertions(+), 32 deletions(-) create mode 100644 layout-webapp/src/main/webapp/WEB-INF/conf/layout/features-configuration.xml create mode 100644 layout-webapp/src/main/webapp/WEB-INF/jsp/layoutEditor.jsp delete mode 100644 layout-webapp/src/main/webapp/html/layoutEditor.html diff --git a/layout-webapp/src/main/resources/locale/portlet/LayoutEditor_en.properties b/layout-webapp/src/main/resources/locale/portlet/LayoutEditor_en.properties index dd3a1b6ec..9c50bd089 100644 --- a/layout-webapp/src/main/resources/locale/portlet/LayoutEditor_en.properties +++ b/layout-webapp/src/main/resources/locale/portlet/LayoutEditor_en.properties @@ -86,3 +86,21 @@ layout.errorUploadingPreview=An error occurred while uploading Illustration. Ple layout.uploadPreviewTitle=Upload an illustration for page template layout.pageTemplateCreatedSuccessfully=Page template created successfully layout.cellHoverTooltip=Drag mouse to select a zone or click to add an application +layout.loadMore=Load more +layout.spacesApplications=Spaces applications +layout.Tools=Tools +layout.Contributions=Contribution +layout.Content=Content +layout.content=Content +layout.Navigation=Navigation +layout.analytics=Analytics +layout.otherApplications=Advanced feature +layout.layout=Layout +layout.notes=Notes +layout.kudos=Kudos +layout.social-portlet=Social +layout.app-center=Application Center +layout.wallet=Wallet +layout.perk-store=Perks +layout.deeds-tenant=Deeds Tenant +layout.gamification-portlets=Gamification diff --git a/layout-webapp/src/main/webapp/WEB-INF/conf/configuration.xml b/layout-webapp/src/main/webapp/WEB-INF/conf/configuration.xml index fe806a361..3554f55bb 100644 --- a/layout-webapp/src/main/webapp/WEB-INF/conf/configuration.xml +++ b/layout-webapp/src/main/webapp/WEB-INF/conf/configuration.xml @@ -25,5 +25,6 @@ xmlns="http://www.exoplaform.org/xml/ns/kernel_1_2.xsd"> war:/conf/layout/dynamic-container-configuration.xml + war:/conf/layout/features-configuration.xml diff --git a/layout-webapp/src/main/webapp/WEB-INF/conf/layout/features-configuration.xml b/layout-webapp/src/main/webapp/WEB-INF/conf/layout/features-configuration.xml new file mode 100644 index 000000000..4ecdeab77 --- /dev/null +++ b/layout-webapp/src/main/webapp/WEB-INF/conf/layout/features-configuration.xml @@ -0,0 +1,35 @@ + + + + + MeedsFeatureProperties + org.exoplatform.container.ExtendedPropertyConfigurator + + + MeedsFeatureProperties + + + + + diff --git a/layout-webapp/src/main/webapp/WEB-INF/jsp/layoutEditor.jsp b/layout-webapp/src/main/webapp/WEB-INF/jsp/layoutEditor.jsp new file mode 100644 index 000000000..bf35b935a --- /dev/null +++ b/layout-webapp/src/main/webapp/WEB-INF/jsp/layoutEditor.jsp @@ -0,0 +1,14 @@ +<%@page import="org.exoplatform.commons.api.settings.ExoFeatureService"%> +<%@page import="org.exoplatform.container.ExoContainerContext"%> +<% + ExoFeatureService featureService = ExoContainerContext.getService(ExoFeatureService.class); + boolean layoutAllAppsDrawerActive = featureService.isFeatureActiveForUser("LayoutAllAppsDrawer", request.getRemoteUser()); +%> +
+
+ +
+
diff --git a/layout-webapp/src/main/webapp/WEB-INF/portlet.xml b/layout-webapp/src/main/webapp/WEB-INF/portlet.xml index 54a4ea0b9..fb17c13d7 100644 --- a/layout-webapp/src/main/webapp/WEB-INF/portlet.xml +++ b/layout-webapp/src/main/webapp/WEB-INF/portlet.xml @@ -65,7 +65,7 @@ org.exoplatform.commons.api.portlet.GenericDispatchedViewPortlet portlet-view-dispatched-file-path - /html/layoutEditor.html + /WEB-INF/jsp/layoutEditor.jsp text/html diff --git a/layout-webapp/src/main/webapp/html/layoutEditor.html b/layout-webapp/src/main/webapp/html/layoutEditor.html deleted file mode 100644 index 59a8ce300..000000000 --- a/layout-webapp/src/main/webapp/html/layoutEditor.html +++ /dev/null @@ -1,7 +0,0 @@ -
-
- -
-
diff --git a/layout-webapp/src/main/webapp/vue-app/layout-editor/components/LayoutEditor.vue b/layout-webapp/src/main/webapp/vue-app/layout-editor/components/LayoutEditor.vue index 20e179f7e..bf098f544 100644 --- a/layout-webapp/src/main/webapp/vue-app/layout-editor/components/LayoutEditor.vue +++ b/layout-webapp/src/main/webapp/vue-app/layout-editor/components/LayoutEditor.vue @@ -67,8 +67,21 @@ export default { draftPageRef() { return this.draftPageKey?.ref || (this.draftPageKey && `${this.draftPageKey.site.typeName}::${this.draftPageKey.site.name}::${this.draftPageKey.name}`); }, + pageLoaded() { + return !!this.draftPageRef && !!this.page; + }, }, watch: { + pageLoaded() { + if (this.pageLoaded) { + this.$pageLayoutService.getPageLayout(this.pageRef, 'contentId') + .then(layout => { + const draftPageLayout = this.$layoutUtils.cleanAttributes(layout, true, false); + return this.$pageLayoutService.updatePageLayout(this.draftPageRef, draftPageLayout, 'contentId') + .then(draftLayout => this.setDraftLayout(draftLayout)); + }); + } + }, pageRef: { immediate: true, handler() { @@ -84,8 +97,6 @@ export default { handler() { if (this.draftPageRef) { this.$root.draftPageRef = this.draftPageRef; - this.$pageLayoutService.getPageLayout(this.draftPageRef, 'contentId') - .then(draftLayout => this.draftLayout = draftLayout); } }, }, diff --git a/layout-webapp/src/main/webapp/vue-app/layout-editor/components/content/Content.vue b/layout-webapp/src/main/webapp/vue-app/layout-editor/components/content/Content.vue index fbcfd08a9..0233d3788 100644 --- a/layout-webapp/src/main/webapp/vue-app/layout-editor/components/content/Content.vue +++ b/layout-webapp/src/main/webapp/vue-app/layout-editor/components/content/Content.vue @@ -91,12 +91,7 @@ export default { handler() { if (this.layout) { const layout = JSON.parse(JSON.stringify(this.layout)); - if (!layout.children?.length) { - this.$layoutUtils.newParentContainer(layout); - this.saveDraft(layout); - } else { - this.setLayout(layout); - } + this.setLayout(layout); } }, }, @@ -354,7 +349,7 @@ export default { this.resetSectionHistory(); this.loading++; - const layoutToUpdate = this.$layoutUtils.cleanAttributes(layout || this.layoutToEdit); + const layoutToUpdate = this.$layoutUtils.cleanAttributes(layout || this.layoutToEdit, false, true); return this.$pageLayoutService.updatePageLayout(this.$root.draftPageRef, layoutToUpdate, 'contentId') .then(layout => this.setLayout(layout)) .finally(() => window.setTimeout(() => this.loading--, 200)); diff --git a/layout-webapp/src/main/webapp/vue-app/layout-editor/components/content/common/ApplicationCard.vue b/layout-webapp/src/main/webapp/vue-app/layout-editor/components/content/common/ApplicationCard.vue index 7734fb33c..9335413cf 100644 --- a/layout-webapp/src/main/webapp/vue-app/layout-editor/components/content/common/ApplicationCard.vue +++ b/layout-webapp/src/main/webapp/vue-app/layout-editor/components/content/common/ApplicationCard.vue @@ -20,15 +20,17 @@ --> + @@ -56,7 +74,38 @@ export default { data: () => ({ drawer: false, expanded: 0, + canLoadMore: true, + layoutAllAppsDrawer: eXo.env.portal.layoutAllAppsDrawer, + allApplications: [], }), + computed: { + applicationCategories() { + const applicationCategories = this.$root.applicationCategories.slice(); + applicationCategories.forEach(c => c.label = this.$te(`layout.${c.name}`) ? this.$t(`layout.${c.name}`) : c.name); + return applicationCategories; + }, + applications() { + return this.applicationCategories.flatMap(c => c.applications); + }, + otherApplications() { + return this.allApplications.filter(a => !this.applications.find(app => app.contentId === a.contentId)); + }, + otherCategories() { + return this.otherApplications.reduce((otherCategories, application) => { + const category = otherCategories.find(c => c.name === application.categoryName); + if (category) { + category.applications.push(application); + } else { + otherCategories.push({ + name: application.categoryName, + label: `${this.$t('layout.otherApplications')}: ${this.$te(`layout.${application.categoryName}`) ? this.$t(`layout.${application.categoryName}`) : application.categoryName}`, + applications: [application], + }); + } + return otherCategories; + }, []); + }, + }, methods: { open() { this.$refs.drawer.endLoading(); @@ -69,6 +118,15 @@ export default { this.close(); }, 200); }, + loadMore() { + this.$refs.drawer.startLoading(); + this.$applicationRegistryService.getApplications('supportedModes') + .then(applications => this.allApplications = applications) + .finally(() => { + this.canLoadMore = false; + this.$refs.drawer.endLoading(); + }); + }, close() { this.$refs.drawer.close(); }, diff --git a/layout-webapp/src/main/webapp/vue-app/layout-editor/components/drawer/PageTemplateDrawer.vue b/layout-webapp/src/main/webapp/vue-app/layout-editor/components/drawer/PageTemplateDrawer.vue index 61a7d90a4..1d6fbb502 100644 --- a/layout-webapp/src/main/webapp/vue-app/layout-editor/components/drawer/PageTemplateDrawer.vue +++ b/layout-webapp/src/main/webapp/vue-app/layout-editor/components/drawer/PageTemplateDrawer.vue @@ -150,7 +150,7 @@ export default { this.$refs.drawer.close(); }, save() { - const pageLayout = this.$layoutUtils.cleanAttributes(this.$root.layout, true); + const pageLayout = this.$layoutUtils.cleanAttributes(this.$root.layout, true, true); const savePageRequest = this.templateId ? this.$pageTemplateService.updatePageTemplate(pageLayout, this.templateId) : this.$pageTemplateService.createPageTemplate(pageLayout); diff --git a/layout-webapp/src/main/webapp/vue-app/layout-editor/components/toolbar/actions/SaveButton.vue b/layout-webapp/src/main/webapp/vue-app/layout-editor/components/toolbar/actions/SaveButton.vue index 6a4f672eb..8a05803fb 100644 --- a/layout-webapp/src/main/webapp/vue-app/layout-editor/components/toolbar/actions/SaveButton.vue +++ b/layout-webapp/src/main/webapp/vue-app/layout-editor/components/toolbar/actions/SaveButton.vue @@ -43,7 +43,7 @@ export default { methods: { savePage() { this.loading = true; - const layoutToUpdate = this.$layoutUtils.cleanAttributes(this.$root.layout); + const layoutToUpdate = this.$layoutUtils.cleanAttributes(this.$root.layout, false, true); return this.$pageLayoutService.updatePageLayout(this.$root.pageRef, layoutToUpdate, 'contentId', true) .then(() => this.$root.$emit('layout-page-saved')) .catch(() => this.$root.$emit('alert-message', this.$t('layout.pageSavingError'), 'error')) diff --git a/layout-webapp/src/main/webapp/vue-app/layout-editor/js/LayoutUtils.js b/layout-webapp/src/main/webapp/vue-app/layout-editor/js/LayoutUtils.js index 8e70dbd0b..4ee874be2 100644 --- a/layout-webapp/src/main/webapp/vue-app/layout-editor/js/LayoutUtils.js +++ b/layout-webapp/src/main/webapp/vue-app/layout-editor/js/LayoutUtils.js @@ -531,12 +531,14 @@ export function isValidTargetMovingCell(section, movingCell, targetRowIndex, tar export function isBetween(value, b0, b1) { return value >= b0 && value <= b1; } - -export function cleanAttributes(container, cleanStorage) { + +export function cleanAttributes(container, cleanStorage, cleanStyle) { container = JSON.parse(JSON.stringify(container)); - applyDesktopStyle(container); + if (cleanStyle) { + applyDesktopStyle(container); + } if (container.children?.length) { - container.children = container.children.map(c => cleanAttributes(c, cleanStorage)); + container.children = container.children.map(c => cleanAttributes(c, cleanStorage, cleanStyle)); } if (container.randomId || cleanStorage) { delete container.storageId;