From 5a25d537a93159ab3bd30836d35ca567aec2a89e Mon Sep 17 00:00:00 2001 From: Boubaker Khanfir Date: Fri, 17 May 2024 12:32:37 +0100 Subject: [PATCH 01/15] feat: Add Page Templates Portlet - MEED-6843 - Meeds-io/MIPs#133 (#63) --- .../io/meeds/layout/LayoutApplication.java | 3 +- .../locale/portlet/LayoutEditor_en.properties | 7 ++ .../main/webapp/WEB-INF/gatein-resources.xml | 43 ++++++++--- .../src/main/webapp/WEB-INF/portlet.xml | 19 +++++ .../webapp/html/pageTemplatesManagement.html | 7 ++ .../components/PageTemplatesManagement.vue | 48 +++++++++++++ .../components/header/Toolbar.vue | 57 +++++++++++++++ .../components/list/PageTemplateItem.vue | 52 ++++++++++++++ .../components/list/PageTemplates.vue | 72 +++++++++++++++++++ .../initComponents.js | 36 ++++++++++ .../vue-app/page-templates-management/main.js | 50 +++++++++++++ layout-webapp/webpack.prod.js | 1 + 12 files changed, 385 insertions(+), 10 deletions(-) create mode 100644 layout-webapp/src/main/webapp/html/pageTemplatesManagement.html create mode 100644 layout-webapp/src/main/webapp/vue-app/page-templates-management/components/PageTemplatesManagement.vue create mode 100644 layout-webapp/src/main/webapp/vue-app/page-templates-management/components/header/Toolbar.vue create mode 100644 layout-webapp/src/main/webapp/vue-app/page-templates-management/components/list/PageTemplateItem.vue create mode 100644 layout-webapp/src/main/webapp/vue-app/page-templates-management/components/list/PageTemplates.vue create mode 100644 layout-webapp/src/main/webapp/vue-app/page-templates-management/initComponents.js create mode 100644 layout-webapp/src/main/webapp/vue-app/page-templates-management/main.js diff --git a/layout-webapp/src/main/java/io/meeds/layout/LayoutApplication.java b/layout-webapp/src/main/java/io/meeds/layout/LayoutApplication.java index f20693629..7ba39cbbe 100644 --- a/layout-webapp/src/main/java/io/meeds/layout/LayoutApplication.java +++ b/layout-webapp/src/main/java/io/meeds/layout/LayoutApplication.java @@ -27,8 +27,7 @@ AvailableIntegration.KERNEL_MODULE, AvailableIntegration.JPA_MODULE, AvailableIntegration.LIQUIBASE_MODULE, - AvailableIntegration.WEB_SECURITY_MODULE, - AvailableIntegration.WEB_TRANSACTION_MODULE, + AvailableIntegration.WEB_MODULE, }) @EnableJpaRepositories(basePackages = LayoutApplication.MODULE_NAME) @PropertySource("classpath:application.properties") 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 a38c8aa34..2bc7219b5 100644 --- a/layout-webapp/src/main/resources/locale/portlet/LayoutEditor_en.properties +++ b/layout-webapp/src/main/resources/locale/portlet/LayoutEditor_en.properties @@ -111,3 +111,10 @@ layout.reminder.description.part1=From now on, in few clicks, design your page u layout.reminder.description.part2=- Organize Pages in Sections layout.reminder.description.part3=- Manage Apps Display layout.reminder.description.part4=- Preview Page Content + +pageTemplates.title=Page Templates +pageTemplates.add=Add +pageTemplates.filter.placeholder=Filter by name, description +pageTemplates.label.name=Name +pageTemplates.label.description=Description +pageTemplate.label.preview=Preview of {0} template diff --git a/layout-webapp/src/main/webapp/WEB-INF/gatein-resources.xml b/layout-webapp/src/main/webapp/WEB-INF/gatein-resources.xml index 6e325efc8..3a7668f35 100644 --- a/layout-webapp/src/main/webapp/WEB-INF/gatein-resources.xml +++ b/layout-webapp/src/main/webapp/WEB-INF/gatein-resources.xml @@ -1,4 +1,4 @@ - + + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.exoplatform.org/xml/ns/gatein_resources_1_4 http://www.exoplatform.org/xml/ns/gatein_resources_1_4" + xmlns="http://www.exoplatform.org/xml/ns/gatein_resources_1_4"> layout @@ -214,10 +214,6 @@ imageCropper - - cropper - Cropper - commonLayoutComponents @@ -239,5 +235,36 @@ + + PageTemplatesManagement + + + + commonVueComponents + + + commonLayoutComponents + + + attachImage + + + translationField + + + applicationToolbarComponent + + + extensionRegistry + + + eXoVueI18n + + + + diff --git a/layout-webapp/src/main/webapp/WEB-INF/portlet.xml b/layout-webapp/src/main/webapp/WEB-INF/portlet.xml index fb17c13d7..946a7ecdb 100644 --- a/layout-webapp/src/main/webapp/WEB-INF/portlet.xml +++ b/layout-webapp/src/main/webapp/WEB-INF/portlet.xml @@ -78,4 +78,23 @@ + + PageTemplatesManagement + Page Templates Management Portlet + org.exoplatform.commons.api.portlet.GenericDispatchedViewPortlet + + portlet-view-dispatched-file-path + /html/pageTemplatesManagement.html + + + text/html + + en + locale.portlet.LayoutEditor + + site navigation + site navigation Management + + + diff --git a/layout-webapp/src/main/webapp/html/pageTemplatesManagement.html b/layout-webapp/src/main/webapp/html/pageTemplatesManagement.html new file mode 100644 index 000000000..3a077d97b --- /dev/null +++ b/layout-webapp/src/main/webapp/html/pageTemplatesManagement.html @@ -0,0 +1,7 @@ +
+
+ +
+
diff --git a/layout-webapp/src/main/webapp/vue-app/page-templates-management/components/PageTemplatesManagement.vue b/layout-webapp/src/main/webapp/vue-app/page-templates-management/components/PageTemplatesManagement.vue new file mode 100644 index 000000000..665764fc0 --- /dev/null +++ b/layout-webapp/src/main/webapp/vue-app/page-templates-management/components/PageTemplatesManagement.vue @@ -0,0 +1,48 @@ + + + diff --git a/layout-webapp/src/main/webapp/vue-app/page-templates-management/components/header/Toolbar.vue b/layout-webapp/src/main/webapp/vue-app/page-templates-management/components/header/Toolbar.vue new file mode 100644 index 000000000..bf6b0eb99 --- /dev/null +++ b/layout-webapp/src/main/webapp/vue-app/page-templates-management/components/header/Toolbar.vue @@ -0,0 +1,57 @@ + + + diff --git a/layout-webapp/src/main/webapp/vue-app/page-templates-management/components/list/PageTemplateItem.vue b/layout-webapp/src/main/webapp/vue-app/page-templates-management/components/list/PageTemplateItem.vue new file mode 100644 index 000000000..ced1f241e --- /dev/null +++ b/layout-webapp/src/main/webapp/vue-app/page-templates-management/components/list/PageTemplateItem.vue @@ -0,0 +1,52 @@ + + \ No newline at end of file diff --git a/layout-webapp/src/main/webapp/vue-app/page-templates-management/components/list/PageTemplates.vue b/layout-webapp/src/main/webapp/vue-app/page-templates-management/components/list/PageTemplates.vue new file mode 100644 index 000000000..3b7754a47 --- /dev/null +++ b/layout-webapp/src/main/webapp/vue-app/page-templates-management/components/list/PageTemplates.vue @@ -0,0 +1,72 @@ + + diff --git a/layout-webapp/src/main/webapp/vue-app/page-templates-management/initComponents.js b/layout-webapp/src/main/webapp/vue-app/page-templates-management/initComponents.js new file mode 100644 index 000000000..e71d3c9c0 --- /dev/null +++ b/layout-webapp/src/main/webapp/vue-app/page-templates-management/initComponents.js @@ -0,0 +1,36 @@ +/* + * This file is part of the Meeds project (https://meeds.io/). + * + * Copyright (C) 2020 - 2024 Meeds Association contact@meeds.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import PageTemplatesManagement from './components/PageTemplatesManagement.vue'; + +import Toolbar from './components/header/Toolbar.vue'; +import PageTemplates from './components/list/PageTemplates.vue'; + +import PageTemplateItem from './components/list/PageTemplateItem.vue'; + +const components = { + 'page-templates-management': PageTemplatesManagement, + 'page-templates-management-toolbar': Toolbar, + 'page-templates-management-list': PageTemplates, + 'page-templates-management-item': PageTemplateItem, +}; + +for (const key in components) { + Vue.component(key, components[key]); +} diff --git a/layout-webapp/src/main/webapp/vue-app/page-templates-management/main.js b/layout-webapp/src/main/webapp/vue-app/page-templates-management/main.js new file mode 100644 index 000000000..c7695b344 --- /dev/null +++ b/layout-webapp/src/main/webapp/vue-app/page-templates-management/main.js @@ -0,0 +1,50 @@ +/* + * This file is part of the Meeds project (https://meeds.io/). + * + * Copyright (C) 2020 - 2024 Meeds Association contact@meeds.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import './initComponents.js'; + +// get overridden components if exists +if (extensionRegistry) { + const components = extensionRegistry.loadComponents('PageTemplatesManagement'); + if (components && components.length > 0) { + components.forEach(cmp => { + Vue.component(cmp.componentName, cmp.componentOptions); + }); + } +} + +const lang = eXo?.env.portal.language || 'en'; +const url = `${eXo.env.portal.context}/${eXo.env.portal.rest}/i18n/bundle/locale.portlet.LayoutEditor-${lang}.json`; + +const appId = 'pageTemplatesManagement'; +export function init() { + exoi18n.loadLanguageAsync(lang, url) + .then(i18n => + Vue.createApp({ + template: ``, + vuetify: Vue.prototype.vuetifyOptions, + i18n, + computed: { + isMobile() { + return this.$vuetify.breakpoint.smAndDown; + }, + }, + }, `#${appId}`, 'Page Layout') + ); +} diff --git a/layout-webapp/webpack.prod.js b/layout-webapp/webpack.prod.js index 5c3f10a5a..1eb9c1feb 100644 --- a/layout-webapp/webpack.prod.js +++ b/layout-webapp/webpack.prod.js @@ -9,6 +9,7 @@ const config = { siteManagement: './src/main/webapp/vue-app/site-management/main.js', layoutEditor: './src/main/webapp/vue-app/layout-editor/main.js', pageLayout: './src/main/webapp/vue-app/page-layout/main.js', + pageTemplatesManagement: './src/main/webapp/vue-app/page-templates-management/main.js', }, mode: 'production', context: path.resolve(__dirname, '.'), From 2812fef922a0aff2bb1ae57927e7c3aefe5fef17 Mon Sep 17 00:00:00 2001 From: Boubaker Khanfir Date: Fri, 17 May 2024 12:40:02 +0100 Subject: [PATCH 02/15] feat: Add Page Templates text filtering - MEED-6844 - Meeds-io/MIPs#133 (#64) --- .../components/header/Toolbar.vue | 2 +- .../components/list/PageTemplates.vue | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/layout-webapp/src/main/webapp/vue-app/page-templates-management/components/header/Toolbar.vue b/layout-webapp/src/main/webapp/vue-app/page-templates-management/components/header/Toolbar.vue index bf6b0eb99..2a3177b1e 100644 --- a/layout-webapp/src/main/webapp/vue-app/page-templates-management/components/header/Toolbar.vue +++ b/layout-webapp/src/main/webapp/vue-app/page-templates-management/components/header/Toolbar.vue @@ -28,7 +28,7 @@ placeholder: $t('pageTemplates.filter.placeholder'), tooltip: $t('pageTemplates.filter.placeholder'), }" - @filter-text-input-end-typing="$emit('page-templates-filter', $event)"> + @filter-text-input="$emit('page-templates-filter', $event)"> \ No newline at end of file diff --git a/layout-webapp/src/main/webapp/vue-app/page-templates-management/components/list/PageTemplates.vue b/layout-webapp/src/main/webapp/vue-app/page-templates-management/components/list/PageTemplates.vue index e40f79aac..fb1e8432a 100644 --- a/layout-webapp/src/main/webapp/vue-app/page-templates-management/components/list/PageTemplates.vue +++ b/layout-webapp/src/main/webapp/vue-app/page-templates-management/components/list/PageTemplates.vue @@ -54,6 +54,14 @@ export default { class: 'page-template-description-header', width: '70%' }, + { + text: this.$t('pageTemplates.label.category'), + value: 'category', + align: 'left', + sortable: false, + class: 'page-template-category-header', + width: '120px' + }, ]; }, filteredPageTemplates() { From 3bb78cd19d3e1ddbc4e85b40103c23122db81dc2 Mon Sep 17 00:00:00 2001 From: Boubaker Khanfir Date: Fri, 17 May 2024 15:57:10 +0100 Subject: [PATCH 04/15] feat: Allow to manage Page Template Status - MEED-6845 - Meeds-io/MIPs#133 (#66) --- .../meeds/layout/rest/PageTemplateRest.java | 13 ++++ .../layout/service/PageTemplateService.java | 28 ++++++--- .../service/PageTemplateServiceTest.java | 60 +++++++++++++++++++ .../locale/portlet/LayoutEditor_en.properties | 3 + .../js/PageTemplateService.js | 21 +++++-- .../components/drawer/PageTemplateDrawer.vue | 7 ++- .../components/list/PageTemplateItem.vue | 33 +++++++++- .../components/list/PageTemplates.vue | 16 ++++- 8 files changed, 163 insertions(+), 18 deletions(-) diff --git a/layout-service/src/main/java/io/meeds/layout/rest/PageTemplateRest.java b/layout-service/src/main/java/io/meeds/layout/rest/PageTemplateRest.java index 9bf8320cc..556e6821d 100644 --- a/layout-service/src/main/java/io/meeds/layout/rest/PageTemplateRest.java +++ b/layout-service/src/main/java/io/meeds/layout/rest/PageTemplateRest.java @@ -61,6 +61,19 @@ public List getPageTemplates(HttpServletRequest request) { return pageTemplateService.getPageTemplates(request.getLocale(), true); } + @GetMapping("{id}") + @Secured("users") + @Operation(summary = "Retrieve a page template designated by its id", method = "GET", + description = "This will retrieve a page template designated by its id") + @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "Request fulfilled"), }) + public PageTemplate getPageTemplate( + HttpServletRequest request, + @Parameter(description = "Page template identifier") + @PathVariable("id") + long id) { + return pageTemplateService.getPageTemplate(id, request.getLocale(), true); + } + @PostMapping @Secured("users") @Operation(summary = "Create a page template", method = "POST", description = "This creates a new page template") diff --git a/layout-service/src/main/java/io/meeds/layout/service/PageTemplateService.java b/layout-service/src/main/java/io/meeds/layout/service/PageTemplateService.java index fb373ac17..7cf962c58 100644 --- a/layout-service/src/main/java/io/meeds/layout/service/PageTemplateService.java +++ b/layout-service/src/main/java/io/meeds/layout/service/PageTemplateService.java @@ -70,19 +70,19 @@ public List getPageTemplates(boolean expand) { public List getPageTemplates(Locale locale, boolean expand) { List pageTemplates = pageTemplateStorage.getPageTemplates(); if (expand) { - pageTemplates.forEach(pageTemplate -> { - pageTemplate.setName(getLabel(pageTemplate.getId(), PageTemplateTranslationPlugin.TITLE_FIELD_NAME, locale)); - pageTemplate.setDescription(getLabel(pageTemplate.getId(), PageTemplateTranslationPlugin.DESCRIPTION_FIELD_NAME, locale)); - List attachmentFileIds = attachmentService.getAttachmentFileIds(PageTemplateAttachmentPlugin.OBJECT_TYPE, - String.valueOf(pageTemplate.getId())); - if (CollectionUtils.isNotEmpty(attachmentFileIds)) { - pageTemplate.setIllustrationId(Long.parseLong(attachmentFileIds.get(0))); - } - }); + pageTemplates.forEach(pageTemplate -> computePageTemplateAttributes(locale, pageTemplate)); } return pageTemplates; } + public PageTemplate getPageTemplate(long id, Locale locale, boolean expand) { + PageTemplate pageTemplate = pageTemplateStorage.getPageTemplate(id); + if (expand) { + computePageTemplateAttributes(locale, pageTemplate); + } + return pageTemplate; + } + public PageTemplate getPageTemplate(long id) { return pageTemplateStorage.getPageTemplate(id); } @@ -157,4 +157,14 @@ private String getLabel(long templateId, String fieldName, Locale locale) { } } + private void computePageTemplateAttributes(Locale locale, PageTemplate pageTemplate) { + pageTemplate.setName(getLabel(pageTemplate.getId(), PageTemplateTranslationPlugin.TITLE_FIELD_NAME, locale)); + pageTemplate.setDescription(getLabel(pageTemplate.getId(), PageTemplateTranslationPlugin.DESCRIPTION_FIELD_NAME, locale)); + List attachmentFileIds = attachmentService.getAttachmentFileIds(PageTemplateAttachmentPlugin.OBJECT_TYPE, + String.valueOf(pageTemplate.getId())); + if (CollectionUtils.isNotEmpty(attachmentFileIds)) { + pageTemplate.setIllustrationId(Long.parseLong(attachmentFileIds.get(0))); + } + } + } diff --git a/layout-service/src/test/java/io/meeds/layout/service/PageTemplateServiceTest.java b/layout-service/src/test/java/io/meeds/layout/service/PageTemplateServiceTest.java index ad52b090e..dc4604c00 100644 --- a/layout-service/src/test/java/io/meeds/layout/service/PageTemplateServiceTest.java +++ b/layout-service/src/test/java/io/meeds/layout/service/PageTemplateServiceTest.java @@ -176,6 +176,66 @@ public void getPageTemplatesWithExpand() throws ObjectNotFoundException { assertEquals(32l, pageTemplates.get(0).getIllustrationId()); } + @Test + public void getPageTemplateWithExpand() throws ObjectNotFoundException { + PageTemplate template = new PageTemplate(2l, false, LAYOUT_CATEGORY, LAYOUT_CONTENT); + when(localeConfigService.getDefaultLocaleConfig()).thenReturn(defaultLocaleConfig); + when(defaultLocaleConfig.getLocale()).thenReturn(Locale.ENGLISH); + + when(pageTemplateStorage.getPageTemplate(2l)).thenReturn(template); + + PageTemplate retrievedPageTemplate = pageTemplateService.getPageTemplate(2l); + assertNotNull(retrievedPageTemplate); + assertEquals(template.getId(), retrievedPageTemplate.getId()); + assertEquals(template.getContent(), retrievedPageTemplate.getContent()); + assertNull(retrievedPageTemplate.getName()); + assertNull(retrievedPageTemplate.getDescription()); + assertEquals(0l, retrievedPageTemplate.getIllustrationId()); + + when(translationService.getTranslationField(PageTemplateTranslationPlugin.OBJECT_TYPE, + template.getId(), + PageTemplateTranslationPlugin.TITLE_FIELD_NAME)).thenThrow(ObjectNotFoundException.class); + retrievedPageTemplate = pageTemplateService.getPageTemplate(2l, Locale.FRENCH, true); + assertNotNull(retrievedPageTemplate); + + reset(translationService); + + TranslationField titleTranslationField = mock(TranslationField.class); + when(translationService.getTranslationField(PageTemplateTranslationPlugin.OBJECT_TYPE, + template.getId(), + PageTemplateTranslationPlugin.TITLE_FIELD_NAME)).thenReturn(titleTranslationField); + retrievedPageTemplate = pageTemplateService.getPageTemplate(2l, Locale.FRENCH, true); + assertNotNull(retrievedPageTemplate); + assertEquals(template.getId(), retrievedPageTemplate.getId()); + assertEquals(template.getContent(), retrievedPageTemplate.getContent()); + assertNull(retrievedPageTemplate.getName()); + assertNull(retrievedPageTemplate.getDescription()); + assertEquals(0l, retrievedPageTemplate.getIllustrationId()); + + String frTitle = "testTitle"; + when(titleTranslationField.getLabels()).thenReturn(Collections.singletonMap(Locale.FRENCH, frTitle)); + + retrievedPageTemplate = pageTemplateService.getPageTemplate(2l, Locale.FRENCH, true); + assertEquals(frTitle, retrievedPageTemplate.getName()); + + TranslationField descriptionTranslationField = mock(TranslationField.class); + when(translationService.getTranslationField(PageTemplateTranslationPlugin.OBJECT_TYPE, + template.getId(), + PageTemplateTranslationPlugin.DESCRIPTION_FIELD_NAME)).thenReturn(descriptionTranslationField); + String enDesc = "testDescription"; + when(descriptionTranslationField.getLabels()).thenReturn(Collections.singletonMap(Locale.ENGLISH, enDesc)); + + retrievedPageTemplate = pageTemplateService.getPageTemplate(2l, Locale.ENGLISH, true); + assertNotNull(retrievedPageTemplate); + assertEquals(enDesc, retrievedPageTemplate.getDescription()); + + when(attachmentService.getAttachmentFileIds(PageTemplateAttachmentPlugin.OBJECT_TYPE, + "2")).thenReturn(Collections.singletonList("32")); + retrievedPageTemplate = pageTemplateService.getPageTemplate(2l, Locale.GERMAN, true); + assertNotNull(retrievedPageTemplate); + assertEquals(32l, retrievedPageTemplate.getIllustrationId()); + } + @Test public void getPageTemplate() { when(pageTemplateStorage.getPageTemplate(2l)).thenReturn(pageTemplate); 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 b7d3c0eeb..c939f69bb 100644 --- a/layout-webapp/src/main/resources/locale/portlet/LayoutEditor_en.properties +++ b/layout-webapp/src/main/resources/locale/portlet/LayoutEditor_en.properties @@ -118,4 +118,7 @@ pageTemplates.filter.placeholder=Filter by name, description pageTemplates.label.name=Name pageTemplates.label.description=Description pageTemplates.label.category=Category +pageTemplates.label.status=Status pageTemplate.label.preview=Preview of {0} template +pageTemplate.status.update.success=Template status successfully updated +pageTemplate.status.update.error=An unknown error occurred while updating template status. Please contact the administrator or try agan later. diff --git a/layout-webapp/src/main/webapp/vue-app/common-layout-components/js/PageTemplateService.js b/layout-webapp/src/main/webapp/vue-app/common-layout-components/js/PageTemplateService.js index 427b30787..7e874ddf9 100644 --- a/layout-webapp/src/main/webapp/vue-app/common-layout-components/js/PageTemplateService.js +++ b/layout-webapp/src/main/webapp/vue-app/common-layout-components/js/PageTemplateService.js @@ -30,6 +30,19 @@ export function getPageTemplates() { }); } +export function getPageTemplate(id) { + return fetch(`/layout/rest/pageTemplates/${id}`, { + method: 'GET', + credentials: 'include', + }).then(resp => { + if (!resp?.ok) { + throw new Error('Error when retrieving page template'); + } else { + return resp.json(); + } + }); +} + export function createPageTemplate(pageContent) { return fetch('/layout/rest/pageTemplates', { credentials: 'include', @@ -49,16 +62,14 @@ export function createPageTemplate(pageContent) { }); } -export function updatePageTemplate(pageContent, id) { - return fetch(`/layout/rest/pageTemplates/${id}`, { +export function updatePageTemplate(pageTemplate) { + return fetch(`/layout/rest/pageTemplates/${pageTemplate.id}`, { credentials: 'include', method: 'PUT', headers: { 'Content-Type': 'application/json', }, - body: JSON.stringify({ - content: JSON.stringify(pageContent), - }), + body: JSON.stringify(pageTemplate), }).then((resp) => { if (!resp?.ok) { throw new Error('Error when creating page template'); 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 1d6fbb502..3c7436120 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 @@ -152,7 +152,12 @@ export default { save() { const pageLayout = this.$layoutUtils.cleanAttributes(this.$root.layout, true, true); const savePageRequest = - this.templateId ? this.$pageTemplateService.updatePageTemplate(pageLayout, this.templateId) + this.templateId ? + this.$pageTemplateService.getPageTemplate(this.templateId) + .then(pageTemplate => this.$pageTemplateService.updatePageTemplate({ + ...pageTemplate, + content: JSON.stringify(pageLayout), + })) : this.$pageTemplateService.createPageTemplate(pageLayout); return savePageRequest .then(pageTemplate => { diff --git a/layout-webapp/src/main/webapp/vue-app/page-templates-management/components/list/PageTemplateItem.vue b/layout-webapp/src/main/webapp/vue-app/page-templates-management/components/list/PageTemplateItem.vue index c35aab3c3..c8f1f9916 100644 --- a/layout-webapp/src/main/webapp/vue-app/page-templates-management/components/list/PageTemplateItem.vue +++ b/layout-webapp/src/main/webapp/vue-app/page-templates-management/components/list/PageTemplateItem.vue @@ -24,10 +24,21 @@ {{ category }} + + + \ No newline at end of file diff --git a/layout-webapp/src/main/webapp/vue-app/page-templates-management/components/list/PageTemplates.vue b/layout-webapp/src/main/webapp/vue-app/page-templates-management/components/list/PageTemplates.vue index fb1e8432a..74743df7e 100644 --- a/layout-webapp/src/main/webapp/vue-app/page-templates-management/components/list/PageTemplates.vue +++ b/layout-webapp/src/main/webapp/vue-app/page-templates-management/components/list/PageTemplates.vue @@ -57,8 +57,16 @@ export default { { text: this.$t('pageTemplates.label.category'), value: 'category', - align: 'left', - sortable: false, + align: 'center', + sortable: true, + class: 'page-template-category-header', + width: '120px' + }, + { + text: this.$t('pageTemplates.label.status'), + value: 'disabled', + align: 'center', + sortable: true, class: 'page-template-category-header', width: '120px' }, @@ -74,8 +82,12 @@ export default { }, }, created() { + this.$root.$on('page-templates-refresh', this.refreshPageTemplates); this.refreshPageTemplates(); }, + beforeDestroy() { + this.$root.$off('page-templates-refresh', this.refreshPageTemplates); + }, methods: { refreshPageTemplates() { this.loading = true; From 0edee38892efde8ea3a14540b0d55df50ce4775c Mon Sep 17 00:00:00 2001 From: Boubaker Khanfir Date: Fri, 17 May 2024 17:50:53 +0100 Subject: [PATCH 05/15] feat: Allow to delete a page template - MEED-6846 - Meeds-io/MIPs#133 --- .../layout/entity/PageTemplateEntity.java | 3 + .../io/meeds/layout/model/PageTemplate.java | 9 +- .../layout/model/PageTemplateDescriptor.java | 2 + .../service/PageTemplateImportService.java | 1 + .../layout/service/PageTemplateService.java | 7 ++ .../layout/storage/PageTemplateStorage.java | 6 + .../layout-rdbms.db.changelog-1.0.0.xml | 11 ++ .../service/PageTemplateServiceTest.java | 13 ++- .../locale/portlet/LayoutEditor_en.properties | 10 ++ .../js/PageTemplateService.js | 11 ++ .../components/container/DynamicSection.vue | 2 +- .../components/PageTemplatesManagement.vue | 2 +- .../components/header/Toolbar.vue | 1 + .../components/list/PageTemplateItem.vue | 26 ++++- .../components/list/PageTemplateItemMenu.vue | 103 ++++++++++++++++++ .../components/list/PageTemplates.vue | 72 +++++++++--- .../initComponents.js | 2 + 17 files changed, 257 insertions(+), 24 deletions(-) create mode 100644 layout-webapp/src/main/webapp/vue-app/page-templates-management/components/list/PageTemplateItemMenu.vue diff --git a/layout-service/src/main/java/io/meeds/layout/entity/PageTemplateEntity.java b/layout-service/src/main/java/io/meeds/layout/entity/PageTemplateEntity.java index 450eb7bb0..e5c9caefb 100644 --- a/layout-service/src/main/java/io/meeds/layout/entity/PageTemplateEntity.java +++ b/layout-service/src/main/java/io/meeds/layout/entity/PageTemplateEntity.java @@ -45,6 +45,9 @@ public class PageTemplateEntity { @Column(name = "DISABLED") private boolean disabled; + @Column(name = "SYSTEM_PAGE_TEMPLATE") + private boolean system; + @Column(name = "CATEGORY") private String category; diff --git a/layout-service/src/main/java/io/meeds/layout/model/PageTemplate.java b/layout-service/src/main/java/io/meeds/layout/model/PageTemplate.java index dc2fe3c13..5e998f93a 100644 --- a/layout-service/src/main/java/io/meeds/layout/model/PageTemplate.java +++ b/layout-service/src/main/java/io/meeds/layout/model/PageTemplate.java @@ -31,6 +31,8 @@ public class PageTemplate { private boolean disabled; + private boolean system; + private String category; private String content; @@ -41,10 +43,15 @@ public class PageTemplate { private long illustrationId; - public PageTemplate(long id, boolean disabled, String category, String content) { + public PageTemplate(long id, + boolean disabled, + boolean system, + String category, + String content) { this.id = id; this.content = content; this.disabled = disabled; + this.system = system; this.category = category; } diff --git a/layout-service/src/main/java/io/meeds/layout/model/PageTemplateDescriptor.java b/layout-service/src/main/java/io/meeds/layout/model/PageTemplateDescriptor.java index 5ce03c077..eaef9ca9c 100644 --- a/layout-service/src/main/java/io/meeds/layout/model/PageTemplateDescriptor.java +++ b/layout-service/src/main/java/io/meeds/layout/model/PageTemplateDescriptor.java @@ -39,6 +39,8 @@ public class PageTemplateDescriptor { private String category; + private boolean system; + private String illustrationPath; private String layoutPath; diff --git a/layout-service/src/main/java/io/meeds/layout/service/PageTemplateImportService.java b/layout-service/src/main/java/io/meeds/layout/service/PageTemplateImportService.java index 14569be5f..55eaeaf8c 100644 --- a/layout-service/src/main/java/io/meeds/layout/service/PageTemplateImportService.java +++ b/layout-service/src/main/java/io/meeds/layout/service/PageTemplateImportService.java @@ -238,6 +238,7 @@ protected PageTemplate createPageTemplate(PageTemplateDescriptor d, long oldTemp isNew = true; } pageTemplate.setCategory(d.getCategory()); + pageTemplate.setSystem(d.isSystem()); try (InputStream is = configurationManager.getInputStream(d.getLayoutPath())) { String xml = IOUtil.getStreamContentAsString(is); Container layout = fromXML(xml); diff --git a/layout-service/src/main/java/io/meeds/layout/service/PageTemplateService.java b/layout-service/src/main/java/io/meeds/layout/service/PageTemplateService.java index 7cf962c58..782aa19dd 100644 --- a/layout-service/src/main/java/io/meeds/layout/service/PageTemplateService.java +++ b/layout-service/src/main/java/io/meeds/layout/service/PageTemplateService.java @@ -102,6 +102,13 @@ public void deletePageTemplate(long templateId, String username) throws IllegalA if (!layoutAclService.isAdministrator(username)) { throw new IllegalAccessException("User isn't authorized to create a page template"); } + PageTemplate pageTemplate = getPageTemplate(templateId); + if (pageTemplate == null) { + throw new ObjectNotFoundException("Page template doesn't exist"); + } + if (pageTemplate.isSystem()) { + throw new IllegalAccessException("Can't delete a system page template"); + } deletePageTemplate(templateId); } diff --git a/layout-service/src/main/java/io/meeds/layout/storage/PageTemplateStorage.java b/layout-service/src/main/java/io/meeds/layout/storage/PageTemplateStorage.java index 4f80a7906..58d8a8755 100644 --- a/layout-service/src/main/java/io/meeds/layout/storage/PageTemplateStorage.java +++ b/layout-service/src/main/java/io/meeds/layout/storage/PageTemplateStorage.java @@ -40,6 +40,7 @@ public List getPageTemplates() { return entities.stream() .map(e -> new PageTemplate(e.getId(), e.isDisabled(), + e.isSystem(), e.getCategory(), e.getContent())) .toList(); @@ -49,6 +50,7 @@ public PageTemplate getPageTemplate(long id) { return pageTemplateDAO.findById(id) .map(e -> new PageTemplate(e.getId(), e.isDisabled(), + e.isSystem(), e.getCategory(), e.getContent())) .orElse(null); @@ -57,11 +59,13 @@ public PageTemplate getPageTemplate(long id) { public PageTemplate createPageTemplate(PageTemplate pageTemplate) { PageTemplateEntity entity = new PageTemplateEntity(null, pageTemplate.isDisabled(), + pageTemplate.isSystem(), pageTemplate.getCategory(), pageTemplate.getContent()); entity = pageTemplateDAO.save(entity); return new PageTemplate(entity.getId(), entity.isDisabled(), + entity.isSystem(), entity.getCategory(), entity.getContent()); } @@ -72,11 +76,13 @@ public PageTemplate updatePageTemplate(PageTemplate pageTemplate) throws ObjectN } PageTemplateEntity entity = new PageTemplateEntity(pageTemplate.getId(), pageTemplate.isDisabled(), + pageTemplate.isSystem(), pageTemplate.getCategory(), pageTemplate.getContent()); entity = pageTemplateDAO.save(entity); return new PageTemplate(entity.getId(), entity.isDisabled(), + entity.isSystem(), entity.getCategory(), entity.getContent()); } diff --git a/layout-service/src/main/resources/changelog/layout-rdbms.db.changelog-1.0.0.xml b/layout-service/src/main/resources/changelog/layout-rdbms.db.changelog-1.0.0.xml index 927ef9a56..771a4c1e7 100644 --- a/layout-service/src/main/resources/changelog/layout-rdbms.db.changelog-1.0.0.xml +++ b/layout-service/src/main/resources/changelog/layout-rdbms.db.changelog-1.0.0.xml @@ -79,4 +79,15 @@ + + + + + + \ No newline at end of file diff --git a/layout-service/src/test/java/io/meeds/layout/service/PageTemplateServiceTest.java b/layout-service/src/test/java/io/meeds/layout/service/PageTemplateServiceTest.java index dc4604c00..2486b4362 100644 --- a/layout-service/src/test/java/io/meeds/layout/service/PageTemplateServiceTest.java +++ b/layout-service/src/test/java/io/meeds/layout/service/PageTemplateServiceTest.java @@ -111,7 +111,7 @@ public void getPageTemplates() { @Test public void getPageTemplatesWithExpand() throws ObjectNotFoundException { - PageTemplate template = new PageTemplate(2l, false, LAYOUT_CATEGORY, LAYOUT_CONTENT); + PageTemplate template = new PageTemplate(2l, false, false, LAYOUT_CATEGORY, LAYOUT_CONTENT); when(localeConfigService.getDefaultLocaleConfig()).thenReturn(defaultLocaleConfig); when(defaultLocaleConfig.getLocale()).thenReturn(Locale.ENGLISH); @@ -178,7 +178,7 @@ public void getPageTemplatesWithExpand() throws ObjectNotFoundException { @Test public void getPageTemplateWithExpand() throws ObjectNotFoundException { - PageTemplate template = new PageTemplate(2l, false, LAYOUT_CATEGORY, LAYOUT_CONTENT); + PageTemplate template = new PageTemplate(2l, false, false, LAYOUT_CATEGORY, LAYOUT_CONTENT); when(localeConfigService.getDefaultLocaleConfig()).thenReturn(defaultLocaleConfig); when(defaultLocaleConfig.getLocale()).thenReturn(Locale.ENGLISH); @@ -261,7 +261,15 @@ public void deletePageTemplate() throws ObjectNotFoundException, IllegalAccessEx assertThrows(IllegalAccessException.class, () -> pageTemplateService.deletePageTemplate(2l, testuser)); when(layoutAclService.isAdministrator(testuser)).thenReturn(true); + assertThrows(ObjectNotFoundException.class, () -> pageTemplateService.deletePageTemplate(2l, testuser)); + + when(pageTemplateStorage.getPageTemplate(2l)).thenReturn(pageTemplate); + when(pageTemplate.isSystem()).thenReturn(true); + assertThrows(IllegalAccessException.class, () -> pageTemplateService.deletePageTemplate(2l, testuser)); + + when(pageTemplate.isSystem()).thenReturn(false); pageTemplateService.deletePageTemplate(2l, testuser); + verify(attachmentService, times(1)).deleteAttachments(PageTemplateAttachmentPlugin.OBJECT_TYPE, "2"); verify(translationService, times(1)).deleteTranslationLabels(PageTemplateTranslationPlugin.OBJECT_TYPE, 2l); verify(pageTemplateStorage, times(1)).deletePageTemplate(2l); @@ -272,6 +280,7 @@ public void deletePageTemplateWhenException() throws ObjectNotFoundException, Il assertThrows(IllegalAccessException.class, () -> pageTemplateService.deletePageTemplate(2l, testuser)); when(layoutAclService.isAdministrator(testuser)).thenReturn(true); + when(pageTemplateStorage.getPageTemplate(2l)).thenReturn(pageTemplate); doThrow(RuntimeException.class).when(attachmentService).deleteAttachments(anyString(), any()); doThrow(ObjectNotFoundException.class).when(translationService).deleteTranslationLabels(anyString(), anyLong()); pageTemplateService.deletePageTemplate(2l, testuser); 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 c939f69bb..302a33ea9 100644 --- a/layout-webapp/src/main/resources/locale/portlet/LayoutEditor_en.properties +++ b/layout-webapp/src/main/resources/locale/portlet/LayoutEditor_en.properties @@ -119,6 +119,16 @@ pageTemplates.label.name=Name pageTemplates.label.description=Description pageTemplates.label.category=Category pageTemplates.label.status=Status +pageTemplates.label.actions=Actions +pageTemplates.menu.open=Open Menu pageTemplate.label.preview=Preview of {0} template pageTemplate.status.update.success=Template status successfully updated pageTemplate.status.update.error=An unknown error occurred while updating template status. Please contact the administrator or try agan later. +pageTemplate.status.delete.success=Template status successfully deleted +pageTemplate.status.delete.error=An unknown error occurred while deleting template status. Please contact the administrator or try agan later. +pageTemplate.label.delete=Delete +pageTemplate.label.system.noDelete=This product template cannot be deleted +pageTemplate.label.confirmDeleteTitle=Delete page template? +pageTemplate.label.confirmDeleteMessage=Would you like to delete page template: {0} +pageTemplate.label.confirm=Confirm +pageTemplate.label.cancel=Cancel diff --git a/layout-webapp/src/main/webapp/vue-app/common-layout-components/js/PageTemplateService.js b/layout-webapp/src/main/webapp/vue-app/common-layout-components/js/PageTemplateService.js index 7e874ddf9..460b2b9a4 100644 --- a/layout-webapp/src/main/webapp/vue-app/common-layout-components/js/PageTemplateService.js +++ b/layout-webapp/src/main/webapp/vue-app/common-layout-components/js/PageTemplateService.js @@ -76,3 +76,14 @@ export function updatePageTemplate(pageTemplate) { } }); } + +export function deletePageTemplate(id) { + return fetch(`/layout/rest/pageTemplates/${id}`, { + credentials: 'include', + method: 'DELETE', + }).then((resp) => { + if (!resp?.ok) { + throw new Error('Error when creating page template'); + } + }); +} diff --git a/layout-webapp/src/main/webapp/vue-app/page-layout/components/container/DynamicSection.vue b/layout-webapp/src/main/webapp/vue-app/page-layout/components/container/DynamicSection.vue index fbf5ca1c5..388448791 100644 --- a/layout-webapp/src/main/webapp/vue-app/page-layout/components/container/DynamicSection.vue +++ b/layout-webapp/src/main/webapp/vue-app/page-layout/components/container/DynamicSection.vue @@ -19,7 +19,7 @@ -->