Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
/*
* 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.business;

import org.apache.commons.lang3.StringUtils;
import org.apache.roller.weblogger.WebloggerException;
import org.apache.roller.weblogger.pojos.RuntimeConfigProperty;
import org.apache.roller.weblogger.pojos.Weblog;

/**
* Reads and writes the site frontpage weblog settings.
*
* <p>Two screens change these values: the one-time setup screen used to choose
* a frontpage while the site is being installed, and the global configuration
* screen used afterwards. Both go through here so that the handle is resolved
* and validated the same way and both properties move together.
*/
public final class FrontpageSettings {

public static final String HANDLE_PROPERTY = "site.frontpage.weblog.handle";
public static final String AGGREGATED_PROPERTY = "site.frontpage.weblog.aggregated";

private FrontpageSettings() {
}

/**
* Resolves a submitted handle to a weblog that actually exists and is
* enabled.
*
* @return the weblog, or null when the handle is blank, unknown or refers
* to a disabled weblog
*/
public static Weblog resolveWeblog(String handle) throws WebloggerException {
if (StringUtils.isBlank(handle) || !isValidHandle(handle.trim())) {
return null;
}
return WebloggerFactory.getWeblogger().getWeblogManager()
.getWeblogByHandle(handle.trim(), Boolean.TRUE);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getWeblogByHandle throws WebloggerException("Invalid handle") for anything outside [A-Za-z0-9_], so a POST with frontpageBlog=my-blog takes the generic WebloggerException branch (stack trace at ERROR, "Error saving properties") instead of the invalid-weblog message this method documents. Pre-check the handle or catch that case here.

}

/** @return the configured frontpage handle, or null when none is set. */
public static String getConfiguredHandle() throws WebloggerException {
RuntimeConfigProperty prop = WebloggerFactory.getWeblogger()
.getPropertiesManager().getProperty(HANDLE_PROPERTY);
if (prop == null || StringUtils.isBlank(prop.getValue())) {
return null;
}
return prop.getValue();
}

/** @return true when a frontpage weblog has already been chosen. */
public static boolean isConfigured() throws WebloggerException {
return resolveWeblog(getConfiguredHandle()) != null;
}

/**
* Validates and stores the frontpage selection.
*
* <p>Both properties are written before the single flush so the pair cannot
* be left half-applied, and the canonical handle from the resolved weblog is
* stored rather than the submitted text. A missing aggregation value is
* treated as false, which is what an unchecked checkbox means.
*
* @param handle submitted weblog handle
* @param aggregated submitted aggregation flag; null means false
* @throws InvalidFrontpageWeblogException when the handle does not name an
* existing, enabled weblog
*/
public static boolean applyInitial(String handle, Boolean aggregated)
throws WebloggerException {

Weblog weblog = resolveWeblog(handle);
if (weblog == null) {
throw new InvalidFrontpageWeblogException(handle);
}

PropertiesManager mgr = WebloggerFactory.getWeblogger().getPropertiesManager();

RuntimeConfigProperty handleProp = mgr.getProperty(HANDLE_PROPERTY);
String currentValue = handleProp == null ? null : handleProp.getValue();
if (resolveWeblog(currentValue) != null
|| !mgr.compareAndSetProperty(HANDLE_PROPERTY, currentValue, weblog.getHandle())) {
return false;
}

RuntimeConfigProperty aggregatedProp = mgr.getProperty(AGGREGATED_PROPERTY);
aggregatedProp.setValue(Boolean.toString(Boolean.TRUE.equals(aggregated)));
mgr.saveProperty(aggregatedProp);

WebloggerFactory.getWeblogger().flush();

return true;
}

private static boolean isValidHandle(String handle) {
for (int i = 0; i < handle.length(); i++) {
if (!Character.isLetterOrDigit(handle.charAt(i)) && handle.charAt(i) != '_') {
return false;
}
}
return true;
}

/** Raised when a submitted frontpage handle cannot be used. */
public static class InvalidFrontpageWeblogException extends WebloggerException {
private final String handle;

public InvalidFrontpageWeblogException(String handle) {
super("Not an existing, enabled weblog handle: " + handle);
this.handle = handle;
}

public String getHandle() {
return handle;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,15 @@ public interface PropertiesManager {
* Save a list of properties
*/
void saveProperties(Map<String, RuntimeConfigProperty> properties) throws WebloggerException;


/**
* Replace a property's value only when it still has the expected value.
*
* @return true when the property was updated, otherwise false
*/
boolean compareAndSetProperty(String name, String expectedValue, String newValue)
throws WebloggerException;


/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import jakarta.persistence.LockModeType;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
Expand Down Expand Up @@ -144,6 +147,19 @@ public void saveProperties(Map<String, RuntimeConfigProperty> properties) throws
this.strategy.store(prop);
}
}


@Override
public boolean compareAndSetProperty(String name, String expectedValue, String newValue)
throws WebloggerException {
RuntimeConfigProperty property = strategy.getEntityManager(true).find(
RuntimeConfigProperty.class, name, LockModeType.PESSIMISTIC_WRITE);
if (property == null || !Objects.equals(expectedValue, property.getValue())) {
return false;
}
property.setValue(newValue);
return true;
}


/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.roller.weblogger.WebloggerException;
import org.apache.roller.weblogger.business.FrontpageSettings;
import org.apache.roller.weblogger.business.PropertiesManager;
import org.apache.roller.weblogger.business.WeblogManager;
import org.apache.roller.weblogger.business.WebloggerFactory;
Expand All @@ -37,6 +38,9 @@
import org.apache.roller.weblogger.pojos.GlobalPermission;
import org.apache.roller.weblogger.pojos.RuntimeConfigProperty;
import org.apache.roller.weblogger.pojos.Weblog;
import org.apache.roller.weblogger.ui.rendering.util.cache.SiteWideCache;
import org.apache.roller.weblogger.ui.rendering.util.cache.WeblogFeedCache;
import org.apache.roller.weblogger.ui.rendering.util.cache.WeblogPageCache;
import org.apache.roller.weblogger.ui.struts2.util.UIAction;
import org.apache.roller.weblogger.util.Utilities;
import org.apache.struts2.dispatcher.HttpParameters;
Expand Down Expand Up @@ -156,6 +160,9 @@ public String save() {
return ERROR;
}

String oldFrontpageHandle = propertyValue(FrontpageSettings.HANDLE_PROPERTY);
String oldFrontpageAggregated = propertyValue(FrontpageSettings.AGGREGATED_PROPERTY);

// only set values for properties that are already defined
RuntimeConfigProperty updProp;
String incomingProp;
Expand Down Expand Up @@ -209,6 +216,23 @@ public String save() {
Arrays.asList(propDesc, propName));
}

} else if ( FrontpageSettings.HANDLE_PROPERTY.equals(propertyDef.getName())
&& incomingProp != null ) {
// Declared as a plain string, but it names a weblog, so it
// is resolved through the same service as the setup path. The
// stored value is always a weblog that exists and is enabled.
try {
Weblog weblog = FrontpageSettings.resolveWeblog(incomingProp);
if (weblog == null) {
addError("frontpageConfig.invalidWeblog");
} else {
updProp.setValue(weblog.getHandle());
}
} catch (WebloggerException ex) {
log.error("Error resolving frontpage weblog", ex);
addError("frontpageConfig.values.error");
}

} else if ( incomingProp != null ){
updProp.setValue( incomingProp.trim() );
log.debug("Set something " + propName + " = " + incomingProp);
Expand Down Expand Up @@ -240,6 +264,12 @@ public String save() {
mgr.saveProperties(getProperties());
WebloggerFactory.getWeblogger().flush();

if (!Objects.equals(oldFrontpageHandle, propertyValue(FrontpageSettings.HANDLE_PROPERTY))
|| !Objects.equals(oldFrontpageAggregated,
propertyValue(FrontpageSettings.AGGREGATED_PROPERTY))) {
invalidateRenderedContent();
}

// notify user of our success
addMessage("generic.changes.saved");

Expand All @@ -251,6 +281,17 @@ public String save() {
return SUCCESS;
}

private String propertyValue(String name) {
RuntimeConfigProperty property = getProperties().get(name);
return property == null ? null : property.getValue();
}

private void invalidateRenderedContent() {
SiteWideCache.getInstance().clear();
WeblogPageCache.getInstance().clear();
WeblogFeedCache.getInstance().clear();
}


@Override
public void setParameters(HttpParameters parameters) {
Expand Down
Loading
Loading