diff --git a/dotCMS/src/main/java/com/dotmarketing/business/FactoryLocator.java b/dotCMS/src/main/java/com/dotmarketing/business/FactoryLocator.java
index fb8f1a4a515f..cb3d99958d92 100644
--- a/dotCMS/src/main/java/com/dotmarketing/business/FactoryLocator.java
+++ b/dotCMS/src/main/java/com/dotmarketing/business/FactoryLocator.java
@@ -38,6 +38,8 @@
import com.dotcms.publisher.environment.business.EnvironmentFactoryImpl;
import com.dotcms.variant.VariantFactory;
import com.dotcms.variant.VariantFactoryImpl;
+import com.dotmarketing.business.portal.PortletFactory;
+import com.dotmarketing.business.portal.PortletFactoryImpl;
import com.dotmarketing.common.reindex.ReindexQueueFactory;
import com.dotmarketing.exception.DotRuntimeException;
import com.dotmarketing.plugin.business.PluginFactory;
@@ -290,6 +292,15 @@ public static ContentAnalyticsFactory getContentAnalyticsFactory() {
return (ContentAnalyticsFactory) getInstance(FactoryIndex.CONTENT_ANALYTICS_FACTORY);
}
+ /**
+ * Returns a singleton of the {@link PortletFactory} class.
+ *
+ * @return The {@link PortletFactory} singleton.
+ */
+ public static PortletFactory getPortletFactory() {
+ return (PortletFactory) getInstance(FactoryIndex.PORTLET_FACTORY);
+ }
+
private static Object getInstance(FactoryIndex index) {
if(instance == null){
@@ -369,7 +380,8 @@ enum FactoryIndex
SYSTEM_TABLE_FACTORY,
CUBEJS_CLIENT_FACTORY,
LANGUAGE_VARIABLE_FACTORY,
- CONTENT_ANALYTICS_FACTORY;
+ CONTENT_ANALYTICS_FACTORY,
+ PORTLET_FACTORY;
Object create() {
switch(this) {
@@ -414,8 +426,9 @@ Object create() {
case CUBEJS_CLIENT_FACTORY: return new CubeJSClientFactoryImpl();
case LANGUAGE_VARIABLE_FACTORY: return new LanguageVariableFactoryImpl();
case CONTENT_ANALYTICS_FACTORY: CDIUtils.getBean(ContentAnalyticsFactory.class).orElseThrow(() -> new DotRuntimeException("ContentAnalyticsFactory not found"));
+ case PORTLET_FACTORY: return new PortletFactoryImpl();
}
throw new AssertionError("Unknown Factory Index: " + this);
}
-}
+}
diff --git a/dotCMS/src/main/java/com/dotmarketing/startup/runonce/Task241016AddCustomLanguageVariablesPortletToLayout.java b/dotCMS/src/main/java/com/dotmarketing/startup/runonce/Task241016AddCustomLanguageVariablesPortletToLayout.java
new file mode 100644
index 000000000000..8834e90ea414
--- /dev/null
+++ b/dotCMS/src/main/java/com/dotmarketing/startup/runonce/Task241016AddCustomLanguageVariablesPortletToLayout.java
@@ -0,0 +1,164 @@
+package com.dotmarketing.startup.runonce;
+
+import com.dotcms.exception.ExceptionUtil;
+import com.dotmarketing.business.CacheLocator;
+import com.dotmarketing.business.FactoryLocator;
+import com.dotmarketing.common.db.DotConnect;
+import com.dotmarketing.exception.DotDataException;
+import com.dotmarketing.startup.StartupTask;
+import com.dotmarketing.util.Logger;
+import com.dotmarketing.util.UUIDUtil;
+import com.dotmarketing.util.UtilMethods;
+import com.liferay.portal.model.Portlet;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * Adds the custom 'Language Variables' portlet to all layouts which have 'Locales' portlet too, if
+ * it does not already exist. If there is already a Language Variables portlet, that one will be
+ * used instead.
+ *
+ * @author Jose Castro
+ * @since Oct 15th, 2024
+ */
+public class Task241016AddCustomLanguageVariablesPortletToLayout implements StartupTask {
+
+ public static final String LANGUAGE_VARIABLES_PORTLET_ID = "c_Language-Variables";
+ public static final String LANGUAGE_VARIABLES_PORTLET_NAME = "Language Variables";
+ public static final String LANGUAGE_VARIABLES_CT_VAR_NAME = "Languagevariable";
+
+ private static final String LOCALES_PORTLET_ID = "locales";
+
+ /**
+ * Verifies if the custom {@code Language Variables} portlet must be added or not. It performs
+ * the following data checks:
+ *
+ * - The {@code Language Variables} portlet must be added in the same layout as the
+ * {@code Locales} portlet.
+ * - If the {@code Locales} portlet is not part of any menu, the
+ * {@code Language Variables} portlet must be added manually then.
+ * - If the {@code Language Variables} portlet is already present, the UT can be skipped.
+ *
+ *
+ *
+ * @return If the UT must run, returns {@code true}.
+ */
+ @Override
+ public boolean forceRun() {
+ try {
+ final List