This fixes #196.
<dependency>
<groupId>net.pterodactylus</groupId>
<artifactId>utils</artifactId>
- <version>0.9.5</version>
+ <version>0.9.6-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>junit</groupId>
import net.pterodactylus.util.config.ConfigurationException;
import net.pterodactylus.util.logging.Logging;
import net.pterodactylus.util.number.Numbers;
+import net.pterodactylus.util.validation.IntegerRangeValidator;
import net.pterodactylus.util.validation.Validation;
import net.pterodactylus.util.version.Version;
import freenet.keys.FreenetURI;
@SuppressWarnings("unchecked")
private void loadConfiguration() {
/* create options. */
- options.addIntegerOption("InsertionDelay", new DefaultOption<Integer>(60, new OptionWatcher<Integer>() {
+ options.addIntegerOption("InsertionDelay", new DefaultOption<Integer>(60, new IntegerRangeValidator(0, Integer.MAX_VALUE), new OptionWatcher<Integer>() {
@Override
public void optionChanged(Option<Integer> option, Integer oldValue, Integer newValue) {
}
}));
- options.addIntegerOption("PostsPerPage", new DefaultOption<Integer>(10));
+ options.addIntegerOption("PostsPerPage", new DefaultOption<Integer>(10, new IntegerRangeValidator(1, Integer.MAX_VALUE)));
options.addBooleanOption("RequireFullAccess", new DefaultOption<Boolean>(false));
- options.addIntegerOption("PositiveTrust", new DefaultOption<Integer>(75));
- options.addIntegerOption("NegativeTrust", new DefaultOption<Integer>(-25));
+ options.addIntegerOption("PositiveTrust", new DefaultOption<Integer>(75, new IntegerRangeValidator(0, 100)));
+ options.addIntegerOption("NegativeTrust", new DefaultOption<Integer>(-25, new IntegerRangeValidator(-100, 100)));
options.addStringOption("TrustComment", new DefaultOption<String>("Set from Sone Web Interface"));
options.addBooleanOption("SoneRescueMode", new DefaultOption<Boolean>(false));
options.addBooleanOption("ClearOnNextRestart", new DefaultOption<Boolean>(false));
return;
}
- options.getIntegerOption("InsertionDelay").set(configuration.getIntValue("Option/InsertionDelay").getValue(null));
- options.getIntegerOption("PostsPerPage").set(configuration.getIntValue("Option/PostsPerPage").getValue(null));
+ loadConfigurationValue("InsertionDelay");
+ loadConfigurationValue("PostsPerPage");
options.getBooleanOption("RequireFullAccess").set(configuration.getBooleanValue("Option/RequireFullAccess").getValue(null));
- options.getIntegerOption("PositiveTrust").set(configuration.getIntValue("Option/PositiveTrust").getValue(null));
- options.getIntegerOption("NegativeTrust").set(configuration.getIntValue("Option/NegativeTrust").getValue(null));
+ loadConfigurationValue("PositiveTrust");
+ loadConfigurationValue("NegativeTrust");
options.getStringOption("TrustComment").set(configuration.getStringValue("Option/TrustComment").getValue(null));
options.getBooleanOption("SoneRescueMode").set(configuration.getBooleanValue("Option/SoneRescueMode").getValue(null));
}
/**
+ * Loads an {@link Integer} configuration value for the option with the
+ * given name, logging validation failures.
+ *
+ * @param optionName
+ * The name of the option to load
+ */
+ private void loadConfigurationValue(String optionName) {
+ try {
+ options.getIntegerOption(optionName).set(configuration.getIntValue("Option/" + optionName).getValue(null));
+ } catch (IllegalArgumentException iae1) {
+ logger.log(Level.WARNING, "Invalid value for " + optionName + " in configuration, using default.");
+ }
+ }
+
+ /**
* Generate a Sone URI from the given URI and latest edition.
*
* @param uriString
}
/**
+ * Validates the given insertion delay.
+ *
+ * @param insertionDelay
+ * The insertion delay to validate
+ * @return {@code true} if the given insertion delay was valid, {@code
+ * false} otherwise
+ */
+ public boolean validateInsertionDelay(Integer insertionDelay) {
+ return options.getIntegerOption("InsertionDelay").validate(insertionDelay);
+ }
+
+ /**
* Sets the insertion delay
*
* @param insertionDelay
}
/**
+ * Validates the number of posts per page.
+ *
+ * @param postsPerPage
+ * The number of posts per page
+ * @return {@code true} if the number of posts per page was valid,
+ * {@code false} otherwise
+ */
+ public boolean validatePostsPerPage(Integer postsPerPage) {
+ return options.getIntegerOption("PostsPerPage").validate(postsPerPage);
+ }
+
+ /**
* Sets the number of posts to show per page.
*
* @param postsPerPage
}
/**
+ * Validates the positive trust.
+ *
+ * @param positiveTrust
+ * The positive trust to validate
+ * @return {@code true} if the positive trust was valid, {@code false}
+ * otherwise
+ */
+ public boolean validatePositiveTrust(Integer positiveTrust) {
+ return options.getIntegerOption("PositiveTrust").validate(positiveTrust);
+ }
+
+ /**
* Sets the positive trust.
*
* @param positiveTrust
}
/**
+ * Validates the negative trust.
+ *
+ * @param negativeTrust
+ * The negative trust to validate
+ * @return {@code true} if the negative trust was valid, {@code false}
+ * otherwise
+ */
+ public boolean validateNegativeTrust(Integer negativeTrust) {
+ return options.getIntegerOption("NegativeTrust").validate(negativeTrust);
+ }
+
+ /**
* Sets the negative trust.
*
* @param negativeTrust
import java.util.List;
import java.util.Map;
+import net.pterodactylus.util.validation.Validator;
+
/**
* Stores various options that influence Sone’s behaviour.
*
public T getReal();
/**
+ * Validates the given value. Note that {@code null} is always a valid
+ * value!
+ *
+ * @param value
+ * The value to validate
+ * @return {@code true} if this option does not have a {@link Validator}
+ * , or the {@link Validator} validates this object, {@code
+ * false} otherwise
+ */
+ public boolean validate(T value);
+
+ /**
* Sets the current value of the option.
*
* @param value
* The new value of the option
+ * @throws IllegalArgumentException
+ * if the value is not valid for this option
*/
- public void set(T value);
+ public void set(T value) throws IllegalArgumentException;
}
/** The current value. */
private volatile T value;
+ /** The validator. */
+ private Validator<T> validator;
+
/** The option watcher. */
private final List<OptionWatcher<T>> optionWatchers = new ArrayList<OptionWatcher<T>>();
* The option watchers
*/
public DefaultOption(T defaultValue, OptionWatcher<T>... optionWatchers) {
+ this(defaultValue, null, optionWatchers);
+ }
+
+ /**
+ * Creates a new default option.
+ *
+ * @param defaultValue
+ * The default value of the option
+ * @param validator
+ * The validator for value validation
+ * @param optionWatchers
+ * The option watchers
+ */
+ public DefaultOption(T defaultValue, Validator<T> validator, OptionWatcher<T>... optionWatchers) {
this.defaultValue = defaultValue;
+ this.validator = validator;
this.optionWatchers.addAll(Arrays.asList(optionWatchers));
}
/**
* {@inheritDoc}
*/
+ public boolean validate(T value) {
+ return (validator == null) || (value == null) || validator.validate(value);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
@Override
public void set(T value) {
+ if ((value != null) && (validator != null) && (!validator.validate(value))) {
+ throw new IllegalArgumentException("New Value (" + value + ") could not be validated.");
+ }
T oldValue = this.value;
this.value = value;
if (!get().equals(oldValue)) {
package net.pterodactylus.sone.web;
+import java.util.ArrayList;
+import java.util.List;
+
import net.pterodactylus.sone.core.Core.Preferences;
import net.pterodactylus.sone.data.Sone;
import net.pterodactylus.sone.web.page.Page.Request.Method;
Preferences preferences = webInterface.getCore().getPreferences();
Sone currentSone = webInterface.getCurrentSone(request.getToadletContext(), false);
if (request.getMethod() == Method.POST) {
+ List<String> fieldErrors = new ArrayList<String>();
if (currentSone != null) {
boolean autoFollow = request.getHttpRequest().isPartSet("auto-follow");
currentSone.getOptions().getBooleanOption("AutoFollow").set(autoFollow);
webInterface.getCore().saveSone(currentSone);
}
Integer insertionDelay = Numbers.safeParseInteger(request.getHttpRequest().getPartAsStringFailsafe("insertion-delay", 16));
- preferences.setInsertionDelay(insertionDelay);
+ if (!preferences.validateInsertionDelay(insertionDelay)) {
+ fieldErrors.add("insertion-delay");
+ } else {
+ preferences.setInsertionDelay(insertionDelay);
+ }
Integer postsPerPage = Numbers.safeParseInteger(request.getHttpRequest().getPartAsStringFailsafe("posts-per-page", 4), null);
- preferences.setPostsPerPage(postsPerPage);
+ if (!preferences.validatePostsPerPage(postsPerPage)) {
+ fieldErrors.add("posts-per-page");
+ } else {
+ preferences.setPostsPerPage(postsPerPage);
+ }
boolean requireFullAccess = request.getHttpRequest().isPartSet("require-full-access");
preferences.setRequireFullAccess(requireFullAccess);
Integer positiveTrust = Numbers.safeParseInteger(request.getHttpRequest().getPartAsStringFailsafe("positive-trust", 3));
- preferences.setPositiveTrust(positiveTrust);
+ if (!preferences.validatePositiveTrust(positiveTrust)) {
+ fieldErrors.add("positive-trust");
+ } else {
+ preferences.setPositiveTrust(positiveTrust);
+ }
Integer negativeTrust = Numbers.safeParseInteger(request.getHttpRequest().getPartAsStringFailsafe("negative-trust", 4));
- preferences.setNegativeTrust(negativeTrust);
+ if (!preferences.validateNegativeTrust(negativeTrust)) {
+ fieldErrors.add("negative-trust");
+ } else {
+ preferences.setNegativeTrust(negativeTrust);
+ }
String trustComment = request.getHttpRequest().getPartAsStringFailsafe("trust-comment", 256);
if (trustComment.trim().length() == 0) {
trustComment = null;
boolean reallyClearOnNextRestart = Boolean.parseBoolean(request.getHttpRequest().getPartAsStringFailsafe("really-clear-on-next-restart", 5));
preferences.setReallyClearOnNextRestart(reallyClearOnNextRestart);
webInterface.getCore().saveConfiguration();
- throw new RedirectException(getPath());
+ if (fieldErrors.isEmpty()) {
+ throw new RedirectException(getPath());
+ }
+ templateContext.set("fieldErrors", fieldErrors);
}
if (currentSone != null) {
templateContext.set("auto-follow", currentSone.getOptions().getBooleanOption("AutoFollow").get());
import net.pterodactylus.util.notify.NotificationManager;
import net.pterodactylus.util.notify.TemplateNotification;
import net.pterodactylus.util.template.CollectionSortFilter;
+import net.pterodactylus.util.template.ContainsFilter;
import net.pterodactylus.util.template.DateFilter;
import net.pterodactylus.util.template.FormatFilter;
import net.pterodactylus.util.template.HtmlFilter;
templateContextFactory.addFilter("format", new FormatFilter());
templateContextFactory.addFilter("sort", new CollectionSortFilter());
templateContextFactory.addFilter("replyGroup", new ReplyGroupFilter());
+ templateContextFactory.addFilter("in", new ContainsFilter());
templateContextFactory.addProvider(Provider.TEMPLATE_CONTEXT_PROVIDER);
templateContextFactory.addProvider(new ClassPathTemplateProvider());
templateContextFactory.addTemplateObject("formPassword", formPassword);
Page.Options.Section.Cleaning.Title=Clean Up
Page.Options.Option.ClearOnNextRestart.Description=Resets the configuration of the Sone plugin at the next restart. Warning! {strong}This will destroy all of your Sones{/strong} so make sure you have backed up everyhing you still need! Also, you need to set the next option to true to actually do it.
Page.Options.Option.ReallyClearOnNextRestart.Description=This option needs to be set to “yes” if you really, {strong}really{/strong} want to clear the plugin configuration on the next restart.
+Page.Options.Warnings.ValueNotChanged=This option was not changed because value you specified was not valid.
Page.Options.Button.Save=Save
Page.Login.Title=Login - Sone
font-weight: bold;
color: red;
}
+
+#sone .warning {
+ color: red;
+ font-style: italic;
+}
<h2><%= Page.Options.Section.RuntimeOptions.Title|l10n|html></h2>
<p><%= Page.Options.Option.InsertionDelay.Description|l10n|html></p>
+ <%if =insertion-delay|in collection=fieldErrors>
+ <p class="warning"><%= Page.Options.Warnings.ValueNotChanged|l10n|html></p>
+ <%/if>
<p><input type="text" name="insertion-delay" value="<% insertion-delay|html>" /></p>
<p><%= Page.Options.Option.PostsPerPage.Description|l10n|html></p>
+ <%if =posts-per-page|in collection=fieldErrors>
+ <p class="warning"><%= Page.Options.Warnings.ValueNotChanged|l10n|html></p>
+ <%/if>
<p><input type="text" name="posts-per-page" value="<% posts-per-page|html>" /></p>
<p>
<h2><%= Page.Options.Section.TrustOptions.Title|l10n|html></h2>
<p><%= Page.Options.Option.PositiveTrust.Description|l10n|html></p>
+ <%if =positive-trust|in collection=fieldErrors>
+ <p class="warning"><%= Page.Options.Warnings.ValueNotChanged|l10n|html></p>
+ <%/if>
<p><input type="text" name="positive-trust" value="<% positive-trust|html>" /></p>
<p><%= Page.Options.Option.NegativeTrust.Description|l10n|html></p>
+ <%if =negative-trust|in collection=fieldErrors>
+ <p class="warning"><%= Page.Options.Warnings.ValueNotChanged|l10n|html></p>
+ <%/if>
<p><input type="text" name="negative-trust" value="<% negative-trust|html>" /></p>
<p><%= Page.Options.Option.TrustComment.Description|l10n|html></p>