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")