You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jspwiki.apache.org by aj...@apache.org on 2008/02/13 06:54:24 UTC
svn commit: r627255 [36/41] - in
/incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src: ./ com/ com/ecyrd/
com/ecyrd/jspwiki/ com/ecyrd/jspwiki/action/ com/ecyrd/jspwiki/attachment/
com/ecyrd/jspwiki/auth/ com/ecyrd/jspwiki/auth/acl/ com/ecyrd/jspwiki...
Added: incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/ui/EditorManager.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/ui/EditorManager.java?rev=627255&view=auto
==============================================================================
--- incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/ui/EditorManager.java (added)
+++ incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/ui/EditorManager.java Tue Feb 12 21:53:55 2008
@@ -0,0 +1,338 @@
+/*
+ JSPWiki - a JSP-based WikiWiki clone.
+
+ Copyright (C) 2001-2002 Janne Jalkanen (Janne.Jalkanen@iki.fi)
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package com.ecyrd.jspwiki.ui;
+
+import java.net.URL;
+import java.util.*;
+
+import javax.servlet.jsp.PageContext;
+
+import org.apache.log4j.Logger;
+import org.jdom.Document;
+import org.jdom.Element;
+import org.jdom.JDOMException;
+import org.jdom.input.SAXBuilder;
+import org.jdom.xpath.XPath;
+
+import com.ecyrd.jspwiki.NoSuchVariableException;
+import com.ecyrd.jspwiki.WikiContext;
+import com.ecyrd.jspwiki.WikiEngine;
+import com.ecyrd.jspwiki.action.PreviewActionBean;
+import com.ecyrd.jspwiki.modules.ModuleManager;
+import com.ecyrd.jspwiki.modules.WikiModuleInfo;
+import com.ecyrd.jspwiki.plugin.PluginManager;
+import com.ecyrd.jspwiki.preferences.Preferences;
+
+/**
+ * Defines an editor manager. An editor can be added by adding a
+ * suitable JSP file under templates/default/editors
+ * If you want your editor to include any scripts or something, you
+ * can simply request it by adding the following in your
+ * ini/jspwiki_module.xml:
+ *
+ * <pre>
+ * <modules>
+ * <editor name="myeditor">
+ * <author>Janne Jalkanen</author>
+ * <script>foo.js</script>
+ * <stylesheet>foo.css</stylesheet>
+ * <path>editors/myeditor.jsp</path>
+ * </editor>
+ * </modules>
+ * </pre>
+ *
+ * @author jalkanen
+ * @author Christoph Sauer
+ * @author Chuck Smith
+ * @author Dirk Frederickx
+ * @since 2.4
+ */
+public class EditorManager extends ModuleManager
+{
+ /** The property name for setting the editor. Current value is "jspwiki.editor" */
+ /* not used anymore -- replaced by defaultpref.template.editor */
+ public static final String PROP_EDITORTYPE = "jspwiki.editor";
+
+ /** Parameter for changing editors at run-time */
+ public static final String PARA_EDITOR = "editor";
+
+ /** Known name for the plain wikimarkup editor. */
+ public static final String EDITOR_PLAIN = "plain";
+
+ /** Known name for the preview editor component. */
+ public static final String EDITOR_PREVIEW = "preview";
+
+ /** Known attribute name for storing the user edited text inside a HTTP parameter. */
+ public static final String REQ_EDITEDTEXT = "_editedtext";
+
+ /** Known attribute name for storing the user edited text inside a session or a page context */
+ public static final String ATTR_EDITEDTEXT = REQ_EDITEDTEXT;
+
+ private Map m_editors;
+
+ private static Logger log = Logger.getLogger( EditorManager.class );
+
+ public EditorManager( WikiEngine engine )
+ {
+ super(engine);
+ }
+
+ /**
+ * Initializes the EditorManager. It also registers any editors it can find.
+ *
+ * @param props Properties for setup.
+ */
+ public void initialize( Properties props )
+ {
+ registerEditors();
+ }
+
+ /**
+ * This method goes through the jspwiki_module.xml files and hunts for editors.
+ * Any editors found are put in the registry.
+ *
+ */
+ private void registerEditors()
+ {
+ log.info( "Registering editor modules" );
+
+ m_editors = new HashMap();
+ SAXBuilder builder = new SAXBuilder();
+
+ try
+ {
+ //
+ // Register all editors which have created a resource containing its properties.
+ //
+ // Get all resources of all modules
+ //
+
+ Enumeration resources = getClass().getClassLoader().getResources( PLUGIN_RESOURCE_LOCATION );
+
+ while( resources.hasMoreElements() )
+ {
+ URL resource = (URL) resources.nextElement();
+
+ try
+ {
+ log.debug( "Processing XML: " + resource );
+
+ Document doc = builder.build( resource );
+
+ List plugins = XPath.selectNodes( doc, "/modules/editor");
+
+ for( Iterator i = plugins.iterator(); i.hasNext(); )
+ {
+ Element pluginEl = (Element) i.next();
+
+ String name = pluginEl.getAttributeValue("name");
+
+ WikiEditorInfo info = WikiEditorInfo.newInstance(name, pluginEl);
+
+ if( checkCompatibility(info) )
+ {
+ m_editors.put( name, info );
+
+ log.debug("Registered editor "+name);
+ }
+ else
+ {
+ log.info("Editor '"+name+"' not compatible with this version of JSPWiki.");
+ }
+ }
+ }
+ catch( java.io.IOException e )
+ {
+ log.error( "Couldn't load " + PluginManager.PLUGIN_RESOURCE_LOCATION + " resources: " + resource, e );
+ }
+ catch( JDOMException e )
+ {
+ log.error( "Error parsing XML for plugin: "+PluginManager.PLUGIN_RESOURCE_LOCATION );
+ }
+ }
+ }
+ catch( java.io.IOException e )
+ {
+ log.error( "Couldn't load all " + PLUGIN_RESOURCE_LOCATION + " resources", e );
+ }
+ }
+
+ /**
+ * Returns an editor for the current context. The editor names are matched in
+ * a case insensitive manner. At the moment, the only place that this method
+ * looks in is the property file, but in the future this will also look at
+ * user preferences.
+ * <p>
+ * Determines the editor to use by the following order of conditions:
+ * 1. Editor set in User Preferences
+ * 2. Default Editor set in jspwiki.properties
+ * <p>
+ * For the PREVIEW context, this method returns the "preview" editor.
+ *
+ * @param context The context that is chosen.
+ * @return The name of the chosen editor. If no match could be found, will
+ * revert to the default "plain" editor.
+ */
+ public String getEditorName( WikiContext context )
+ {
+ if( context instanceof PreviewActionBean )
+ return EDITOR_PREVIEW;
+
+ String editor = null;
+
+ // User has set an editor in preferences
+ editor = Preferences.getPreference( context, PARA_EDITOR );
+
+ /* FIXME: actual default 'editor' property is read by the Preferences class */
+ if (editor == null)
+ {
+ // or use the default editor in jspwiki.properties
+ try
+ {
+ editor = m_engine.getVariableManager().getValue( context, PROP_EDITORTYPE );
+ }
+ catch( NoSuchVariableException e ) {} // This is fine
+ }
+
+ if (editor != null)
+ {
+ String[] editorlist = getEditorList();
+
+ editor = editor.trim();
+
+ for( int i = 0; i < editorlist.length; i++ )
+ {
+ if( editorlist[i].equalsIgnoreCase(editor))
+ {
+ return editorlist[i];
+ }
+ }
+ }
+
+ return EDITOR_PLAIN;
+ }
+
+ /**
+ * Returns a list of editors as Strings of editor names.
+ *
+ * @return the list of available editors
+ */
+ public String[] getEditorList()
+ {
+ String[] editors = new String[m_editors.size()];
+
+ Set keys = m_editors.keySet();
+
+ return (String[]) keys.toArray( editors );
+ }
+
+ /**
+ * Convenience method for getting the path to the editor JSP file.
+ *
+ * @param context
+ * @return e.g. "editors/plain.jsp"
+ */
+ public String getEditorPath( WikiContext context )
+ {
+ String path = null;
+
+ String editor = getEditorName( context );
+
+ WikiEditorInfo ed = (WikiEditorInfo)m_editors.get( editor );
+
+ if( ed != null )
+ {
+ path = ed.getPath();
+ }
+ else
+ {
+ path = "editors/"+editor+".jsp";
+ }
+
+ return path;
+ }
+
+ /**
+ * Convenience function which examines the current context and attempts to figure
+ * out whether the edited text is in the HTTP request parameters or somewhere in
+ * the session.
+ *
+ * @param ctx the JSP page context
+ * @return the edited text, if present in the session page context or as a parameter
+ */
+ public static String getEditedText( PageContext ctx )
+ {
+ String usertext = ctx.getRequest().getParameter( REQ_EDITEDTEXT );
+
+ if( usertext == null )
+ {
+ usertext = (String)ctx.findAttribute( ATTR_EDITEDTEXT );
+ }
+
+ return usertext;
+ }
+
+ /**
+ * Contains info about an editor.
+ *
+ * @author jalkanen
+ *
+ */
+ private static final class WikiEditorInfo
+ extends WikiModuleInfo
+ {
+ private String m_path;
+
+ protected static WikiEditorInfo newInstance( String name, Element el )
+ {
+ if( name == null || name.length() == 0 ) return null;
+ WikiEditorInfo info = new WikiEditorInfo( name );
+
+ info.initializeFromXML( el );
+ return info;
+ }
+
+ protected void initializeFromXML( Element el )
+ {
+ super.initializeFromXML( el );
+ m_path = el.getChildText("path");
+ }
+
+ private WikiEditorInfo( String name )
+ {
+ super(name);
+ }
+
+ public String getPath()
+ {
+ return m_path;
+ }
+ }
+
+ public Collection modules()
+ {
+ ArrayList ls = new ArrayList();
+
+ ls.addAll( m_editors.values() );
+
+ return ls;
+ }
+
+}
Added: incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/ui/GenericHTTPHandler.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/ui/GenericHTTPHandler.java?rev=627255&view=auto
==============================================================================
--- incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/ui/GenericHTTPHandler.java (added)
+++ incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/ui/GenericHTTPHandler.java Tue Feb 12 21:53:55 2008
@@ -0,0 +1,55 @@
+/*
+ JSPWiki - a JSP-based WikiWiki clone.
+
+ Copyright (C) 2001-2007 JSPWiki development group
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package com.ecyrd.jspwiki.ui;
+
+import com.ecyrd.jspwiki.WikiContext;
+
+/**
+ * Provides a generic HTTP handler interface.
+ *
+ * @author jalkanen
+ *
+ */
+public interface GenericHTTPHandler
+{
+
+ /**
+ * Get an identifier for this particular AdminBean. This id MUST
+ * conform to URI rules. The ID must also be unique across all HTTPHandlers.
+ *
+ * @return
+ */
+ public String getId();
+
+ /**
+ * Return basic HTML.
+ *
+ * @param context
+ * @return
+ */
+ public String doGet( WikiContext context );
+
+ /**
+ * Handles a POST response.
+ * @param context
+ * @return
+ */
+ public String doPost( WikiContext context );
+}
Added: incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/ui/GroupTypeConverter.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/ui/GroupTypeConverter.java?rev=627255&view=auto
==============================================================================
--- incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/ui/GroupTypeConverter.java (added)
+++ incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/ui/GroupTypeConverter.java Tue Feb 12 21:53:55 2008
@@ -0,0 +1,83 @@
+/* Copyright 2005-2006 Tim Fennell
+ *
+ * Licensed 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.
+ */
+package com.ecyrd.jspwiki.ui;
+
+import java.util.Collection;
+import java.util.Locale;
+
+import net.sourceforge.stripes.controller.StripesFilter;
+import net.sourceforge.stripes.validation.LocalizableError;
+import net.sourceforge.stripes.validation.TypeConverter;
+import net.sourceforge.stripes.validation.ValidationError;
+
+import com.ecyrd.jspwiki.WikiEngine;
+import com.ecyrd.jspwiki.auth.NoSuchPrincipalException;
+import com.ecyrd.jspwiki.auth.authorize.Group;
+import com.ecyrd.jspwiki.auth.authorize.GroupManager;
+
+/**
+ * Stripes type converter that converts a Group name, expressed as a String,
+ * into an {@link com.ecyrd.jspwiki.auth.authorize.Group} object. This converter
+ * is looked up and returned by {@link WikiTypeConverterFactory} for HTTP
+ * request parameters that need to be bound to ActionBean properties of type
+ * Group. Stripes executes this TypeConverter during the
+ * {@link net.sourceforge.stripes.controller.LifecycleStage#BindingAndValidation}
+ * stage of request processing.
+ *
+ * @author Andrew Jaquith
+ */
+public class GroupTypeConverter implements TypeConverter<Group>
+{
+ /**
+ * Converts a named wiki group into a valid
+ * {@link com.ecyrd.jspwiki.auth.authorize.Group} object by retrieving it
+ * via the Wiki{@link com.ecyrd.jspwiki.auth.authorize.GroupManager}. If
+ * the group cannot be found (perhaps because it does not exist), this
+ * method will add a validation error to the supplied Collection of errors.
+ * The error will be of type
+ * {@link net.sourceforge.stripes.validation.LocalizableError} and will have
+ * a message key of <code>pageNotFound</code> and a single parameter
+ * (equal to the value passed for <code>groupName</code>).
+ *
+ * @param groupName
+ * the name of the WikiPage to retrieve
+ * @param targetType
+ * the type to return, which will always be of type
+ * {@link com.ecyrd.jspwiki.auth.authorize.Group}
+ * @param errors
+ * the current Collection of validation errors for this field
+ * @return the resolved Group
+ */
+ public Group convert(String groupName, Class<? extends Group> targetType, Collection<ValidationError> errors)
+ {
+ WikiRuntimeConfiguration config = (WikiRuntimeConfiguration)StripesFilter.getConfiguration();
+ WikiEngine engine = config.getEngine();
+ GroupManager mgr = engine.getGroupManager();
+ Group group = null;
+ try
+ {
+ group = mgr.getGroup(groupName);
+ }
+ catch (NoSuchPrincipalException e)
+ {
+ errors.add(new LocalizableError("groupNotFound", groupName));
+ }
+ return group;
+ }
+
+ public void setLocale(Locale locale)
+ {
+ };
+}
Added: incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/ui/InputValidator.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/ui/InputValidator.java?rev=627255&view=auto
==============================================================================
--- incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/ui/InputValidator.java (added)
+++ incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/ui/InputValidator.java Tue Feb 12 21:53:55 2008
@@ -0,0 +1,188 @@
+/*
+ JSPWiki - a JSP-based WikiWiki clone.
+
+ Copyright (C) 2001-2006 Janne Jalkanen (Janne.Jalkanen@iki.fi)
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package com.ecyrd.jspwiki.ui;
+
+import java.text.MessageFormat;
+import java.util.ResourceBundle;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import com.ecyrd.jspwiki.WikiSession;
+import com.ecyrd.jspwiki.i18n.InternationalizationManager;
+
+/**
+ * Provides basic validation services for HTTP parameters. Three standard
+ * validators are provided: email address, identifier and standard input. Standard input
+ * validator will reject any HTML-like input, and any of a number of special
+ * characters. ID validator rejects HTML and quoted strings, and a couple of special characters.
+ * @author Andrew Jaquith
+ * @since 2.3.54
+ */
+public final class InputValidator
+{
+ /** Standard input validator. */
+ public static final int STANDARD = 0;
+
+ /** Input validator for e-mail addresses. **/
+ public static final int EMAIL = 1;
+
+ /**
+ * @since 2.4.82
+ */
+ public static final int ID = 2;
+
+ protected static final Pattern EMAIL_PATTERN = Pattern.compile( "^[0-9a-zA-Z-_\\.\\+]+@([0-9a-zA-Z-_]+\\.)+[a-zA-Z]+$" );
+
+ protected static final Pattern UNSAFE_PATTERN = Pattern.compile( "[\\x00\\r\\n\\x0f\"':<>;&@\\xff{}\\$%\\\\]" );
+
+ /** Used when checking against IDs such as a full name when saving groups.
+ * @since 2.4.82 */
+ protected static final Pattern ID_PATTERN = Pattern.compile( "[\\x00\\r\\n\\x0f\"'<>;&\\xff{}]" );
+
+ private final String m_form;
+
+ private final WikiSession m_session;
+
+ /**
+ * Constructs a new input validator for a specific form and wiki session.
+ * When validation errors are detected, they will be added to the wiki
+ * session's messages.
+ * @param form the ID or name of the form this validator should be
+ * associated with
+ * @param session the wiki session
+ */
+ public InputValidator( String form, WikiSession session )
+ {
+ m_form = form;
+ m_session = session;
+ }
+
+ /**
+ * Validates a string against the {@link #STANDARD} validator and
+ * additionally checks that the value is not <code>null</code> or blank.
+ * @param input the string to validate
+ * @param label the label for the string or field ("E-mail address")
+ * @return returns <code>true</code> if valid, <code>false</code>
+ * otherwise
+ */
+ public final boolean validateNotNull( String input, String label )
+ {
+ return validateNotNull( input, label, STANDARD );
+ }
+
+ /**
+ * Validates a string against a particular pattern type and additionally
+ * checks that the value is not <code>null</code> or blank. Delegates to
+ * {@link #validate(String, String, int)}.
+ * @param input the string to validate
+ * @param label the label for the string or field ("E-mail address")
+ * @param type the pattern type to use (<em>e.g.</em>, {@link #STANDARD, #EMAIL}.
+ * @return returns <code>true</code> if valid, <code>false</code>
+ * otherwise
+ */
+ public final boolean validateNotNull( String input, String label, int type )
+ {
+ if ( isBlank( input ) )
+ {
+ ResourceBundle rb = ResourceBundle.getBundle( InternationalizationManager.CORE_BUNDLE,
+ m_session.getLocale() );
+
+ Object[] args = { label };
+ m_session.addMessage( m_form, MessageFormat.format( rb.getString("validate.cantbenull"),
+ args ) );
+ return false;
+ }
+ return validate( input, label, type ) && !isBlank( input );
+ }
+
+ /**
+ * Validates a string against a particular pattern type: e-mail address,
+ * standard HTML input, etc. Note that a blank or null string will
+ * always validate.
+ * @param input the string to validate
+ * @param label the label for the string or field ("E-mail address")
+ * @param type the target pattern to validate against ({@link #STANDARD},
+ * {@link #EMAIL})
+ * @return returns <code>true</code> if valid, <code>false</code>
+ * otherwise
+ */
+ public final boolean validate( String input, String label, int type )
+ {
+ // If blank, it's valid
+ if ( isBlank( input ) )
+ {
+ return true;
+ }
+
+ ResourceBundle rb = ResourceBundle.getBundle( InternationalizationManager.CORE_BUNDLE,
+ m_session.getLocale() );
+
+ // Otherwise, see if it matches the pattern for the target type
+ Matcher matcher;
+ boolean valid;
+ switch( type )
+ {
+ case STANDARD:
+ matcher = UNSAFE_PATTERN.matcher( input );
+ valid = !matcher.find();
+ if ( !valid )
+ {
+ Object[] args = { label, ""'<>;&\\@{}%$" };
+ m_session.addMessage( m_form, MessageFormat.format( rb.getString("validate.unsafechars"),
+ args ) );
+ }
+ return valid;
+ case EMAIL:
+ matcher = EMAIL_PATTERN.matcher( input );
+ valid = matcher.matches();
+ if ( !valid )
+ {
+ Object[] args = { label };
+ m_session.addMessage( m_form, MessageFormat.format( rb.getString("validate.invalidemail"),
+ args ) );
+ }
+ return valid;
+ case ID:
+ matcher = ID_PATTERN.matcher( input );
+ valid = !matcher.find();
+ if ( !valid )
+ {
+ Object[] args = { label, ""'<>;&{}" };
+ m_session.addMessage( m_form, MessageFormat.format( rb.getString("validate.unsafechars"),
+ args ) );
+ }
+ return valid;
+ default:
+ break;
+ }
+ throw new IllegalArgumentException( "Invalid input type." );
+ }
+
+ /**
+ * Returns <code>true</code> if a supplied string is null or blank
+ * @param input the string to check
+ * @return <code>true</code> if <code>null</code> or blank (zero-length);
+ * <code>false</code> otherwise
+ */
+ public static final boolean isBlank( String input )
+ {
+ return input == null || input.trim().length() < 1;
+ }
+}
Added: incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/ui/Installer.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/ui/Installer.java?rev=627255&view=auto
==============================================================================
--- incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/ui/Installer.java (added)
+++ incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/ui/Installer.java Tue Feb 12 21:53:55 2008
@@ -0,0 +1,361 @@
+/*
+ JSPWiki - a JSP-based WikiWiki clone.
+
+ Copyright (C) 2001-2002 Janne Jalkanen (Janne.Jalkanen@iki.fi)
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package com.ecyrd.jspwiki.ui;
+
+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 javax.servlet.ServletConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+
+import com.ecyrd.jspwiki.*;
+import com.ecyrd.jspwiki.auth.AuthenticationManager;
+import com.ecyrd.jspwiki.auth.NoSuchPrincipalException;
+import com.ecyrd.jspwiki.auth.UserManager;
+import com.ecyrd.jspwiki.auth.WikiPrincipal;
+import com.ecyrd.jspwiki.auth.WikiSecurityException;
+import com.ecyrd.jspwiki.auth.authorize.Group;
+import com.ecyrd.jspwiki.auth.authorize.GroupManager;
+import com.ecyrd.jspwiki.auth.user.UserDatabase;
+import com.ecyrd.jspwiki.auth.user.UserProfile;
+import com.ecyrd.jspwiki.providers.BasicAttachmentProvider;
+import com.ecyrd.jspwiki.providers.FileSystemProvider;
+import com.ecyrd.jspwiki.util.CommentedProperties;
+
+/**
+ * Manages JSPWiki installation on behalf of <code>admin/Install.jsp</code>.
+ * The contents of this class were previously part of <code>Install.jsp</code>.
+ * @author Janne Jalkanen
+ * @since 2.4.20
+ */
+public class Installer
+{
+ public static final String ADMIN_ID = "admin";
+ public static final String ADMIN_NAME = "Administrator";
+ public static final String INSTALL_INFO = "Installer.Info";
+ public static final String INSTALL_WARNING = "Installer.Warning";
+ public static final String INSTALL_ERROR = "Installer.Error";
+ public static final String APP_NAME = WikiEngine.PROP_APPNAME;
+ public static final String BASE_URL = WikiEngine.PROP_BASEURL;
+ public static final String STORAGE_DIR = BasicAttachmentProvider.PROP_STORAGEDIR;
+ public static final String LOG_DIR = "log4j.appender.FileLog.File";
+ public static final String PAGE_DIR = FileSystemProvider.PROP_PAGEDIR;
+ public static final String WORK_DIR = WikiEngine.PROP_WORKDIR;
+ public static final String ADMIN_GROUP = "Admin";
+ private final WikiSession m_session;
+ private final File m_propertyFile;
+ private final Properties m_props;
+ private final WikiEngine m_engine;
+ private HttpServletRequest m_request;
+ private boolean m_validated;
+
+ public Installer( HttpServletRequest request, ServletConfig config )
+ {
+ // Get wiki session for this user
+ m_engine = WikiEngine.getInstance( config );
+ m_session = WikiSession.getWikiSession( m_engine, request );
+
+ // Get the servlet context, and file for properties
+ ServletContext context = config.getServletContext();
+ String path = context.getRealPath("/");
+ m_propertyFile = new File( path, PropertyReader.DEFAULT_PROPERTYFILE );
+ m_props = new CommentedProperties();
+
+ // Stash the request
+ m_request = request;
+ m_validated = false;
+ }
+
+ /**
+ * Returns <code>true</code> if the administrative user had
+ * been created previously.
+ * @return the result
+ */
+ public boolean adminExists()
+ {
+ // See if the admin user exists already
+ UserManager userMgr = m_engine.getUserManager();
+ UserDatabase userDb = userMgr.getUserDatabase();
+
+ try
+ {
+ userDb.findByLoginName( ADMIN_ID );
+ return true;
+ }
+ catch ( NoSuchPrincipalException e )
+ {
+ return false;
+ }
+ }
+
+ /**
+ * Creates an adminstrative user and returns the new password.
+ * If the admin user exists, the password will be <code>null</code>.
+ * @return the password
+ * @throws WikiSecurityException
+ */
+ public String createAdministrator() throws WikiSecurityException
+ {
+ if ( !m_validated )
+ {
+ throw new WikiSecurityException( "Cannot create administrator because one or more of the installation settings are invalid." );
+ }
+
+ if ( adminExists() )
+ {
+ return null;
+ }
+
+ // See if the admin user exists already
+ UserManager userMgr = m_engine.getUserManager();
+ UserDatabase userDb = userMgr.getUserDatabase();
+ String password = null;
+
+ try
+ {
+ userDb.findByLoginName( ADMIN_ID );
+ }
+ catch ( NoSuchPrincipalException e )
+ {
+ // Create a random 12-character password
+ password = TextUtil.generateRandomPassword();
+ UserProfile profile = userDb.newProfile();
+ profile.setLoginName( ADMIN_ID );
+ profile.setFullname( ADMIN_NAME );
+ profile.setPassword( password );
+ userDb.save( profile );
+ }
+
+ // Create a new admin group
+ GroupManager groupMgr = m_engine.getGroupManager();
+ Group group = null;
+ try
+ {
+ group = groupMgr.getGroup( ADMIN_GROUP );
+ group.add( new WikiPrincipal( ADMIN_NAME ) );
+ }
+ catch ( NoSuchPrincipalException e )
+ {
+ group = groupMgr.parseGroup( ADMIN_GROUP, ADMIN_NAME, true );
+ }
+ groupMgr.setGroup( m_session, group );
+
+ return password;
+ }
+
+ /**
+ * Returns the properties file as a string
+ * @return the string
+ */
+ public String getProperties()
+ {
+ return m_props.toString();
+ }
+
+ public String getPropertiesPath()
+ {
+ return m_propertyFile.getAbsolutePath();
+ }
+
+ /**
+ * Returns a property from the WikiEngine's properties.
+ * @param key the property key
+ * @return the property value
+ */
+ public String getProperty( String key )
+ {
+ return m_props.getProperty( key );
+ }
+
+ public void parseProperties () throws Exception
+ {
+ m_validated = false;
+
+ // Set request encoding
+ m_request.setCharacterEncoding("UTF-8");
+
+ try
+ {
+ InputStream in = null;
+ try
+ {
+ // Load old properties from disk
+ in = new FileInputStream( m_propertyFile );
+ m_props.load( in );
+ }
+ finally
+ {
+ if( in != null )
+ {
+ in.close();
+ }
+ }
+ }
+ catch( IOException e )
+ {
+ m_session.addMessage( INSTALL_ERROR,
+ "Unable to read properties: " +
+ e.getMessage() );
+ }
+
+ // Get application name
+ String nullValue = m_props.getProperty( APP_NAME, "MyWiki" );
+ parseProperty( APP_NAME, nullValue );
+
+ // Get/sanitize base URL
+ nullValue = m_request.getRequestURL().toString();
+ nullValue = nullValue.substring( 0, nullValue.lastIndexOf('/') )+"/";
+ nullValue = m_props.getProperty( BASE_URL, nullValue );
+ parseProperty( BASE_URL, nullValue );
+ sanitizeURL( BASE_URL );
+
+ // Get/sanitize page directory
+ nullValue = m_props.getProperty( PAGE_DIR, "Please configure me!" );
+ parseProperty( PAGE_DIR, nullValue );
+ sanitizePath( PAGE_DIR );
+
+ // Get/sanitize log directory
+ nullValue = m_props.getProperty( LOG_DIR, "/tmp/" );
+ parseProperty( LOG_DIR, nullValue );
+ sanitizePath( LOG_DIR );
+
+ // Get/sanitize work directory
+ nullValue = m_props.getProperty( WORK_DIR, "/tmp/" );
+ parseProperty( WORK_DIR, nullValue );
+ sanitizePath( WORK_DIR );
+
+ // Get/sanitize security property
+ nullValue = m_props.getProperty( AuthenticationManager.PROP_SECURITY, AuthenticationManager.SECURITY_JAAS );
+ parseProperty( AuthenticationManager.PROP_SECURITY, nullValue );
+
+ // Set a few more default properties, for easy setup
+ m_props.setProperty( STORAGE_DIR, m_props.getProperty( PAGE_DIR ) );
+ m_props.setProperty( PageManager.PROP_PAGEPROVIDER, "VersioningFileProvider" );
+ m_props.setProperty( WikiEngine.PROP_ENCODING, "UTF-8" );
+ }
+
+ public void saveProperties()
+ {
+ // Write the file back to disk
+ try
+ {
+ OutputStream out = null;
+ try
+ {
+ out = new FileOutputStream( m_propertyFile );
+ m_props.store( out, null );
+ }
+ finally
+ {
+ if ( out != null )
+ {
+ out.close();
+ }
+ }
+ m_session.addMessage( INSTALL_INFO,
+ "Your new properties have been saved. Please restart your container (unless this was your first install). Scroll down a bit to see your new jspwiki.properties." );
+ }
+ catch( IOException e )
+ {
+ m_session.addMessage( INSTALL_ERROR,
+ "Unable to write properties: " +
+ e.getMessage() +
+ ". Please copy the file below as your jspwiki.properties:\n" +
+ m_props.toString() );
+ }
+ }
+
+ public boolean validateProperties() throws Exception
+ {
+ m_session.clearMessages( INSTALL_ERROR );
+ parseProperties();
+ validateNotNull( BASE_URL, "You must define the base URL for this wiki." );
+ validateNotNull( PAGE_DIR, "You must define the location where the files are stored." );
+ validateNotNull( APP_NAME, "You must define the application name." );
+ validateNotNull( WORK_DIR, "You must define a work directory." );
+ validateNotNull( LOG_DIR, "You must define a log directory." );
+
+ if ( m_session.getMessages( INSTALL_ERROR ).length == 0 )
+ {
+ m_validated = true;
+ }
+ return m_validated;
+ }
+
+ /**
+ * Sets a property based on the value of an HTTP request parameter.
+ * If the parameter is not found, a default value is used instead.
+ * @param request the HTTP request
+ * @param param the parameter containing the value we will extract
+ * @param defaultValue the default to use if the parameter was not passed
+ * in the request
+ */
+ private void parseProperty( String param, String defaultValue )
+ {
+ String value = m_request.getParameter( param );
+ if ( value == null )
+ {
+ value = defaultValue;
+ }
+ m_props.put( param, value );
+ }
+
+ /**
+ * Simply sanitizes any path which contains backslashes (sometimes Windows
+ * users may have them) by expanding them to double-backslashes
+ * @param s the key of the property to sanitize
+ */
+ private void sanitizePath( String key )
+ {
+ String s = m_props.getProperty( key );
+ s = TextUtil.replaceString(s, "\\", "\\\\" );
+ s = s.trim();
+ m_props.put( key, s );
+ }
+
+ /**
+ * Simply sanitizes any URL which contains backslashes (sometimes Windows
+ * users may have them)
+ * @param s the key of the property to sanitize
+ */
+ private void sanitizeURL( String key )
+ {
+ String s = m_props.getProperty( key );
+ s = TextUtil.replaceString( s, "\\", "/" );
+ s = s.trim();
+ m_props.put( key, s );
+ }
+
+ private void validateNotNull( String key, String message )
+ {
+ String value = m_props.getProperty( key );
+ if ( value == null || value.length() == 0 )
+ {
+ m_session.addMessage( INSTALL_ERROR, message );
+ }
+ }
+
+}
Added: incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/ui/PrincipalTypeConverter.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/ui/PrincipalTypeConverter.java?rev=627255&view=auto
==============================================================================
--- incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/ui/PrincipalTypeConverter.java (added)
+++ incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/ui/PrincipalTypeConverter.java Tue Feb 12 21:53:55 2008
@@ -0,0 +1,62 @@
+/* Copyright 2005-2006 Tim Fennell
+ *
+ * Licensed 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.
+ */
+package com.ecyrd.jspwiki.ui;
+
+import java.security.Principal;
+import java.util.Collection;
+import java.util.Locale;
+
+import net.sourceforge.stripes.validation.TypeConverter;
+import net.sourceforge.stripes.validation.ValidationError;
+
+import com.ecyrd.jspwiki.auth.WikiPrincipal;
+
+/**
+ * Stripes type converter that converts a Principal name, expressed as a String, into an
+ * {@link com.ecyrd.jspwiki.auth.WikiPrincipal} object. This converter is looked up
+ * and returned by {@link WikiTypeConverterFactory} for HTTP request parameters
+ * that need to be bound to ActionBean properties of type Principal. Stripes
+ * executes this TypeConverter during the
+ * {@link net.sourceforge.stripes.controller.LifecycleStage#BindingAndValidation}
+ * stage of request processing.
+ *
+ * @author Andrew Jaquith
+ */
+public class PrincipalTypeConverter implements TypeConverter<Principal>
+{
+
+ /**
+ * Converts a named user, passed as a String, into a valid
+ * {@link com.ecyrd.jspwiki.auth.WikiPrincipal} object. This method will not
+ * ever return errors.
+ *
+ * @param principalName
+ * the name of the Principal to create
+ * @param targetType
+ * the type to return, which will always be of type
+ * {@link java.security.Principal}
+ * @param errors
+ * the current Collection of validation errors for this field
+ * @return the
+ */
+ public Principal convert(String principalName, Class<? extends Principal> targetType, Collection<ValidationError> errors)
+ {
+ return new WikiPrincipal(principalName);
+ }
+
+ public void setLocale(Locale locale)
+ {
+ };
+}
Added: incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/ui/TemplateManager.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/ui/TemplateManager.java?rev=627255&view=auto
==============================================================================
--- incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/ui/TemplateManager.java (added)
+++ incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/ui/TemplateManager.java Tue Feb 12 21:53:55 2008
@@ -0,0 +1,607 @@
+/*
+ JSPWiki - a JSP-based WikiWiki clone.
+
+ Copyright (C) 2003-2006 Janne Jalkanen (Janne.Jalkanen@iki.fi)
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package com.ecyrd.jspwiki.ui;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.*;
+
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.jsp.PageContext;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.log4j.Logger;
+
+import com.ecyrd.jspwiki.InternalWikiException;
+import com.ecyrd.jspwiki.WikiContext;
+import com.ecyrd.jspwiki.WikiEngine;
+import com.ecyrd.jspwiki.action.WikiActionBean;
+import com.ecyrd.jspwiki.modules.ModuleManager;
+
+/**
+ * This class takes care of managing JSPWiki templates. This class also provides
+ * the ResourceRequest mechanism.
+ *
+ * @since 2.1.62
+ * @author Janne Jalkanen
+ */
+public class TemplateManager
+ extends ModuleManager
+{
+ private static final String SKIN_DIRECTORY = "skins";
+
+ /**
+ * Requests a JavaScript function to be called during window.onload. Value is {@value}.
+ */
+ public static final String RESOURCE_JSFUNCTION = "jsfunction";
+
+ /**
+ * Requests a JavaScript associative array with all localized strings.
+ */
+ public static final String RESOURCE_JSLOCALIZEDSTRINGS = "jslocalizedstrings";
+
+ /**
+ * Requests a stylesheet to be inserted. Value is {@value}.
+ */
+ public static final String RESOURCE_STYLESHEET = "stylesheet";
+
+ /**
+ * Requests a script to be loaded. Value is {@value}.
+ */
+ public static final String RESOURCE_SCRIPT = "script";
+
+ /**
+ * Requests inlined CSS. Value is {@value}.
+ */
+ public static final String RESOURCE_INLINECSS = "inlinecss";
+
+ /** The default directory for the properties. Value is {@value}. */
+ public static final String DIRECTORY = "templates";
+
+ /** The name of the default template. Value is {@value}. */
+ public static final String DEFAULT_TEMPLATE = "default";
+
+ /** Name of the file that contains the properties.*/
+
+ public static final String PROPERTYFILE = "template.properties";
+
+ /** The name under which the resource includes map is stored in the WikiContext. */
+ public static final String RESOURCE_INCLUDES = "jspwiki.resourceincludes";
+
+ // private Cache m_propertyCache;
+
+ protected static final Logger log = Logger.getLogger( TemplateManager.class );
+
+ /** Requests a HTTP header. Value is {@value}. */
+ public static final String RESOURCE_HTTPHEADER = "httpheader";
+
+ /**
+ * Creates a new TemplateManager. There is typically one manager per engine.
+ *
+ * @param engine The owning engine.
+ * @param properties The property list used to initialize this.
+ */
+ public TemplateManager( WikiEngine engine, Properties properties )
+ {
+ super(engine);
+
+ //
+ // Uses the unlimited cache.
+ //
+ // m_propertyCache = new Cache( true, false );
+ }
+
+ /**
+ * Check the existence of a template.
+ */
+ // FIXME: Does not work yet
+ public boolean templateExists( String templateName )
+ {
+ ServletContext context = m_engine.getServletContext();
+
+ InputStream in = context.getResourceAsStream( getPath(templateName)+"ViewTemplate.jsp");
+
+ if( in != null )
+ {
+ try
+ {
+ in.close();
+ }
+ catch( IOException e ) {}
+
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Tries to locate a given resource from the template directory. If the
+ * given resource is not found under the current name, returns the
+ * path to the corresponding one in the default template.
+ *
+ * @param sContext The servlet context
+ * @param name The name of the resource
+ * @return The name of the resource which was found.
+ */
+ private static String findResource( ServletContext sContext, String name )
+ {
+ InputStream is = sContext.getResourceAsStream( name );
+
+ if( is == null )
+ {
+ String defname = makeFullJSPName( DEFAULT_TEMPLATE,
+ removeTemplatePart(name) );
+ is = sContext.getResourceAsStream( defname );
+
+ if( is != null )
+ name = defname;
+ else
+ name = null;
+ }
+
+ if( is != null )
+ {
+ try
+ {
+ is.close();
+ }
+ catch( IOException e )
+ {}
+ }
+
+ return name;
+ }
+
+ /**
+ * Attempts to find a resource from the given template, and if it's not found
+ * attempts to locate it from the default template.
+ * @param sContext
+ * @param template
+ * @param name
+ * @return
+ */
+ private static String findResource( ServletContext sContext, String template, String name )
+ {
+ if( name.charAt(0) == '/' )
+ {
+ // This is already a full path
+ return findResource( sContext, name );
+ }
+
+ String fullname = makeFullJSPName( template, name );
+
+ return findResource( sContext, fullname );
+ }
+
+ /**
+ * An utility method for finding a JSP page. It searches only under
+ * either current context or by the absolute name.
+ *
+ * @param pageContext the JSP PageContext
+ * @param name The name of the JSP page to look for (e.g "Wiki.jsp")
+ * @return The context path to the resource
+ */
+ public String findJSP( PageContext pageContext, String name )
+ {
+ ServletContext sContext = pageContext.getServletContext();
+
+ return findResource( sContext, name );
+ }
+
+ /**
+ * Removes the template part of a name.
+ */
+ private static final String removeTemplatePart( String name )
+ {
+ int idx = name.indexOf('/');
+ if( idx != -1 )
+ {
+ idx = name.indexOf('/', idx); // Find second "/"
+
+ if( idx != -1 )
+ {
+ return name.substring( idx+1 );
+ }
+ }
+
+ return name;
+ }
+
+ /**
+ * Returns the full name (/templates/foo/bar) for name=bar, template=foo.
+ *
+ * @param template
+ * @param name
+ * @return
+ */
+ private static final String makeFullJSPName( String template, String name )
+ {
+ return "/"+DIRECTORY+"/"+template+"/"+name;
+ }
+
+ /**
+ * Attempts to locate a resource under the given template. If that template
+ * does not exist, or the page does not exist under that template, will
+ * attempt to locate a similarly named file under the default template.
+ * <p>
+ * Even though the name suggests only JSP files can be located, but in fact
+ * this method can find also other resources than JSP files.
+ *
+ * @param pageContext The JSP PageContext
+ * @param template From which template we should seek initially?
+ * @param name Which resource are we looking for (e.g. "ViewTemplate.jsp")
+ * @return path to the JSP page; null, if it was not found.
+ */
+ public String findJSP( PageContext pageContext, String template, String name )
+ {
+ if( name == null || template == null )
+ {
+ log.fatal("findJSP() was asked to find a null template or name ("+template+","+name+")."+
+ " JSP page '"+
+ ((HttpServletRequest)pageContext.getRequest()).getRequestURI()+"'");
+ throw new InternalWikiException("Illegal arguments to findJSP(); please check logs.");
+ }
+
+ return findResource( pageContext.getServletContext(), template, name );
+ }
+
+ /**
+ * Attempts to locate a resource under the given template. This matches the
+ * functionality findJSP(), but uses the WikiContext as the argument. If there
+ * is no servlet context (i.e. this is embedded), will just simply return
+ * a best-guess.
+ * <p>
+ * This method is typically used to locate any resource, including JSP pages, images,
+ * scripts, etc.
+ *
+ * @since 2.6
+ * @param ctx the wiki context
+ * @param template the name of the template to use
+ * @param name the name of the resource to fine
+ * @return the path to the resource
+ */
+ public String findResource( WikiContext ctx, String template, String name )
+ {
+ if( m_engine.getServletContext() != null )
+ {
+ return findResource( m_engine.getServletContext(), template, name );
+ }
+
+ return getPath(template)+"/"+name;
+ }
+
+ /**
+ * Returns a property, as defined in the template. The evaluation
+ * is lazy, i.e. the properties are not loaded until the template is
+ * actually used for the first time.
+ */
+ /*
+ public String getTemplateProperty( WikiContext context, String key )
+ {
+ String template = context.getTemplate();
+
+ try
+ {
+ Properties props = (Properties)m_propertyCache.getFromCache( template, -1 );
+
+ if( props == null )
+ {
+ try
+ {
+ props = getTemplateProperties( template );
+
+ m_propertyCache.putInCache( template, props );
+ }
+ catch( IOException e )
+ {
+ log.warn("IO Exception while reading template properties",e);
+
+ return null;
+ }
+ }
+
+ return props.getProperty( key );
+ }
+ catch( NeedsRefreshException ex )
+ {
+ // FIXME
+ return null;
+ }
+ }
+*/
+ /**
+ * Returns an absolute path to a given template.
+ */
+ private static final String getPath( String template )
+ {
+ return "/"+DIRECTORY+"/"+template+"/";
+ }
+
+ /**
+ * Lists the skins available under this template. Returns an
+ * empty Set, if there are no extra skins available. Note that
+ * this method does not check whether there is anything actually
+ * in the directories, it just lists them. This may change
+ * in the future.
+ *
+ * @param pageContext the JSP PageContext
+ * @param template The template to search
+ * @return Set of Strings with the skin names.
+ * @since 2.3.26
+ */
+ public Set listSkins( PageContext pageContext, String template )
+ {
+ String place = makeFullJSPName( template, SKIN_DIRECTORY );
+
+ ServletContext sContext = pageContext.getServletContext();
+
+ Set skinSet = sContext.getResourcePaths( place );
+ TreeSet resultSet = new TreeSet();
+
+ if( log.isDebugEnabled() ) log.debug( "Listings skins from "+place );
+
+ if( skinSet != null )
+ {
+ String[] skins = {};
+
+ skins = (String[]) skinSet.toArray(skins);
+
+ for( int i = 0; i < skins.length; i++ )
+ {
+ String[] s = StringUtils.split(skins[i],"/");
+
+ if( s.length > 2 && skins[i].endsWith("/") )
+ {
+ String skinName = s[s.length-1];
+ resultSet.add( skinName );
+ if( log.isDebugEnabled() ) log.debug("...adding skin '"+skinName+"'");
+ }
+ }
+ }
+
+ return resultSet;
+ }
+
+ /**
+ * Always returns a valid property map.
+ */
+ /*
+ private Properties getTemplateProperties( String templateName )
+ throws IOException
+ {
+ Properties p = new Properties();
+
+ ServletContext context = m_engine.getServletContext();
+
+ InputStream propertyStream = context.getResourceAsStream(getPath(templateName)+PROPERTYFILE);
+
+ if( propertyStream != null )
+ {
+ p.load( propertyStream );
+
+ propertyStream.close();
+ }
+ else
+ {
+ log.debug("Template '"+templateName+"' does not have a propertyfile '"+PROPERTYFILE+"'.");
+ }
+
+ return p;
+ }
+*/
+ /**
+ * Returns the include resources marker for a given type. This is in a
+ * HTML or Javascript comment format.
+ *
+ * @param wiki context
+ * @param type the marker
+ * @return the generated marker comment
+ */
+ public static String getMarker(WikiActionBean actionBean, String type )
+ {
+ if( type.equals(RESOURCE_JSLOCALIZEDSTRINGS) )
+ {
+ return getJSLocalizedStrings( actionBean );
+ }
+ else if( type.equals(RESOURCE_JSFUNCTION) )
+ {
+ return "/* INCLUDERESOURCES ("+type+") */";
+ }
+ return "<!-- INCLUDERESOURCES ("+type+") -->";
+ }
+
+ /**
+ * Extract all i18n strings in the javascript domain. (javascript.*)
+ * Returns a javascript snippet which defines the LoacalizedStings array.
+ *
+ * @param wiki context
+ * @return Javascript snippet which defines the LocaliedStrings array
+ * @author Dirk Frederickx
+ * @since 2.5.108
+ */
+ private static String getJSLocalizedStrings( WikiActionBean actionBean )
+ {
+ StringBuffer sb = new StringBuffer();
+
+ sb.append( "var LocalizedStrings = {\n");
+
+ ResourceBundle rb = actionBean.getBundle("templates.default");
+
+ boolean first = true;
+
+ for( Enumeration en = rb.getKeys(); en.hasMoreElements(); )
+ {
+ String key = (String)en.nextElement();
+
+ if( key.startsWith("javascript") )
+ {
+ if( first )
+ {
+ first = false;
+ }
+ else
+ {
+ sb.append( ",\n" );
+ }
+ sb.append( "\""+key+"\":\""+rb.getString(key)+"\"" );
+ }
+ }
+ sb.append("\n};\n");
+
+ return( sb.toString() );
+ }
+
+ /**
+ * Adds a resource request to the current request context.
+ * The content will be added at the resource-type marker
+ * (see IncludeResourcesTag) in WikiJSPFilter.
+ * <p>
+ * The resources can be of different types. For RESOURCE_SCRIPT and RESOURCE_STYLESHEET
+ * this is an URI path to the resource (a script file or an external stylesheet)
+ * that needs to be included. For RESOURCE_INLINECSS
+ * the resource should be something that can be added between <style></style> in the
+ * header file (commonheader.jsp). For RESOURCE_JSFUNCTION it is the name of the Javascript
+ * function that should be run at page load.
+ * <p>
+ * The IncludeResourceTag inserts code in the template files, which is then filled
+ * by the WikiFilter after the request has been rendered but not yet sent to the recipient.
+ * <p>
+ * Note that ALL resource requests get rendered, so this method does not check if
+ * the request already exists in the resources. Therefore, if you have a plugin which
+ * makes a new resource request every time, you'll end up with multiple resource requests
+ * rendered. It's thus a good idea to make this request only once during the page
+ * life cycle.
+ *
+ * @param ctx The current wiki context
+ * @param type What kind of a request should be added?
+ * @param resource The resource to add.
+ */
+ public static void addResourceRequest( WikiActionBean actionBean, String type, String resource )
+ {
+ HashMap resourcemap = (HashMap) actionBean.getVariable( RESOURCE_INCLUDES );
+
+ if( resourcemap == null )
+ {
+ resourcemap = new HashMap();
+ }
+
+ Vector resources = (Vector) resourcemap.get( type );
+
+ if( resources == null )
+ {
+ resources = new Vector();
+ }
+
+ String resourceString = null;
+
+ if( type.equals(RESOURCE_SCRIPT) )
+ {
+ resourceString = "<script type='text/javascript' src='"+resource+"'></script>";
+ }
+ else if( type.equals(RESOURCE_STYLESHEET) )
+ {
+ resourceString = "<link rel='stylesheet' type='text/css' href='"+resource+"' />";
+ }
+ else if( type.equals(RESOURCE_INLINECSS) )
+ {
+ resourceString = "<style type='text/css'>\n"+resource+"\n</style>\n";
+ }
+ else if( type.equals(RESOURCE_JSFUNCTION) )
+ {
+ resourceString = resource;
+ }
+ else if( type.equals(RESOURCE_HTTPHEADER) )
+ {
+ resourceString = resource;
+ }
+
+ if( resourceString != null )
+ {
+ resources.add( resourceString );
+ }
+
+ log.debug("Request to add a resource: "+resourceString);
+
+ resourcemap.put( type, resources );
+ actionBean.setVariable( RESOURCE_INCLUDES, resourcemap );
+ }
+
+ /**
+ * Returns resource requests for a particular type. If there are no resources,
+ * returns an empty array.
+ *
+ * @param ctx WikiContext
+ * @param type The resource request type
+ * @return a String array for the resource requests
+ */
+
+ public static String[] getResourceRequests( WikiActionBean actionBean, String type )
+ {
+ HashMap hm = (HashMap) actionBean.getVariable( RESOURCE_INCLUDES );
+
+ if( hm == null ) return new String[0];
+
+ Vector resources = (Vector) hm.get( type );
+
+ if( resources == null ) return new String[0];
+
+ String[] res = new String[resources.size()];
+
+ return (String[]) resources.toArray( res );
+ }
+
+ /**
+ * Returns all those types that have been requested so far.
+ *
+ * @param ctx the wiki context
+ * @return the array of types requested
+ */
+ public static String[] getResourceTypes( WikiActionBean actionBean )
+ {
+ String[] res = new String[0];
+
+ if( actionBean != null )
+ {
+ HashMap hm = (HashMap) actionBean.getVariable( RESOURCE_INCLUDES );
+
+ if( hm != null )
+ {
+ Set keys = hm.keySet();
+
+ res = (String[]) keys.toArray( res );
+ }
+ }
+
+ return res;
+ }
+
+ /**
+ * Returns an empty collection, since at the moment the TemplateManager
+ * does not manage any modules.
+ *
+ * @return {@inheritDoc}
+ */
+ public Collection modules()
+ {
+ return new ArrayList();
+ }
+}
Added: incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/ui/WikiExceptionHandler.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/ui/WikiExceptionHandler.java?rev=627255&view=auto
==============================================================================
--- incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/ui/WikiExceptionHandler.java (added)
+++ incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/ui/WikiExceptionHandler.java Tue Feb 12 21:53:55 2008
@@ -0,0 +1,18 @@
+package com.ecyrd.jspwiki.ui;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import net.sourceforge.stripes.action.Resolution;
+import net.sourceforge.stripes.exception.AutoExceptionHandler;
+
+public class WikiExceptionHandler implements AutoExceptionHandler
+{
+
+ public Resolution handle(Throwable exception, HttpServletRequest req, HttpServletResponse res)
+ {
+ exception.printStackTrace();
+ return null;
+ }
+
+}
Added: incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/ui/WikiInterceptor.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/ui/WikiInterceptor.java?rev=627255&view=auto
==============================================================================
--- incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/ui/WikiInterceptor.java (added)
+++ incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/ui/WikiInterceptor.java Tue Feb 12 21:53:55 2008
@@ -0,0 +1,272 @@
+package com.ecyrd.jspwiki.ui;
+
+import java.lang.reflect.Method;
+import java.security.Permission;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.jsp.PageContext;
+
+import net.sourceforge.stripes.action.RedirectResolution;
+import net.sourceforge.stripes.action.Resolution;
+import net.sourceforge.stripes.controller.*;
+
+import org.apache.log4j.Logger;
+
+import com.ecyrd.jspwiki.WikiEngine;
+import com.ecyrd.jspwiki.WikiSession;
+import com.ecyrd.jspwiki.action.*;
+import com.ecyrd.jspwiki.auth.AuthorizationManager;
+import com.ecyrd.jspwiki.auth.SessionMonitor;
+
+/**
+ * <p>
+ * Stripes {@link net.sourceforge.stripes.controller.Interceptor} that
+ * instantiates the correct WikiContext associated with JSPs, checks for access,
+ * and redirects users if necessary. The interceptor executes during two stages:
+ * after the second lifecycle stage, <em>aka</em>
+ * {@link net.sourceforge.stripes.controller.LifecycleStage#HandlerResolution},
+ * and around (before and after) the third stage,
+ * {@link net.sourceforge.stripes.controller.LifecycleStage#BindingAndValidation}.
+ * </p>
+ * <p>
+ * WikiInterceptor assumes primary responsibility for making JSPWiki objects
+ * available to JSPs as variables. In particular, when WikiInterceptor fires
+ * during the binding and validation stage, sets the following PageContext
+ * attributes, all in {@link PageContext#REQUEST_SCOPE}:
+ * </p>
+ * <ul>
+ * <li><code>wikiEngine</code> - the {@link com.ecyrd.jspwiki.WikiEngine}</li>
+ * <li><code>wikiSession</code> - the user's
+ * {@link com.ecyrd.jspwiki.WikiSession}</li>
+ * <li><code>wikiActionBean</code> - the
+ * {@link com.ecyrd.jspwiki.action.WikiActionBean} injected by Stripes</li>
+ * <li><code>wikiPage</code> - the {@link com.ecyrd.jspwiki.WikiPage}
+ * associated with the WikiActionBean, or the "front page" if the WikiActionBean
+ * is not a WikiContext</li>
+ * </ul>
+ * <p>
+ * After the intercept method fires, calling classes can obtain the saved
+ * WikiActionBean by calling {@link WikiActionBeanFactory#findActionBean(PageContext)}. This is,
+ * indeed, the recommended method for JSP scriptlet code.
+ * </p>
+ * <p>
+ * Because these objects are saved as attributes, they are available to JSPs as
+ * the Expression Language variables <code>${wikiEngine}</code>,
+ * <code>${wikiSession}</code>, <code>${wikiActionBean}</code> and
+ * <code>${wikiPage}</code>.
+ * </p>
+ *
+ * @author Andrew Jaquith
+ */
+@Intercepts( { LifecycleStage.HandlerResolution, LifecycleStage.BindingAndValidation })
+public class WikiInterceptor implements Interceptor
+{
+ /**
+ * The PageContext attribute name of the WikiActionBean stored by
+ * WikiInterceptor.
+ */
+ public static final String ATTR_ACTIONBEAN = "wikiActionBean";
+
+ /** The PageContext attribute name of the WikiPage stored by WikiInterceptor. */
+ public static final String ATTR_WIKIPAGE = "wikiPage";
+
+ /**
+ * The PageContext attribute name of the WikiEngine stored by
+ * WikiInterceptor.
+ */
+ public static final String ATTR_WIKIENGINE = "wikiEngine";
+
+ /**
+ * The PageContext attribute name of the WikiSession stored by
+ * WikiInterceptor.
+ */
+ public static final String ATTR_WIKISESSION = "wikiSession";
+
+ private static final Logger log = Logger.getLogger(WikiInterceptor.class);
+
+ /**
+ * Facade method that forwards execution to
+ * {@link #interceptHandlerResolution(ExecutionContext)} or
+ * {@link #interceptBindingAndValidation(ExecutionContext)}, depending on
+ * the Stripes {@link net.sourceforge.stripes.controller.LifecycleStage}.
+ *
+ * @param context
+ * the current Stripes execution context
+ * @return a Resolution if the delgate intercept method returns one;
+ * otherwise, <code>null</code>
+ */
+ public Resolution intercept(ExecutionContext context) throws Exception
+ {
+ switch (context.getLifecycleStage())
+ {
+ case HandlerResolution: {
+ return interceptHandlerResolution(context);
+ }
+ case BindingAndValidation: {
+ return interceptBindingAndValidation(context);
+ }
+ default:
+ return null;
+ }
+ }
+
+ /**
+ * <p>
+ * Intercepts the
+ * {@link net.sourceforge.stripes.controller.LifecycleStage#BindingAndValidation}
+ * lifecycle stage and checks for proper access to the current ActionBean
+ * and target event. The access-checking logic runs after after the rest of
+ * the BindingAndValidation processing logic does, after which point Stripes
+ * has already discovered the correct ActionBean, and bound and validated
+ * its request parameters.
+ * </p>
+ * <p>
+ * To determine if the user is allowed to access the target event method,
+ * the method is examined to see if contains a
+ * {@link com.ecyrd.jspwiki.action.EventPermission}) annotation that
+ * specifies the required {@link java.security.Permission}. If the user
+ * does not possess the Permission -- that is,
+ * {@link com.ecyrd.jspwiki.auth.AuthorizationManager#checkPermission(WikiSession, Permission)}
+ * returns <code>false</code> -- this method returns a RedirectResolution
+ * to the login page, with all current parameters appended.
+ * </p>
+ * <p>
+ * If access is allowed, this method injects the WikiEngine, WikiSession,
+ * resolved WikiActionBean and WikiPage as request-scoped PageContext
+ * attributes, and returns a <code>null</code>. After the objects are
+ * injected, downstream classes like WikiTagBase can use them. The attribute
+ * can also be accessed as variables using the JSP Expression Language
+ * (example: <code>${wikiPage}</code>).
+ *
+ * @param context
+ * the current execution context
+ * @return a Resolution if the
+ * {@link net.sourceforge.stripes.controller.LifecycleStage#HandlerResolution}
+ * lifecycle stage's normal execution returns one; <code>null</code>
+ * otherwise
+ * @throws Exception
+ * if the underlying lifcycle stage's execution throws an
+ * Exception
+ */
+ protected Resolution interceptBindingAndValidation(ExecutionContext context) throws Exception
+ {
+ // Did the handler resolution stage return a Resolution? If so, bail.
+ Resolution r = context.proceed();
+ if (r != null)
+ {
+ return r;
+ }
+
+ // Get the resolved ActionBean and event handler method
+ WikiActionBean actionBean = (WikiActionBean) context.getActionBean();
+ WikiActionBeanContext beanContext = actionBean.getContext();
+ Method handler = context.getHandler();
+
+ // Does the event handler have a required permission?
+ boolean allowed = true;
+ EventPermissionInfo permInfo = beanContext.getPermissionInfo(handler);
+ if (permInfo != null)
+ {
+ Permission requiredPermission = permInfo.getPermission(actionBean);
+ if (requiredPermission != null)
+ {
+ WikiEngine engine = actionBean.getEngine();
+ AuthorizationManager mgr = engine.getAuthorizationManager();
+ WikiSession wikiSession = actionBean.getWikiSession();
+ allowed = mgr.checkPermission(wikiSession, requiredPermission);
+ }
+ }
+
+ // If not allowed, redirect to login page with all parameters intact;
+ // otherwise proceed
+ if (!allowed)
+ {
+ r = new RedirectResolution(LoginActionBean.class);
+ ((RedirectResolution) r).includeRequestParameters(true);
+ if (log.isDebugEnabled())
+ {
+ log.debug("WikiInterceptor rejected access to ActionBean: " + actionBean + ", method " + handler.getName());
+ }
+ return r;
+ }
+
+ if (log.isDebugEnabled())
+ {
+ log.debug("WikiInterceptor resolved ActionBean: " + actionBean);
+ }
+
+ // If not already set, inject WikiEngine as a request attribute (can be
+ // used later as ${wikiEngine} in EL markup)
+ WikiEngine engine = beanContext.getWikiEngine();
+ HttpServletRequest httpRequest = beanContext.getRequest();
+ httpRequest.setAttribute(ATTR_WIKIENGINE, engine);
+
+ // If not already set, Inject the WikiSession as a request attribute
+ WikiSession wikiSession = SessionMonitor.getInstance(engine).find(httpRequest.getSession());
+ httpRequest.setAttribute(ATTR_WIKISESSION, wikiSession);
+
+ // Stash the WikiActionBean and WikiPage in the PageContext
+ // Note: it probably seems a bit tricky that we're grabbing the
+ // PageContext from Stripes. We happen
+ // to know, thanks to the glories of open source code, that Stripes
+ // calls DispatcherHelper's
+ // setPageContext() method immediately before executing the
+ // BindingAndValidation stage,
+ // in *both* the <stripes:useActionBean> case and the StripesFilter
+ // case.
+ // So, the PageContext safe to grab, and boy are we glad that we can!
+ PageContext pageContext = DispatcherHelper.getPageContext();
+ if (pageContext != null)
+ {
+ // Save ActionBean to the current context
+ WikiActionBeanFactory.saveActionBean(pageContext, actionBean);
+ }
+
+ return null;
+ }
+
+ /**
+ * Intercepts the
+ * {@link net.sourceforge.stripes.controller.LifecycleStage#HandlerResolution}
+ * lifecycle stage and injects
+ * {@link com.ecyrd.jspwiki.action.EventPermissionInfo} objects into the
+ * WikiActionBeanContext if indicated by
+ * {@link com.ecyrd.jspwiki.action.EventPermission} annotations on the
+ * current ActionBean.
+ *
+ * @param context
+ * the current execution context
+ * @return a Resolution if the
+ * {@link net.sourceforge.stripes.controller.LifecycleStage#HandlerResolution}
+ * lifecycle stage's normal execution returns one; <code>null</code>
+ * otherwise
+ * @throws Exception
+ * if the underlying lifcycle stage's execution throws an
+ * Exception
+ */
+ protected Resolution interceptHandlerResolution(ExecutionContext context) throws Exception
+ {
+ // Did the handler resolution stage return a Resolution? If so, bail.
+ Resolution r = context.proceed();
+ if (r != null)
+ {
+ return r;
+ }
+
+ // Get the ActionBean and ActionBeanContext
+ WikiActionBean actionBean = (WikiActionBean) context.getActionBean();
+ WikiActionBeanContext beanContext = actionBean.getContext();
+
+ // Stash the EventPermissionInfo items for this ActionBean's event
+ // handlers
+ Map<Method, EventPermissionInfo> permMap = EventPermissionInfo.getEventPermissionInfo(actionBean.getClass());
+ for (Map.Entry<Method, EventPermissionInfo> entry : permMap.entrySet())
+ {
+ beanContext.addPermissionInfo(entry.getKey(), entry.getValue());
+ }
+
+ return null;
+ }
+
+}
Added: incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/ui/WikiJSPFilter.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/ui/WikiJSPFilter.java?rev=627255&view=auto
==============================================================================
--- incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/ui/WikiJSPFilter.java (added)
+++ incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/ui/WikiJSPFilter.java Tue Feb 12 21:53:55 2008
@@ -0,0 +1,344 @@
+/*
+ JSPWiki - a JSP-based WikiWiki clone.
+
+ Copyright (C) 2001-2006 Janne Jalkanen (Janne.Jalkanen@iki.fi)
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package com.ecyrd.jspwiki.ui;
+
+import java.io.CharArrayWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+
+import javax.servlet.*;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpServletResponseWrapper;
+
+import org.apache.log4j.Logger;
+import org.apache.log4j.NDC;
+
+import com.ecyrd.jspwiki.TextUtil;
+import com.ecyrd.jspwiki.WikiContext;
+import com.ecyrd.jspwiki.WikiEngine;
+import com.ecyrd.jspwiki.action.WikiActionBean;
+import com.ecyrd.jspwiki.event.*;
+import com.ecyrd.jspwiki.util.WatchDog;
+
+/**
+ * This filter goes through the generated page response prior and
+ * places requested resources at the appropriate inclusion markers.
+ * This is done to let dynamic content (e.g. plugins, editors)
+ * include custom resources, even after the HTML head section is
+ * in fact built. This filter is typically the last filter to execute,
+ * and it <em>must</em> run after servlet or JSP code that performs
+ * redirections or sends error codes (such as access control methods).
+ * <p>
+ * Inclusion markers are placed by the IncludeResourcesTag; the
+ * defult content templates (see .../templates/default/commonheader.jsp)
+ * are configured to do this. As an example, a JavaScript resource marker
+ * is added like this:
+ * <pre>
+ * <wiki:IncludeResources type="script"/>
+ * </pre>
+ * Any code that requires special resources must register a resource
+ * request with the TemplateManager. For example:
+ * <pre>
+ * <wiki:RequestResource type="script" path="scripts/custom.js" />
+ * </pre>
+ * or programmatically,
+ * <pre>
+ * TemplateManager.addResourceRequest( context, TemplateManager.RESOURCE_SCRIPT, "scripts/customresource.js" );
+ * </pre>
+ *
+ * @see TemplateManager
+ * @see com.ecyrd.jspwiki.tags.RequestResourceTag
+ */
+public class WikiJSPFilter implements Filter
+{
+ protected static final Logger log = Logger.getLogger( WikiJSPFilter.class );
+ protected WikiEngine m_engine = null;
+
+ public WikiJSPFilter()
+ {
+ super();
+ }
+
+ public void init(FilterConfig config) throws ServletException
+ {
+ ServletContext context = config.getServletContext();
+ m_engine = WikiEngine.getInstance( context, null );
+ }
+
+ public void destroy()
+ {
+ log.info("WikiJSPFilter destroyed; telling WikiEngine to stop..");
+ m_engine.shutdown();
+ }
+
+ public void doFilter( ServletRequest request,
+ ServletResponse response,
+ FilterChain chain )
+ throws ServletException, IOException
+ {
+ WatchDog w = m_engine.getCurrentWatchDog();
+ try
+ {
+ NDC.push( m_engine.getApplicationName()+":"+((HttpServletRequest)request).getRequestURI() );
+
+ w.enterState("Filtering for URL "+((HttpServletRequest)request).getRequestURI(), 90 );
+
+ HttpServletResponseWrapper responseWrapper = new MyServletResponseWrapper( (HttpServletResponse)response );
+
+ // fire PAGE_REQUESTED event
+ WikiActionBean actionBean = getWikiActionBean( request );
+ boolean isWikiContext = ( actionBean instanceof WikiContext );
+ if ( isWikiContext )
+ {
+ String pageName = ((WikiContext)actionBean).getPage().getName();
+ fireEvent( WikiPageEvent.PAGE_REQUESTED, pageName );
+ }
+
+ chain.doFilter( request, responseWrapper );
+
+ // The response is now complete. Lets replace the markers now.
+
+ try
+ {
+ w.enterState( "Delivering response", 30 );
+ String r = filter( actionBean, responseWrapper );
+
+ //String encoding = "UTF-8";
+ //if( wikiContext != null ) encoding = wikiContext.getEngine().getContentEncoding();
+
+ // Only now write the (real) response to the client.
+ // response.setContentLength(r.length());
+ // response.setContentType(encoding);
+
+ response.getWriter().write(r);
+
+ // Clean up the UI messages and loggers
+ actionBean.getWikiSession().clearMessages();
+
+ // fire PAGE_DELIVERED event
+ if ( isWikiContext )
+ {
+ String pageName = ((WikiContext)actionBean).getPage().getName();
+ fireEvent( WikiPageEvent.PAGE_DELIVERED, pageName );
+ }
+
+ }
+ finally
+ {
+ w.exitState();
+ }
+ }
+ finally
+ {
+ w.exitState();
+ NDC.pop();
+ NDC.remove();
+ }
+ }
+
+ /**
+ * Goes through all types and writes the appropriate response.
+ *
+ * @param actionBean The action bean for the current context
+ * @param string The source string
+ * @return The modified string with all the insertions in place.
+ */
+ private String filter(WikiActionBean actionBean, HttpServletResponse response )
+ {
+ String string = response.toString();
+
+ if( actionBean != null )
+ {
+ String[] resourceTypes = TemplateManager.getResourceTypes( actionBean );
+
+ for( int i = 0; i < resourceTypes.length; i++ )
+ {
+ string = insertResources( actionBean, string, resourceTypes[i] );
+ }
+
+ //
+ // Add HTTP header Resource Requests
+ //
+ String[] headers = TemplateManager.getResourceRequests( actionBean,
+ TemplateManager.RESOURCE_HTTPHEADER );
+
+ for( int i = 0; i < headers.length; i++ )
+ {
+ String key = headers[i];
+ String value = "";
+ int split = headers[i].indexOf(':');
+ if( split > 0 && split < headers[i].length()-1 )
+ {
+ key = headers[i].substring( 0, split );
+ value = headers[i].substring( split+1 );
+ }
+
+ response.addHeader( key.trim(), value.trim() );
+ }
+ }
+
+ return string;
+ }
+
+ /**
+ * Inserts whatever resources
+ * were requested by any plugins or other components for this particular
+ * type.
+ *
+ * @param actionBean The action bean for the current context
+ * @param string The source string
+ * @param type Type identifier for insertion
+ * @return The filtered string.
+ */
+ private String insertResources( WikiActionBean actionBean, String string, String type )
+ {
+ if( actionBean == null )
+ {
+ return string;
+ }
+
+ String marker = TemplateManager.getMarker( actionBean, type );
+ int idx = string.indexOf( marker );
+
+ if( idx == -1 )
+ {
+ return string;
+ }
+
+ log.debug("...Inserting...");
+
+ String[] resources = TemplateManager.getResourceRequests( actionBean, type );
+
+ StringBuffer concat = new StringBuffer( resources.length * 40 );
+
+ for( int i = 0; i < resources.length; i++ )
+ {
+ log.debug("...:::"+resources[i]);
+ concat.append( resources[i] );
+ }
+
+ string = TextUtil.replaceString( string,
+ idx,
+ idx+marker.length(),
+ concat.toString() );
+
+ return string;
+ }
+
+ /**
+ * Simple response wrapper that just allows us to gobble through the entire
+ * response before it's output.
+ *
+ * @author jalkanen
+ */
+ private static class MyServletResponseWrapper
+ extends HttpServletResponseWrapper
+ {
+ private CharArrayWriter m_output;
+
+ /**
+ * How large the initial buffer should be. This should be tuned to achieve
+ * a balance in speed and memory consumption.
+ */
+ private static final int INIT_BUFFER_SIZE = 4096;
+
+ public MyServletResponseWrapper( HttpServletResponse r )
+ {
+ super(r);
+ m_output = new CharArrayWriter( INIT_BUFFER_SIZE );
+ }
+
+ /**
+ * Returns a writer for output; this wraps the internal buffer
+ * into a PrintWriter.
+ */
+ public PrintWriter getWriter()
+ {
+ return new PrintWriter( m_output );
+ }
+
+ public ServletOutputStream getOutputStream()
+ {
+ return new MyServletOutputStream( m_output );
+ }
+
+ class MyServletOutputStream extends ServletOutputStream
+ {
+ CharArrayWriter m_buffer;
+
+ public MyServletOutputStream(CharArrayWriter aCharArrayWriter)
+ {
+ super();
+ m_buffer = aCharArrayWriter;
+ }
+
+ public void write(int aInt)
+ {
+ m_buffer.write( aInt );
+ }
+
+ }
+
+ /**
+ * Returns whatever was written so far into the Writer.
+ */
+ public String toString()
+ {
+ return m_output.toString();
+ }
+ }
+
+ /**
+ * Looks up the WikiActionBean stored in the request. This method does not create the
+ * action bean if it does not exist.
+ *
+ * @param request The request to examine
+ * @return A valid WikiActionBean, or <code>null</code> if one could not be located
+ */
+ protected WikiContext getWikiActionBean(ServletRequest request)
+ {
+ HttpServletRequest httpRequest = (HttpServletRequest) request;
+
+ WikiContext ctx = (WikiContext) httpRequest.getAttribute( WikiInterceptor.ATTR_ACTIONBEAN );
+
+ return ctx;
+ }
+
+ // events processing .......................................................
+
+
+ /**
+ * Fires a WikiPageEvent of the provided type and page name
+ * to all registered listeners of the current WikiEngine.
+ *
+ * @see com.ecyrd.jspwiki.event.WikiPageEvent
+ * @param type the event type to be fired
+ * @param pagename the wiki page name as a String
+ */
+ protected final void fireEvent( int type, String pagename )
+ {
+ if ( WikiEventManager.isListening(m_engine) )
+ {
+ WikiEventManager.fireEvent(m_engine,new WikiPageEvent(m_engine,type,pagename));
+ }
+ }
+
+}