From 11a47ce585908e98aabdf1af98b13369f83242c5 Mon Sep 17 00:00:00 2001 From: Victor Alfaro Date: Wed, 16 Oct 2024 13:37:12 -0600 Subject: [PATCH] #30296: Creation of upgrade task to replace languages portlets by locales ones. (#30340) --- .../Task240530AddDotAIPortletToLayout.java | 2 +- ...015ReplaceLanguagesWithLocalesPortlet.java | 100 +++++++++++++++ .../dotmarketing/util/TaskLocatorUtil.java | 7 +- .../src/test/java/com/dotcms/MainSuite2b.java | 4 +- ...eplaceLanguagesWithLocalesPortletTest.java | 114 ++++++++++++++++++ 5 files changed, 220 insertions(+), 7 deletions(-) create mode 100644 dotCMS/src/main/java/com/dotmarketing/startup/runonce/Task241015ReplaceLanguagesWithLocalesPortlet.java create mode 100644 dotcms-integration/src/test/java/com/dotmarketing/startup/runonce/Task241015ReplaceLanguagesWithLocalesPortletTest.java diff --git a/dotCMS/src/main/java/com/dotmarketing/startup/runonce/Task240530AddDotAIPortletToLayout.java b/dotCMS/src/main/java/com/dotmarketing/startup/runonce/Task240530AddDotAIPortletToLayout.java index 75001e4d9eea..30a2524761df 100644 --- a/dotCMS/src/main/java/com/dotmarketing/startup/runonce/Task240530AddDotAIPortletToLayout.java +++ b/dotCMS/src/main/java/com/dotmarketing/startup/runonce/Task240530AddDotAIPortletToLayout.java @@ -23,7 +23,7 @@ public class Task240530AddDotAIPortletToLayout implements StartupTask { @Override public boolean forceRun() { - // first we get all layouts we have maintenance portelt + // first we get all layouts we have maintenance portlet final List apiPlaygroundLayouts = getAPIPlaygroundLayouts(); // then check if all layouts which contains API Playground also have dotAI diff --git a/dotCMS/src/main/java/com/dotmarketing/startup/runonce/Task241015ReplaceLanguagesWithLocalesPortlet.java b/dotCMS/src/main/java/com/dotmarketing/startup/runonce/Task241015ReplaceLanguagesWithLocalesPortlet.java new file mode 100644 index 000000000000..69a0ba00eee6 --- /dev/null +++ b/dotCMS/src/main/java/com/dotmarketing/startup/runonce/Task241015ReplaceLanguagesWithLocalesPortlet.java @@ -0,0 +1,100 @@ +package com.dotmarketing.startup.runonce; + +import com.dotmarketing.business.CacheLocator; +import com.dotmarketing.common.db.DotConnect; +import com.dotmarketing.exception.DotDataException; +import com.dotmarketing.exception.DotRuntimeException; +import com.dotmarketing.startup.StartupTask; +import com.dotmarketing.util.UUIDUtil; +import io.vavr.control.Try; + +import java.util.List; +import java.util.Map; +import java.util.Optional; + +/** + * Adds the dotAI portlet to all layouts which have API Playground portlet too, if it does not already exist. + * @author vico + */ +public class Task241015ReplaceLanguagesWithLocalesPortlet implements StartupTask { + + public static final String LANGUAGES_PORTLET_ID = "languages"; + public static final String LOCALES_PORTLET_ID = "locales"; + public static final String WORKFLOW_PORTLET_ID = "workflow-schemes"; + + /** + * Determines if the task should be forced to run. + * + * @return true if no locales portlet is found in any layout, false otherwise. + */ + @Override + public boolean forceRun() { + // if no locales found then flag it as true + return getLayoutPortletsByPortletId(LOCALES_PORTLET_ID).isEmpty(); + } + + /** + * Executes the upgrade task. + * Adds the locales portlet to layouts containing the languages portlet or the workflow portlet. + * Replaces the languages portlet with the locales portlet. + * Clears the layout cache. + * + * @throws DotDataException if there is an error accessing the database. + * @throws DotRuntimeException if there is a runtime error during execution. + */ + @Override + public void executeUpgrade() throws DotDataException, DotRuntimeException { + final List> languagesLayoutPortlets = getLayoutPortletsByPortletId(LANGUAGES_PORTLET_ID); + + if (languagesLayoutPortlets.isEmpty()) { + final List> workflowLayoutPortlets = getLayoutPortletsByPortletId(WORKFLOW_PORTLET_ID); + if (!workflowLayoutPortlets.isEmpty()) { + final Map row = workflowLayoutPortlets.get(0); + final String layoutId = (String) row.get("layout_id"); + final int portletOrder = Optional.ofNullable((Integer) row.get("portlet_order")).orElse(0) + 1; + insertLocalesPortlet(layoutId, portletOrder); + } + } else { + languagesLayoutPortlets.forEach(this::replaceLanguage); + } + + CacheLocator.getLayoutCache().clearCache(); + } + + private void replaceLanguage(final Map row) { + final String layoutId = (String) row.get("layout_id"); + final int portletOrder = Optional.ofNullable((Integer) row.get("portlet_order")).orElse(1); + insertLocalesPortlet(layoutId, portletOrder); + + final String id = (String) row.get("id"); + deleteLanguagesPortlet(id); + } + + private void deleteLanguagesPortlet(final String id) { + Try.run(() -> new DotConnect() + .executeStatement(String.format("DELETE FROM cms_layouts_portlets WHERE id = '%s'", id))) + .getOrElseThrow(ex -> new RuntimeException(ex)); + } + + private static void insertLocalesPortlet(final String layoutId, final int portletOrder) { + Try.run(() -> new DotConnect() + .setSQL("INSERT INTO cms_layouts_portlets(id, layout_id, portlet_id, portlet_order)" + + " VALUES (?, ?, ?, ?)") + .addParam(UUIDUtil.uuid()) + .addParam(layoutId) + .addParam(LOCALES_PORTLET_ID) + .addParam(portletOrder) + .loadResult()) + .getOrElseThrow(ex -> new RuntimeException(ex)); + } + + private List> getLayoutPortletsByPortletId(final String portletId) { + return Try.of( + () -> new DotConnect() + .setSQL("SELECT * FROM cms_layouts_portlets WHERE portlet_id = ?") + .addParam(portletId) + .loadObjectResults()) + .getOrElse(List.of()); + } + +} diff --git a/dotCMS/src/main/java/com/dotmarketing/util/TaskLocatorUtil.java b/dotCMS/src/main/java/com/dotmarketing/util/TaskLocatorUtil.java index 2a96dbda5601..281ea2f9b541 100644 --- a/dotCMS/src/main/java/com/dotmarketing/util/TaskLocatorUtil.java +++ b/dotCMS/src/main/java/com/dotmarketing/util/TaskLocatorUtil.java @@ -351,15 +351,12 @@ public static List> getStartupRunOnceTaskClasses() { .add(Task241009CreatePostgresJobQueueTables.class) .add(Task241013RemoveFullPathLcColumnFromIdentifier.class) .add(Task241014AddTemplateValueOnContentletIndex.class) + .add(Task241015ReplaceLanguagesWithLocalesPortlet.class) .build(); return ret.stream().sorted(classNameComparator).collect(Collectors.toList()); } - final static private Comparator> classNameComparator = new Comparator>() { - public int compare(Class o1, Class o2) { - return o1.getName().compareTo(o2.getName()); - } - }; + private static final Comparator> classNameComparator = Comparator.comparing(Class::getName); /** * Returns list of tasks that are run every time that dotCMS starts diff --git a/dotcms-integration/src/test/java/com/dotcms/MainSuite2b.java b/dotcms-integration/src/test/java/com/dotcms/MainSuite2b.java index e5f62526af07..bd20f8376271 100644 --- a/dotcms-integration/src/test/java/com/dotcms/MainSuite2b.java +++ b/dotcms-integration/src/test/java/com/dotcms/MainSuite2b.java @@ -178,6 +178,7 @@ import com.dotmarketing.startup.runonce.Task240530AddDotAIPortletToLayoutTest; import com.dotmarketing.startup.runonce.Task240606AddVariableColumnToWorkflowTest; import com.dotmarketing.startup.runonce.Task241009CreatePostgresJobQueueTablesTest; +import com.dotmarketing.startup.runonce.Task241015ReplaceLanguagesWithLocalesPortletTest; import com.dotmarketing.startup.runonce.Task241013RemoveFullPathLcColumnFromIdentifierTest; import com.dotmarketing.util.ConfigUtilsTest; import com.dotmarketing.util.ITConfigTest; @@ -386,7 +387,8 @@ LegacyJSONObjectRenderTest.class, Task241013RemoveFullPathLcColumnFromIdentifierTest.class, Task241009CreatePostgresJobQueueTablesTest.class, - Task241013RemoveFullPathLcColumnFromIdentifierTest.class + Task241013RemoveFullPathLcColumnFromIdentifierTest.class, + Task241015ReplaceLanguagesWithLocalesPortletTest.class }) public class MainSuite2b { diff --git a/dotcms-integration/src/test/java/com/dotmarketing/startup/runonce/Task241015ReplaceLanguagesWithLocalesPortletTest.java b/dotcms-integration/src/test/java/com/dotmarketing/startup/runonce/Task241015ReplaceLanguagesWithLocalesPortletTest.java new file mode 100644 index 000000000000..3492e8b50478 --- /dev/null +++ b/dotcms-integration/src/test/java/com/dotmarketing/startup/runonce/Task241015ReplaceLanguagesWithLocalesPortletTest.java @@ -0,0 +1,114 @@ +package com.dotmarketing.startup.runonce; + +import com.dotcms.util.IntegrationTestInitService; +import com.dotmarketing.common.db.DotConnect; +import com.dotmarketing.exception.DotDataException; +import com.dotmarketing.util.UUIDUtil; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.sql.SQLException; +import java.util.List; +import java.util.Map; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class Task241015ReplaceLanguagesWithLocalesPortletTest { + + @BeforeClass + public static void prepare() throws Exception { + // Setting web app environment + IntegrationTestInitService.getInstance().init(); + } + + /** + * Given the locales portlets are inserted + * When the upgrade task is executed + * Then the locales portlets should be added to the layout + * And the task should not need to be forced to run + * + * @throws SQLException if there is an SQL error + * @throws DotDataException if there is a data access error + */ + @Test + public void test_upgradeTask_success() throws SQLException, DotDataException { + final DotConnect dotConnect = new DotConnect(); + final Task241015ReplaceLanguagesWithLocalesPortlet task = new Task241015ReplaceLanguagesWithLocalesPortlet(); + + insertLocalesPortlets(dotConnect); + assertFalse(task.forceRun()); + + deleteAnyLocalesPortlets(dotConnect); + assertTrue(task.forceRun()); + + task.executeUpgrade(); + assertFalse(task.forceRun()); + } + + /** + * Scenario: Upgrade task execution when no languages portlets are present + * + * Given the locales portlets are not present + * When the upgrade task is executed + * Then the locales portlets should be added to the layout + * And the task should not need to be forced to run + * + * @throws SQLException if there is an SQL error + * @throws DotDataException if there is a data access error + */ + @Test + public void test_upgradeTask_noLanguages_success() throws SQLException, DotDataException { + final DotConnect dotConnect = new DotConnect(); + final Task241015ReplaceLanguagesWithLocalesPortlet task = new Task241015ReplaceLanguagesWithLocalesPortlet(); + + assertFalse(task.forceRun()); + + deleteAnyLanguagesPortlets(dotConnect); + deleteAnyLocalesPortlets(dotConnect); + assertTrue(task.forceRun()); + + task.executeUpgrade(); + assertFalse(task.forceRun()); + } + + private void insertLocalesPortlets(final DotConnect dotConnect) throws DotDataException { + final List> rows = dotConnect + .setSQL("SELECT layout_id, portlet_order" + + " FROM cms_layouts_portlets" + + " WHERE portlet_id = ?" + + " ORDER BY portlet_order" + + " DESC LIMIT 1") + .addParam(Task241015ReplaceLanguagesWithLocalesPortlet.LANGUAGES_PORTLET_ID) + .loadObjectResults(); + if (rows.isEmpty()) { + return; + } + + final Map row = rows.get(0); + dotConnect + .setSQL("INSERT INTO cms_layouts_portlets(id, layout_id, portlet_id, portlet_order)" + + " VALUES(?, ?, ?, ?)") + .addParam(UUIDUtil.uuid()) + .addParam(row.get("layout_id")) + .addParam(Task241015ReplaceLanguagesWithLocalesPortlet.LOCALES_PORTLET_ID) + .addParam(((Integer) row.get("portlet_order")) + 1) + .loadResult(); + } + + private void deletePortlets(final DotConnect dotConnect, final String portletId) throws SQLException { + dotConnect + .executeStatement(String.format( + "DELETE FROM cms_layouts_portlets WHERE portlet_id = '%s'", + portletId)); + } + + private void deleteAnyLocalesPortlets(final DotConnect dotConnect) throws SQLException { + deletePortlets(dotConnect, Task241015ReplaceLanguagesWithLocalesPortlet.LOCALES_PORTLET_ID); + } + + private void deleteAnyLanguagesPortlets(final DotConnect dotConnect) throws SQLException { + deletePortlets(dotConnect, Task241015ReplaceLanguagesWithLocalesPortlet.LANGUAGES_PORTLET_ID); + } + +}