diff --git a/data-upgrade-navigations/src/main/java/org/exoplatform/migration/PortalNavigationIconMigration.java b/data-upgrade-navigations/src/main/java/org/exoplatform/migration/PortalNavigationIconMigration.java new file mode 100644 index 000000000..573547d8b --- /dev/null +++ b/data-upgrade-navigations/src/main/java/org/exoplatform/migration/PortalNavigationIconMigration.java @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2024 eXo Platform SAS. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.exoplatform.migration; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +import org.exoplatform.commons.api.persistence.ExoTransactional; +import org.exoplatform.commons.persistence.impl.EntityManagerService; +import org.exoplatform.commons.upgrade.UpgradeProductPlugin; +import org.exoplatform.container.xml.InitParams; +import org.exoplatform.services.log.ExoLogger; +import org.exoplatform.services.log.Log; + +import jakarta.persistence.EntityManager; +import jakarta.persistence.Query; + +/** + * This plugin will be executed in order to set icons for the portal navigation + * nodes as provided in the configuration + */ +public class PortalNavigationIconMigration extends UpgradeProductPlugin { + + private static final Log LOG = ExoLogger.getExoLogger(PortalNavigationIconMigration.class); + + private static final String ICON_UPDATE_SQL = + """ + UPDATE PORTAL_NAVIGATION_NODES + SET ICON = + CASE + %s + END + WHERE ICON IS NULL + AND EXISTS (SELECT * FROM PORTAL_PAGES p INNER JOIN PORTAL_SITES s ON s.ID = p.SITE_ID WHERE PAGE_ID = p.ID AND s.TYPE = 0 AND s.NAME LIKE 'dw') + """; + + private static final String ICON_UPDATE_CASE_SQL = """ + WHEN NAME in (%s) THEN TRIM('%s') + """; + + private static final String PORTAL_NODE_NAMES = "portal.node.names"; + + private static final String PORTAL_NODE_ICONS = "portal.node.icons"; + + private final EntityManagerService entityManagerService; + + private final Map portalNodes = new HashMap<>(); + + private int migratedPortalNodeIcons; + + public PortalNavigationIconMigration(EntityManagerService entityManagerService, InitParams initParams) { + super(initParams); + this.entityManagerService = entityManagerService; + if (initParams.containsKey(PORTAL_NODE_ICONS) && initParams.containsKey(PORTAL_NODE_NAMES)) { + String[] portalNodeNames = initParams.getValueParam(PORTAL_NODE_NAMES).getValue().split(";"); + String[] portalNodeIcons = initParams.getValueParam(PORTAL_NODE_ICONS).getValue().split(";"); + if (portalNodeIcons.length == portalNodeNames.length) { + for (int i = 0; i < portalNodeNames.length; i++) { + this.portalNodes.put(portalNodeNames[i], portalNodeIcons[i]); + } + } + } + } + + @Override + public boolean shouldProceedToUpgrade(String newVersion, String previousVersion) { + return !portalNodes.isEmpty(); + } + + @Override + public void processUpgrade(String oldVersion, String newVersion) { + + long startupTime = System.currentTimeMillis(); + + LOG.info("Start:: Upgrade of portal navigation node icons"); + Set> portalNodesEntrySet = portalNodes.entrySet(); + this.migratedPortalNodeIcons = upgradePortalNodeIcons(portalNodesEntrySet); + LOG.info("End:: Upgrade of '{}' node icons. It tooks {} ms", + migratedPortalNodeIcons, + (System.currentTimeMillis() - startupTime)); + } + + @ExoTransactional + public int upgradePortalNodeIcons(Set> portalNodesEntrySet) { + EntityManager entityManager = entityManagerService.getEntityManager(); + + String sqlStatement = String.format(ICON_UPDATE_SQL, portalNodes.entrySet().stream().map(e -> { + String keys = Arrays.stream(e.getKey().split(",")).map(key -> String.format("'%s'", key)).collect(Collectors.joining(",")); + return String.format(ICON_UPDATE_CASE_SQL, keys, e.getValue()); + }).collect(Collectors.joining())); + Query query = entityManager.createNativeQuery(sqlStatement); + return query.executeUpdate(); + } + + public int getMigratedPortalNodeIconsNodeIcons() { + return migratedPortalNodeIcons; + } +} diff --git a/data-upgrade-navigations/src/main/resources/conf/portal/configuration.xml b/data-upgrade-navigations/src/main/resources/conf/portal/configuration.xml index d78753906..1fc6479c1 100644 --- a/data-upgrade-navigations/src/main/resources/conf/portal/configuration.xml +++ b/data-upgrade-navigations/src/main/resources/conf/portal/configuration.xml @@ -195,5 +195,48 @@ + + PortalNavigationIconUpgradePlugin + addUpgradePlugin + org.exoplatform.migration.PortalNavigationIconMigration + Configure portal node icons + + + product.group.id + The groupId of the product + org.exoplatform.platform + + + plugin.upgrade.target.version + The plugin target version (will not be executed if previous version is equal or higher than 6.6.0) + 6.6.0 + + + portal.node.names + The plugin will set the icons of these portal nodes names + external-stream + + + portal.node.icons + The plugin will set the portal nodes icons with these icons + fas fa-user-lock + + + plugin.execution.order + The plugin execution order + 101 + + + plugin.upgrade.execute.once + The plugin must be executed only once + true + + + plugin.upgrade.async.execution + The plugin will be executed in an asynchronous mode + true + + + diff --git a/data-upgrade-navigations/src/test/java/org/exoplatform/migration/PortalNavigationIconMigrationTest.java b/data-upgrade-navigations/src/test/java/org/exoplatform/migration/PortalNavigationIconMigrationTest.java new file mode 100644 index 000000000..c6d266086 --- /dev/null +++ b/data-upgrade-navigations/src/test/java/org/exoplatform/migration/PortalNavigationIconMigrationTest.java @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2024 eXo Platform SAS. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.exoplatform.migration; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import org.exoplatform.commons.persistence.impl.EntityManagerService; +import org.exoplatform.component.test.AbstractKernelTest; +import org.exoplatform.component.test.ConfigurationUnit; +import org.exoplatform.component.test.ConfiguredBy; +import org.exoplatform.component.test.ContainerScope; +import org.exoplatform.container.PortalContainer; +import org.exoplatform.container.component.RequestLifeCycle; +import org.exoplatform.container.xml.InitParams; +import org.exoplatform.container.xml.ValueParam; +import org.exoplatform.portal.jdbc.entity.NodeEntity; +import org.exoplatform.portal.jdbc.entity.PageEntity; +import org.exoplatform.portal.jdbc.entity.SiteEntity; +import org.exoplatform.portal.mop.NodeTarget; +import org.exoplatform.portal.mop.PageType; +import org.exoplatform.portal.mop.SiteType; +import org.exoplatform.portal.mop.dao.NodeDAO; +import org.exoplatform.portal.mop.dao.PageDAO; +import org.exoplatform.portal.mop.dao.SiteDAO; + +@ConfiguredBy({ @ConfigurationUnit(scope = ContainerScope.PORTAL, path = "conf/portal/configuration.xml"), + @ConfigurationUnit(scope = ContainerScope.PORTAL, path = "conf/exo.portal.component.portal-configuration-local.xml"), + @ConfigurationUnit(scope = ContainerScope.PORTAL, path = "org/exoplatform/portal/config/conf/configuration.xml") }) +public class PortalNavigationIconMigrationTest extends AbstractKernelTest { + InitParams initParams = new InitParams(); + private PortalContainer container; + private EntityManagerService entityManagerService; + private PortalNavigationIconMigration portalNavigationIconMigration; + private SiteDAO siteDAO; + private PageDAO pageDAO; + private NodeDAO nodeDAO; + + @Before + public void setUp() { + container = getContainer(); + siteDAO = container.getComponentInstanceOfType(SiteDAO.class); + pageDAO = container.getComponentInstanceOfType(PageDAO.class); + nodeDAO = container.getComponentInstanceOfType(NodeDAO.class); + entityManagerService = container.getComponentInstanceOfType(EntityManagerService.class); + begin(); + ValueParam productGroupIdValueParam = new ValueParam(); + productGroupIdValueParam.setName("product.group.id"); + productGroupIdValueParam.setValue("org.exoplatform.platform"); + ValueParam portalNodeNamesValueParam = new ValueParam(); + portalNodeNamesValueParam.setName("portal.node.names"); + portalNodeNamesValueParam.setValue("external-stream"); + ValueParam portalNodeIconsValueParam = new ValueParam(); + portalNodeIconsValueParam.setName("portal.node.icons"); + portalNodeIconsValueParam.setValue("fas fa-user-lock"); + initParams.addParameter(productGroupIdValueParam); + initParams.addParameter(portalNodeNamesValueParam); + initParams.addParameter(portalNodeIconsValueParam); + this.portalNavigationIconMigration = new PortalNavigationIconMigration(entityManagerService, initParams); + } + + @After + public void tearDown() throws Exception { + end(); + } + + @Test + public void testPortalNavigationIconMigration() { + + SiteEntity siteEntity = new SiteEntity(); + siteEntity.setName("dw"); + siteEntity.setSiteType(SiteType.PORTAL); + siteDAO.create(siteEntity); + siteEntity = siteDAO.findByType(SiteType.PORTAL).stream().filter(e -> e.getName().equals("dw")).toList().get(0); + // + assertNotNull(siteEntity); + PageEntity pageEntity = new PageEntity(); + pageEntity.setName("stream"); + pageEntity.setOwner(siteEntity); + pageEntity.setPageType(PageType.PAGE); + pageDAO.create(pageEntity); + pageEntity = pageDAO.findAll().stream().filter(e -> e.getName().equals("stream")).toList().get(0); + // + assertNotNull(pageEntity); + NodeEntity nodeEntity = new NodeEntity(); + nodeEntity.setName("external-stream"); + nodeEntity.setIcon(null); + nodeEntity.setPage(pageEntity); + nodeEntity.setTarget(NodeTarget.NEW_TAB); + nodeDAO.create(nodeEntity); + nodeEntity = nodeDAO.findAllByPage(pageEntity.getId()).stream().filter(e -> e.getName().equals("external-stream")).toList().get(0); + // + assertNotNull(nodeEntity); + restartTransaction(); + portalNavigationIconMigration.processUpgrade(null, null); + // + assertEquals(1, portalNavigationIconMigration.getMigratedPortalNodeIconsNodeIcons()); + nodeEntity = nodeDAO.find(nodeEntity.getId()); + assertNotNull(nodeEntity); + assertNotNull(nodeEntity.getIcon()); + assertEquals("fas fa-user-lock", nodeEntity.getIcon()); + } + +}