From 9aa4053474856320e263e90c63ee9df631cd94a8 Mon Sep 17 00:00:00 2001 From: "David M. Johnson" Date: Sat, 14 Nov 2026 22:12:50 -0500 Subject: [PATCH] Make the Design pages follow the custom-theme setting The Design menu is shown only while themes.customtheme.allowed is on, but the theme, stylesheet and template actions behind it did not check that setting. They now return it from isFeatureEnabled(), so the security interceptor treats them like the menu does. The XML-RPC blogger.setTemplate call checks the same setting. Weblogs that already use custom templates keep rendering them. --- CHANGES.md | 11 +++ .../ui/struts2/editor/StylesheetEdit.java | 10 +++ .../ui/struts2/editor/TemplateEdit.java | 10 +++ .../ui/struts2/editor/Templates.java | 10 +++ .../ui/struts2/editor/ThemeEdit.java | 10 +++ .../webservices/xmlrpc/BloggerAPIHandler.java | 10 ++- .../editor/DesignPagesFeatureTest.java | 67 +++++++++++++++++++ .../xmlrpc/XMLRPCWeblogPermissionTest.java | 32 +++++++++ 8 files changed, 159 insertions(+), 1 deletion(-) create mode 100644 app/src/test/java/org/apache/roller/weblogger/ui/struts2/editor/DesignPagesFeatureTest.java diff --git a/CHANGES.md b/CHANGES.md index ae054e20d7..ff165e3142 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,16 @@ # Apache Roller — Changes +## 6.1.8 (in development) + +### Behaviour changes worth reading before upgrading + +- **The Design pages follow "Allow custom themes".** While + `themes.customtheme.allowed` is off (the default), the theme, stylesheet and + template pages answer as unavailable, as the hidden Design menu already + implied, and the XML-RPC `blogger.setTemplate` call is refused. Weblogs that + already use custom templates keep showing them. Turn the setting on in + Server Settings to edit templates again. + ## 6.1.7 ### Improvements diff --git a/app/src/main/java/org/apache/roller/weblogger/ui/struts2/editor/StylesheetEdit.java b/app/src/main/java/org/apache/roller/weblogger/ui/struts2/editor/StylesheetEdit.java index 049d28a581..e833a7ed72 100644 --- a/app/src/main/java/org/apache/roller/weblogger/ui/struts2/editor/StylesheetEdit.java +++ b/app/src/main/java/org/apache/roller/weblogger/ui/struts2/editor/StylesheetEdit.java @@ -20,6 +20,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.apache.roller.weblogger.config.WebloggerRuntimeConfig; import org.apache.roller.weblogger.WebloggerException; import org.apache.roller.weblogger.business.WeblogManager; import org.apache.roller.weblogger.business.WebloggerFactory; @@ -59,6 +60,15 @@ public StylesheetEdit() { this.pageTitle = "stylesheetEdit.title"; } + /** + * The Design pages are available only while the site allows weblogs to + * have custom themes, matching the Design menu. + */ + @Override + public boolean isFeatureEnabled() { + return WebloggerRuntimeConfig.getBooleanProperty("themes.customtheme.allowed"); + } + @Override public void myPrepare() { diff --git a/app/src/main/java/org/apache/roller/weblogger/ui/struts2/editor/TemplateEdit.java b/app/src/main/java/org/apache/roller/weblogger/ui/struts2/editor/TemplateEdit.java index fea372e0d2..d90e3cee72 100644 --- a/app/src/main/java/org/apache/roller/weblogger/ui/struts2/editor/TemplateEdit.java +++ b/app/src/main/java/org/apache/roller/weblogger/ui/struts2/editor/TemplateEdit.java @@ -21,6 +21,7 @@ import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.apache.roller.weblogger.config.WebloggerRuntimeConfig; import org.apache.roller.weblogger.WebloggerException; import org.apache.roller.weblogger.business.WebloggerFactory; import org.apache.roller.weblogger.pojos.TemplateRendition.TemplateLanguage; @@ -55,6 +56,15 @@ public TemplateEdit() { this.pageTitle = "pagesForm.title"; } + /** + * The Design pages are available only while the site allows weblogs to + * have custom themes, matching the Design menu. + */ + @Override + public boolean isFeatureEnabled() { + return WebloggerRuntimeConfig.getBooleanProperty("themes.customtheme.allowed"); + } + @Override public void myPrepare() { try { diff --git a/app/src/main/java/org/apache/roller/weblogger/ui/struts2/editor/Templates.java b/app/src/main/java/org/apache/roller/weblogger/ui/struts2/editor/Templates.java index 157e8f4092..7dcd32d9e8 100644 --- a/app/src/main/java/org/apache/roller/weblogger/ui/struts2/editor/Templates.java +++ b/app/src/main/java/org/apache/roller/weblogger/ui/struts2/editor/Templates.java @@ -21,6 +21,7 @@ import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.apache.roller.weblogger.config.WebloggerRuntimeConfig; import org.apache.roller.util.RollerConstants; import org.apache.roller.weblogger.WebloggerException; import org.apache.roller.weblogger.business.WeblogManager; @@ -67,6 +68,15 @@ public Templates() { this.pageTitle = "pagesForm.title"; } + /** + * The Design pages are available only while the site allows weblogs to + * have custom themes, matching the Design menu. + */ + @Override + public boolean isFeatureEnabled() { + return WebloggerRuntimeConfig.getBooleanProperty("themes.customtheme.allowed"); + } + @Override public String execute() { diff --git a/app/src/main/java/org/apache/roller/weblogger/ui/struts2/editor/ThemeEdit.java b/app/src/main/java/org/apache/roller/weblogger/ui/struts2/editor/ThemeEdit.java index bf5a52725a..02b6e35160 100644 --- a/app/src/main/java/org/apache/roller/weblogger/ui/struts2/editor/ThemeEdit.java +++ b/app/src/main/java/org/apache/roller/weblogger/ui/struts2/editor/ThemeEdit.java @@ -24,6 +24,7 @@ import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.apache.roller.weblogger.config.WebloggerRuntimeConfig; import org.apache.roller.weblogger.WebloggerException; import org.apache.roller.weblogger.business.WeblogManager; import org.apache.roller.weblogger.business.WebloggerFactory; @@ -73,6 +74,15 @@ public ThemeEdit() { this.pageTitle = "themeEditor.title"; } + /** + * The Design pages are available only while the site allows weblogs to + * have custom themes, matching the Design menu. + */ + @Override + public boolean isFeatureEnabled() { + return WebloggerRuntimeConfig.getBooleanProperty("themes.customtheme.allowed"); + } + @Override public void myPrepare() { ThemeManager themeMgr = WebloggerFactory.getWeblogger() diff --git a/app/src/main/java/org/apache/roller/weblogger/webservices/xmlrpc/BloggerAPIHandler.java b/app/src/main/java/org/apache/roller/weblogger/webservices/xmlrpc/BloggerAPIHandler.java index 32fa5b5519..203e59c296 100644 --- a/app/src/main/java/org/apache/roller/weblogger/webservices/xmlrpc/BloggerAPIHandler.java +++ b/app/src/main/java/org/apache/roller/weblogger/webservices/xmlrpc/BloggerAPIHandler.java @@ -35,6 +35,7 @@ import org.apache.roller.weblogger.business.Weblogger; import org.apache.roller.weblogger.business.WebloggerFactory; import org.apache.roller.weblogger.business.WeblogEntryManager; +import org.apache.roller.weblogger.config.WebloggerRuntimeConfig; import org.apache.roller.weblogger.pojos.CustomTemplateRendition; import org.apache.roller.weblogger.pojos.TemplateRendition.RenditionType; import org.apache.roller.weblogger.pojos.User; @@ -149,7 +150,14 @@ public boolean setTemplate(String appkey, String blogid, String userid, throw new XmlRpcException( UNKNOWN_EXCEPTION, "Roller supports only main template"); } - + + // templates are edited only while the site allows custom themes, + // as on the Design pages + if (!WebloggerRuntimeConfig.getBooleanProperty("themes.customtheme.allowed")) { + throw new XmlRpcException(UNKNOWN_EXCEPTION, + "Custom templates are not enabled on this site"); + } + try { WeblogTemplate template = WebloggerFactory.getWeblogger() .getWeblogManager().getTemplateByAction( diff --git a/app/src/test/java/org/apache/roller/weblogger/ui/struts2/editor/DesignPagesFeatureTest.java b/app/src/test/java/org/apache/roller/weblogger/ui/struts2/editor/DesignPagesFeatureTest.java new file mode 100644 index 0000000000..c2e555790b --- /dev/null +++ b/app/src/test/java/org/apache/roller/weblogger/ui/struts2/editor/DesignPagesFeatureTest.java @@ -0,0 +1,67 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. The ASF licenses this file to You + * under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. For additional information regarding + * copyright in this work, please see the NOTICE file in the top level + * directory of this distribution. + */ + +package org.apache.roller.weblogger.ui.struts2.editor; + +import java.util.List; +import java.util.function.Supplier; + +import org.apache.roller.weblogger.config.WebloggerRuntimeConfig; +import org.apache.roller.weblogger.ui.struts2.util.UISecurityEnforced; +import org.junit.jupiter.api.Test; +import org.mockito.MockedStatic; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.mockStatic; + +/** + * The Design menu appears only while the site allows custom themes, and the + * pages behind it must follow the same setting. + */ +public class DesignPagesFeatureTest { + + private static final List> DESIGN_PAGES = List.of( + ThemeEdit::new, Templates::new, TemplateEdit::new, StylesheetEdit::new); + + private static void assertDesignPagesEnabled(boolean customThemesAllowed) { + try (MockedStatic config = mockStatic(WebloggerRuntimeConfig.class)) { + config.when(() -> WebloggerRuntimeConfig.getBooleanProperty("themes.customtheme.allowed")) + .thenReturn(customThemesAllowed); + for (Supplier page : DESIGN_PAGES) { + UISecurityEnforced action = page.get(); + String name = action.getClass().getSimpleName(); + if (customThemesAllowed) { + assertTrue(action.isFeatureEnabled(), name); + } else { + assertFalse(action.isFeatureEnabled(), name); + } + } + } + } + + @Test + public void designPagesAreAvailableWhileCustomThemesAreAllowed() { + assertDesignPagesEnabled(true); + } + + @Test + public void designPagesAreUnavailableWhileCustomThemesAreNotAllowed() { + assertDesignPagesEnabled(false); + } +} diff --git a/app/src/test/java/org/apache/roller/weblogger/webservices/xmlrpc/XMLRPCWeblogPermissionTest.java b/app/src/test/java/org/apache/roller/weblogger/webservices/xmlrpc/XMLRPCWeblogPermissionTest.java index 4e775e3fe5..042cea2d22 100644 --- a/app/src/test/java/org/apache/roller/weblogger/webservices/xmlrpc/XMLRPCWeblogPermissionTest.java +++ b/app/src/test/java/org/apache/roller/weblogger/webservices/xmlrpc/XMLRPCWeblogPermissionTest.java @@ -83,6 +83,7 @@ public class XMLRPCWeblogPermissionTest { private WeblogEntry publishedEntry; private WeblogEntry deleteEntry; private String oldXmlRpcValue; + private String oldCustomThemeValue; private Object oldPasswordEncoder; @BeforeEach @@ -97,6 +98,7 @@ public void setUp() throws Exception { oldXmlRpcValue = xmlRpcProperty.getValue(); xmlRpcProperty.setValue("true"); propertiesManager.saveProperty(xmlRpcProperty); + oldCustomThemeValue = setCustomThemesAllowed(true); owner = TestUtils.setupUser("xmlrpcOwner"); author = TestUtils.setupUser("xmlrpcAuthor"); @@ -151,10 +153,40 @@ public void tearDown() throws Exception { .getProperty("webservices.enableXmlRpc"); xmlRpcProperty.setValue(oldXmlRpcValue); propertiesManager.saveProperty(xmlRpcProperty); + RuntimeConfigProperty customThemeProperty = propertiesManager + .getProperty("themes.customtheme.allowed"); + customThemeProperty.setValue(oldCustomThemeValue); + propertiesManager.saveProperty(customThemeProperty); TestUtils.endSession(true); restorePasswordEncoder(); } + @Test + public void testSetTemplateFollowsTheCustomThemeSetting() throws Exception { + String ownerName = owner.getUserName(); + String handle = weblog.getHandle(); + + setCustomThemesAllowed(false); + TestUtils.endSession(true); + + assertThrows(XmlRpcException.class, () -> blogger.setTemplate("", handle, + ownerName, PASSWORD, "changed-while-disallowed", "main")); + assertEquals("main-template-code", blogger.getTemplate("", handle, + ownerName, PASSWORD, "main")); + } + + /** Sets themes.customtheme.allowed and returns its previous value. */ + private String setCustomThemesAllowed(boolean allowed) throws Exception { + PropertiesManager propertiesManager = WebloggerFactory.getWeblogger() + .getPropertiesManager(); + RuntimeConfigProperty property = propertiesManager + .getProperty("themes.customtheme.allowed"); + String previous = property.getValue(); + property.setValue(Boolean.toString(allowed)); + propertiesManager.saveProperty(property); + return previous; + } + @Test public void testAuthenticationAndWeblogDiscovery() throws Exception { @SuppressWarnings("unchecked")