diff --git a/data-upgrade-move-folders/pom.xml b/data-upgrade-move-folders/pom.xml new file mode 100644 index 000000000..be82a8f93 --- /dev/null +++ b/data-upgrade-move-folders/pom.xml @@ -0,0 +1,72 @@ + + + + 4.0.0 + + org.exoplatform.addons.upgrade + upgrade + 6.4.x-SNAPSHOT + + + data-upgrade-move-folders + jar + eXo Add-on:: Data Upgrade Add-on - Move Folders + + + 0.66 + + + + + org.exoplatform.commons + commons-component-upgrade + provided + + + org.exoplatform.jcr + exo.jcr.component.core + provided + + + org.exoplatform.jcr + exo.jcr.component.ext + provided + + + org.exoplatform.ecms + ecms-core-services + provided + + + junit + junit + test + + + org.mockito + mockito-core + test + + + org.powermock + powermock-api-mockito2 + test + + + org.powermock + powermock-module-junit4 + test + + + diff --git a/data-upgrade-move-folders/src/main/java/org/exoplatform/jcr/upgrade/MoveNodesUpgradePlugin.java b/data-upgrade-move-folders/src/main/java/org/exoplatform/jcr/upgrade/MoveNodesUpgradePlugin.java new file mode 100644 index 000000000..5f800d018 --- /dev/null +++ b/data-upgrade-move-folders/src/main/java/org/exoplatform/jcr/upgrade/MoveNodesUpgradePlugin.java @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2003-2023 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 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.jcr.upgrade; + +import javax.jcr.Item; +import javax.jcr.RepositoryException; +import javax.jcr.Session; + +import org.apache.commons.lang3.StringUtils; +import org.exoplatform.commons.upgrade.UpgradeProductPlugin; +import org.exoplatform.commons.utils.ListAccess; +import org.exoplatform.container.PortalContainer; +import org.exoplatform.container.component.RequestLifeCycle; +import org.exoplatform.container.xml.InitParams; +import org.exoplatform.services.jcr.RepositoryService; +import org.exoplatform.services.jcr.ext.app.SessionProviderService; +import org.exoplatform.services.jcr.ext.common.SessionProvider; +import org.exoplatform.services.log.ExoLogger; +import org.exoplatform.services.log.Log; +import org.exoplatform.social.core.space.model.Space; +import org.exoplatform.social.core.space.spi.SpaceService; + +import java.util.ArrayList; +import java.util.List; + +/** + * plugin will be executed in order to move folders under spaces drives + * from an original path to a destination path as provided in the configuration + */ +public class MoveNodesUpgradePlugin extends UpgradeProductPlugin { + + private static final Log log = ExoLogger.getLogger(MoveNodesUpgradePlugin.class.getName()); + + private static final String ORIGIN_PATH = "origin-folder-path"; + + private static final String DESTINATION_PATH = "destination-folder-path"; + private static final String FOLDERS_TO_REMOVE = "folders-to-remove"; + + private static final int SPACES_PAGE_SIZE = 2; + + private final SpaceService spaceService; + + private RepositoryService repositoryService; + + private SessionProviderService sessionProviderService; + + private String originPath; + + private String destinationPath; + + private List foldersToRemove = new ArrayList<>(); + + + public MoveNodesUpgradePlugin(InitParams initParams, + SpaceService spaceService, + RepositoryService repositoryService, + SessionProviderService sessionProviderService) { + super(initParams); + if(initParams.getValueParam(ORIGIN_PATH) != null && StringUtils.isNotBlank(initParams.getValueParam(ORIGIN_PATH).getValue())) { + this.originPath = initParams.getValueParam(ORIGIN_PATH).getValue(); + } + if(initParams.getValueParam(ORIGIN_PATH) != null && StringUtils.isNotBlank(initParams.getValueParam(ORIGIN_PATH).getValue())) { + this.destinationPath = initParams.getValueParam(DESTINATION_PATH).getValue(); + } + if(initParams.getValuesParam(FOLDERS_TO_REMOVE) != null && !initParams.getValuesParam(FOLDERS_TO_REMOVE).getValues().isEmpty()) { + this.foldersToRemove = initParams.getValuesParam(FOLDERS_TO_REMOVE).getValues(); + } + + this.spaceService = spaceService; + this.repositoryService = repositoryService; + this.sessionProviderService = sessionProviderService; + } + + @Override + public void processUpgrade(String oldVersion, String newVersion) { + if(StringUtils.isBlank(originPath) || StringUtils.isBlank(destinationPath)) { + log.warn("Invalid parameter was provided for {}, this upgrade plugin will be ignored", StringUtils.isBlank(originPath) ? "'Origin path'":"'Destination path'"); + return; + } + long startupTime = System.currentTimeMillis(); + int movedFoldersCount = 0; + log.info("Start upgrade : Moving of folder from {} to {}", originPath, destinationPath); + + SessionProvider sessionProvider = null; + RequestLifeCycle.begin(PortalContainer.getInstance()); + try { + sessionProvider = sessionProviderService.getSystemSessionProvider(null); + Session session = sessionProvider.getSession( + repositoryService.getCurrentRepository() + .getConfiguration() + .getDefaultWorkspaceName(), + repositoryService.getCurrentRepository()); + ListAccess spaces = spaceService.getAllSpacesWithListAccess(); + int index = 0; + while(index <= spaces.getSize()) { + Space[] spaceArray = spaces.load(index, SPACES_PAGE_SIZE); + for (Space space : spaceArray) { + String originFolderPath = "/Groups" + space.getGroupId() + originPath; + String destinationFolderPath = "/Groups" + space.getGroupId() + destinationPath; + try { + Item originFolderNode = session.getItem(originFolderPath); + if (originFolderNode != null) { + session.move(originFolderPath, destinationFolderPath); + movedFoldersCount++; + } + } catch(RepositoryException e) { + if (log.isDebugEnabled()) { + log.warn("Folder {} to move was not found, ignoring it", originFolderPath, e); + } else { + log.warn("Folder {} to move was not found, ignoring it", originFolderPath); + } + } + // remove unnecessary folders if defined in init params + if(!foldersToRemove.isEmpty()) { + for(String folderToRemove : foldersToRemove) { + folderToRemove = "/Groups" + space.getGroupId() + folderToRemove; + try { + Item folderToRemoveNode = session.getItem(folderToRemove); + if (folderToRemoveNode != null) { + folderToRemoveNode.remove(); + } + } catch (RepositoryException re) { + if(log.isDebugEnabled()) { + log.warn("Folder {} to delete was not found, ignoring it", folderToRemove, re); + } else { + log.warn("Folder {} to delete was not found, ignoring it", folderToRemove); + } + } + } + } + } + + session.save(); + index = index + SPACES_PAGE_SIZE; + } + + log.info("End Moving of '{}' folders. It took {} ms", + movedFoldersCount, + (System.currentTimeMillis() - startupTime)); + } catch (Exception e) { + if (log.isErrorEnabled()) { + log.error("An unexpected error occurs when moving folders:", e); + } + } finally { + if (sessionProvider != null) { + sessionProvider.close(); + } + RequestLifeCycle.end(); + } + } + +} diff --git a/data-upgrade-move-folders/src/main/resources/conf/portal/configuration.xml b/data-upgrade-move-folders/src/main/resources/conf/portal/configuration.xml new file mode 100644 index 000000000..b36de3b8a --- /dev/null +++ b/data-upgrade-move-folders/src/main/resources/conf/portal/configuration.xml @@ -0,0 +1,125 @@ + + + + + + + + org.exoplatform.commons.upgrade.UpgradeProductService + + MoveNewsFolderToSpaceRoot + addUpgradePlugin + org.exoplatform.jcr.upgrade.MoveNodesUpgradePlugin + Move images attached to News to a new location + + + product.group.id + The groupId of the product + org.exoplatform.news + + + plugin.execution.order + The plugin execution order + 1 + + + plugin.upgrade.execute.once + Execute this upgrade plugin only once + true + + + plugin.upgrade.async.execution + Execute this upgrade asynchronously + true + + + plugin.upgrade.target.version + Target version of the plugin + 6.4.4 + + + + origin-folder-path + Origin folder path + /Documents/news/images + + + destination-folder-path + Destination parent folder path + /News/images + + + folders-to-remove + /Documents/news + + + + + MoveNotesFolderToSpaceRoot + addUpgradePlugin + org.exoplatform.jcr.upgrade.MoveNodesUpgradePlugin + Move images attached to notes to a new location + + + product.group.id + The groupId of the product + org.meeds-io.notes + + + plugin.execution.order + The plugin execution order + 1 + + + plugin.upgrade.execute.once + Execute this upgrade plugin only once + true + + + plugin.upgrade.async.execution + Execute this upgrade asynchronously + true + + + plugin.upgrade.target.version + Target version of the plugin + 6.4.4 + + + + origin-folder-path + Origin folder path + /Documents/notes + + + destination-folder-path + Destination parent folder path + /notes + + + + + + + diff --git a/data-upgrade-move-folders/src/test/java/org/exoplatform/jcr/upgrade/MoveNodesUpgradePluginTest.java b/data-upgrade-move-folders/src/test/java/org/exoplatform/jcr/upgrade/MoveNodesUpgradePluginTest.java new file mode 100644 index 000000000..20a4c541e --- /dev/null +++ b/data-upgrade-move-folders/src/test/java/org/exoplatform/jcr/upgrade/MoveNodesUpgradePluginTest.java @@ -0,0 +1,118 @@ +package org.exoplatform.jcr.upgrade; + +import org.exoplatform.commons.utils.ListAccess; +import org.exoplatform.container.ExoContainerContext; +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.services.cms.impl.Utils; +import org.exoplatform.services.jcr.RepositoryService; +import org.exoplatform.services.jcr.config.RepositoryEntry; +import org.exoplatform.services.jcr.core.ManageableRepository; +import org.exoplatform.services.jcr.ext.app.SessionProviderService; +import org.exoplatform.services.jcr.ext.common.SessionProvider; +import org.exoplatform.social.core.space.SpaceListAccess; +import org.exoplatform.social.core.space.model.Space; +import org.exoplatform.social.core.space.spi.SpaceService; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PowerMockIgnore; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; + +import javax.jcr.Item; +import javax.jcr.Session; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.*; + +@RunWith(PowerMockRunner.class) +@PrepareForTest({ExoContainerContext.class, PortalContainer.class, RequestLifeCycle.class}) +@PowerMockIgnore({ "javax.management.*", "jdk.internal.reflect.*", "javax.naming.*", "javax.xml.*", "org.apache.xerces.*", "org.xml.*", "com.sun.*", "org.w3c.*" }) +public class MoveNodesUpgradePluginTest { + @Mock + private RepositoryService repositoryService; + + @Mock + private SessionProviderService sessionProviderService; + + @Mock + private ManageableRepository repository; + + @Mock + private RepositoryEntry repositoryEntry; + + @Mock + private SessionProvider sessionProvider; + + @Mock + private Session session; + + @Mock + private SpaceService spaceService; + + @Before + public void setUp() throws Exception { + lenient().when(sessionProviderService.getSystemSessionProvider(any())).thenReturn(sessionProvider); + lenient().when(repositoryService.getCurrentRepository()).thenReturn(repository); + lenient().when(repository.getConfiguration()).thenReturn(repositoryEntry); + lenient().when(repositoryEntry.getDefaultWorkspaceName()).thenReturn("collaboration"); + Item node = mock(Item.class); + when(session.getItem(anyString())).thenReturn(node); + lenient().when(sessionProvider.getSession(any(), any(ManageableRepository.class))).thenReturn(session); + PowerMockito.mockStatic(ExoContainerContext.class); + PowerMockito.mockStatic(PortalContainer.class); + PowerMockito.mockStatic(RequestLifeCycle.class); + } + + @Test + public void testMoveFoldersUpgrade() throws Exception { + InitParams initParams = new InitParams(); + + ValueParam valueParam = new ValueParam(); + valueParam.setName("product.group.id"); + valueParam.setValue("org.exoplatform.platform"); + initParams.addParameter(valueParam); + + MoveNodesUpgradePlugin plugin = new MoveNodesUpgradePlugin(initParams, + spaceService, + repositoryService, + sessionProviderService); + plugin.processUpgrade(null,null); + + verify(session, never()).move(anyString(), anyString()); + + + ValueParam valueParam1 = new ValueParam(); + valueParam1.setName("origin-folder-path"); + valueParam1.setValue("/Documents/News"); + initParams.addParameter(valueParam1); + ValueParam valueParam2 = new ValueParam(); + valueParam2.setName("destination-folder-path"); + valueParam2.setValue("/"); + initParams.addParameter(valueParam2); + Space space1 = new Space(); + space1.setGroupId("/spaces/spaceOne"); + + Space space2 = new Space(); + space2.setGroupId("/spaces/spaceTw"); + + ListAccess spaces = mock(SpaceListAccess.class); + when(spaces.load(anyInt(), anyInt())).thenReturn(new Space[]{space1, space2}); + + when(spaceService.getAllSpacesWithListAccess()).thenReturn(spaces); + + plugin = new MoveNodesUpgradePlugin(initParams, + spaceService, + repositoryService, + sessionProviderService); + plugin.processUpgrade(null,null); + + verify(session, times(2)).move(anyString(), anyString()); + } +} diff --git a/data-upgrade-packaging/pom.xml b/data-upgrade-packaging/pom.xml index 88cde2645..b09d291d9 100644 --- a/data-upgrade-packaging/pom.xml +++ b/data-upgrade-packaging/pom.xml @@ -80,6 +80,10 @@ ${project.groupId} data-upgrade-es-reindex + + ${project.groupId} + data-upgrade-move-folders + diff --git a/pom.xml b/pom.xml index 55e124f7c..19d753248 100644 --- a/pom.xml +++ b/pom.xml @@ -32,6 +32,7 @@ data-upgrade-notifications data-upgrade-processes-permissions data-upgrade-es-reindex + data-upgrade-move-folders data-upgrade-packaging @@ -183,6 +184,11 @@ data-upgrade-es-reindex ${project.version} + + ${project.groupId} + data-upgrade-move-folders + ${project.version} + ${project.groupId} data-upgrade-packaging