Index: AbstractFileProvider.java =================================================================== --- AbstractFileProvider.java (revision 1618353) +++ AbstractFileProvider.java (working copy) @@ -31,11 +31,13 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Date; +import java.util.Enumeration; import java.util.List; import java.util.Properties; import java.util.TreeSet; import org.apache.commons.io.IOUtils; +import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; import org.apache.wiki.InternalWikiException; import org.apache.wiki.WikiEngine; @@ -72,6 +74,18 @@ protected String m_encoding; protected WikiEngine m_engine; + + public static final String PROP_CUSTOMPROP_MAXLIMIT = "custom.property.max.allowed"; + public static final String PROP_CUSTOMPROP_MAXKEYLENGTH = "custom.property.key.length"; + public static final String PROP_CUSTOMPROP_MAXVALLENGTH = "custom.property.value.length"; + + public static final int DEFAULT_MAX_PROPLIMIT = 100; + public static final int DEFAULT_MAX_PROPKEYLENGTH = 255; + public static final int DEFAULT_MAX_PROPVALUELENGTH = 255; + + public static int MAX_PROPLIMIT = DEFAULT_MAX_PROPLIMIT; + public static int MAX_PROPKEYLENGTH = DEFAULT_MAX_PROPKEYLENGTH; + public static int MAX_PROPVALUELENGTH = DEFAULT_MAX_PROPVALUELENGTH; /** * Name of the property that defines where page directories are. @@ -136,6 +150,21 @@ m_windowsHackNeeded = true; } + if (properties != null) { + String maxLimit = (String)properties.get(PROP_CUSTOMPROP_MAXLIMIT); + if (StringUtils.isNotBlank(maxLimit) && StringUtils.isNumeric(maxLimit)) { + MAX_PROPLIMIT = Integer.parseInt(maxLimit); + } + String maxKeyLength = (String)properties.get(PROP_CUSTOMPROP_MAXKEYLENGTH); + if (StringUtils.isNotBlank(maxKeyLength) && StringUtils.isNumeric(maxKeyLength)) { + MAX_PROPKEYLENGTH = Integer.parseInt(maxKeyLength); + } + String maxValueLength = (String)properties.get(PROP_CUSTOMPROP_MAXVALLENGTH); + if (StringUtils.isNotBlank(maxValueLength) && StringUtils.isNumeric(maxValueLength)) { + MAX_PROPVALUELENGTH = Integer.parseInt(maxValueLength); + } + } + log.info( "Wikipages are read from '" + m_pageDirectory + "'" ); } @@ -514,6 +543,81 @@ } /** + * Add the properties provided into the given page. + */ + protected void setCustomProperties(WikiPage page, Properties properties) { + Enumeration propertyNames = properties.propertyNames(); + while (propertyNames.hasMoreElements()) { + String key = (String) propertyNames.nextElement(); + if (!key.equals("author") && !key.equals("changenote") && !key.equals("viewcount")) { + page.setAttribute(key, properties.get(key)); + } + } + } + + /** + * Get the properties using {@link getCustomPageProperties}, validate them using {@link validateCustomPageProperties} + * and add them to default properties provided + */ + protected void addCustomProperties(String page, Properties defaultProperties) { + Properties customPageProperties = getCustomPageProperties(page,defaultProperties); + try { + validateCustomPageProperties(customPageProperties); + defaultProperties.putAll(customPageProperties); + } catch (Exception e) { + log.error("CustomPageProperties could not be saved! ERROR: "+e.getMessage()); + } + } + + /** + * This can be overwritten by custom FileSystemProviders to save additional properties. Use with {@link validateCustomPageProperties} + * @since 2.10.2 + * @param page the current page + * @param props the default properties of this page + * @return default implementation returns empty Properties. + */ + protected Properties getCustomPageProperties(String page, Properties props) { + return new Properties(); + } + + /** + * Default validation, validates that key and value is ASCII and within lengths set up jspwiki.properties. + * This can be overwritten by custom FileSystemProviders to save additional properties + * See https://issues.apache.org/jira/browse/JSPWIKI-856 + * @since 2.10.2 + * @param customProperties the custom page properties being added + */ + protected void validateCustomPageProperties(Properties customProperties) throws Exception { + // Default validation rules + if (customProperties != null && !customProperties.isEmpty()) { + if (customProperties.size()>MAX_PROPLIMIT) { + throw new Exception("Too many custom properties. You are adding "+customProperties.size()+", but max limit is "+MAX_PROPLIMIT); + } + Enumeration propertyNames = customProperties.propertyNames(); + while (propertyNames.hasMoreElements()) { + String key = (String) propertyNames.nextElement(); + String value = (String)customProperties.get(key); + if (key != null) { + if (key.length()>MAX_PROPKEYLENGTH) { + throw new Exception("Custom property key "+key+" is too long. Max allowed length is "+MAX_PROPKEYLENGTH); + } + if (!StringUtils.isAsciiPrintable(key)) { + throw new Exception("Custom property key "+key+" is not simple ASCII!"); + } + } + if (value != null) { + if (value.length()>MAX_PROPVALUELENGTH) { + throw new Exception("Custom property key "+key+" has value that is too long. Value="+value+". Max allowed length is "+MAX_PROPVALUELENGTH); + } + if (!StringUtils.isAsciiPrintable(value)) { + throw new Exception("Custom property key "+key+" has value that is not simple ASCII! Value="+value); + } + } + } + } + } + + /** * A simple filter which filters only those filenames which correspond to the * file extension used. */ Index: FileSystemProvider.java =================================================================== --- FileSystemProvider.java (revision 1618353) +++ FileSystemProvider.java (working copy) @@ -18,11 +18,16 @@ */ package org.apache.wiki.providers; -import java.io.*; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import java.util.Properties; import org.apache.log4j.Logger; -import org.apache.wiki.*; +import org.apache.wiki.WikiPage; import org.apache.wiki.api.exceptions.ProviderException; /** @@ -72,6 +77,7 @@ { String author = page.getAuthor(); String changenote = (String)page.getAttribute( WikiPage.CHANGENOTE ); + String viewcount = (String)page.getAttribute( WikiPage.VIEWCOUNT ); if( author != null ) { @@ -82,7 +88,15 @@ { props.setProperty( "changenote", changenote ); } + + if( viewcount != null ) + { + props.setProperty( "viewcount", viewcount ); + } + addCustomProperties(page.getName(), props); + + File file = new File( getPageDirectory(), mangleName(page.getName())+PROP_EXT ); @@ -95,7 +109,7 @@ if( out != null ) out.close(); } } - + /** * Gets basic metadata from file. */ @@ -123,6 +137,14 @@ { page.setAttribute( WikiPage.CHANGENOTE, changenote ); } + + String viewcount = props.getProperty( "viewcount" ); + if( viewcount != null ) + { + page.setAttribute( WikiPage.VIEWCOUNT, viewcount ); + } + + setCustomProperties(page, props); } } finally Index: VersioningFileProvider.java =================================================================== --- VersioningFileProvider.java (revision 1618353) +++ VersioningFileProvider.java (working copy) @@ -29,6 +29,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Date; +import java.util.Enumeration; import java.util.Iterator; import java.util.List; import java.util.Properties; @@ -281,6 +282,8 @@ try { + addCustomProperties(page,properties); + out = new FileOutputStream( propertyFile ); properties.store( out, " JSPWiki page properties for "+page+". DO NOT MODIFY!" ); @@ -517,7 +520,10 @@ props.setProperty( versionNumber+".changenote", changeNote ); } + setCustomProperties(page, props); + putPageProperties( page.getName(), props ); + } catch( IOException e ) {