blob: 344cc06fbd965428685c41929188b18b1574d7a5 [file] [log] [blame]
/*
JSPWiki - a JSP-based WikiWiki clone.
Copyright (C) 2001-2003 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.filters;
import java.io.InputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.File;
import java.util.Properties;
import java.util.Iterator;
import java.util.List;
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.WikiContext;
import com.ecyrd.jspwiki.WikiEngine;
import com.ecyrd.jspwiki.WikiException;
import com.ecyrd.jspwiki.event.WikiEventManager;
import com.ecyrd.jspwiki.event.WikiPageEvent;
import com.ecyrd.jspwiki.util.PriorityList;
import com.ecyrd.jspwiki.util.ClassUtil;
/**
* Manages the page filters. Page filters are components that can be executed
* at certain places:
* <ul>
* <li>Before the page is translated into HTML.
* <li>After the page has been translated into HTML.
* <li>Before the page is saved.
* <li>After the page has been saved.
* </ul>
*
* Using page filters allows you to modify the page data on-the-fly, and do things like
* adding your own custom WikiMarkup.
*
* <p>
* The initial page filter configuration is kept in a file called "filters.xml". The
* format is really very simple:
* <pre>
* <?xml version="1.0"?>
*
* <pagefilters>
*
* <filter>
* <class>com.ecyrd.jspwiki.filters.ProfanityFilter</class>
* </filter>
*
* <filter>
* <class>com.ecyrd.jspwiki.filters.TestFilter</class>
*
* <param>
* <name>foobar</name>
* <value>Zippadippadai</value>
* </param>
*
* <param>
* <name>blatblaa</name>
* <value>5</value>
* </param>
*
* </filter>
* </pagefilters>
* </pre>
*
* The &lt;filter> -sections define the filters. For more information, please see
* the PageFilterConfiguration page in the JSPWiki distribution.
*
* @author Janne Jalkanen
*/
public final class FilterManager
{
private PriorityList m_pageFilters = new PriorityList();
private static final Logger log = Logger.getLogger(WikiEngine.class);
public static final String PROP_FILTERXML = "jspwiki.filterConfig";
public static final String DEFAULT_XMLFILE = "/filters.xml";
/** JSPWiki system filters are all below this value. */
public static final int SYSTEM_FILTER_PRIORITY = -1000;
/** The standard user level filtering. */
public static final int USER_FILTER_PRIORITY = 0;
private WikiEngine m_engine = null;
public FilterManager( WikiEngine engine, Properties props )
throws WikiException
{
initialize( engine, props );
}
/**
* Adds a page filter to the queue. The priority defines in which
* order the page filters are run, the highest priority filters go
* in the queue first.
* <p>
* In case two filters have the same priority, their execution order
* is the insertion order.
*
* @since 2.1.44.
* @param f PageFilter to add
* @param priority The priority in which position to add it in.
* @throws IllegalArgumentException If the PageFilter is null or invalid.
*/
public void addPageFilter( PageFilter f, int priority )
{
if( f == null )
{
throw new IllegalArgumentException("Attempt to provide a null filter - this should never happen. Please check your configuration (or if you're a developer, check your own code.)");
}
m_pageFilters.add( f, priority );
}
private void initPageFilter( String className, Properties props )
{
try
{
int priority = 0; // FIXME: Currently fixed.
Class cl = ClassUtil.findClass( "com.ecyrd.jspwiki.filters",
className );
PageFilter filter = (PageFilter)cl.newInstance();
filter.initialize( props );
addPageFilter( filter, priority );
log.info("Added page filter "+cl.getName()+" with priority "+priority);
}
catch( ClassNotFoundException e )
{
log.error("Unable to find the filter class: "+className);
}
catch( InstantiationException e )
{
log.error("Cannot create filter class: "+className);
}
catch( IllegalAccessException e )
{
log.error("You are not allowed to access class: "+className);
}
catch( ClassCastException e )
{
log.error("Suggested class is not a PageFilter: "+className);
}
catch( FilterException e )
{
log.error("Filter "+className+" failed to initialize itself.", e);
}
}
/**
* Initializes the filters from an XML file.
*/
public void initialize( WikiEngine engine, Properties props )
throws WikiException
{
m_engine = engine;
InputStream xmlStream = null;
String xmlFile = props.getProperty( PROP_FILTERXML );
try
{
if( xmlFile == null )
{
log.debug("Attempting to locate "+DEFAULT_XMLFILE+" from class path.");
xmlStream = getClass().getResourceAsStream( DEFAULT_XMLFILE );
}
else
{
log.debug("Attempting to load property file "+xmlFile);
xmlStream = new FileInputStream( new File(xmlFile) );
}
if( xmlStream == null )
{
log.info("Cannot find property file for filters (this is okay, expected to find it as: '"+ (xmlFile == null ? DEFAULT_XMLFILE : xmlFile ) +"')");
return;
}
parseConfigFile( xmlStream );
}
catch( IOException e )
{
log.error("Unable to read property file", e);
}
catch( JDOMException e )
{
log.error("Problem in the XML file",e);
}
}
/**
* Parses the XML filters configuration file.
*
* @param xmlStream
* @throws JDOMException
* @throws IOException
*/
private void parseConfigFile( InputStream xmlStream )
throws JDOMException,
IOException
{
Document doc = new SAXBuilder().build( xmlStream );
XPath xpath = XPath.newInstance("/pagefilters/filter");
List nodes = xpath.selectNodes( doc );
for( Iterator i = nodes.iterator(); i.hasNext(); )
{
Element f = (Element) i.next();
String filterClass = f.getChildText("class");
List params = f.getChildren("param");
Properties props = new Properties();
for( Iterator par = params.iterator(); par.hasNext(); )
{
Element p = (Element) par.next();
props.setProperty( p.getChildText("name"), p.getChildText("value") );
}
initPageFilter( filterClass, props );
}
}
/**
* Does the filtering before a translation.
*/
public String doPreTranslateFiltering( WikiContext context, String pageData )
throws FilterException
{
fireEvent( WikiPageEvent.PRE_TRANSLATE_BEGIN, context );
for( Iterator i = m_pageFilters.iterator(); i.hasNext(); )
{
PageFilter f = (PageFilter) i.next();
pageData = f.preTranslate( context, pageData );
}
fireEvent( WikiPageEvent.PRE_TRANSLATE_END, context );
return pageData;
}
/**
* Does the filtering after HTML translation.
*/
public String doPostTranslateFiltering( WikiContext context, String pageData )
throws FilterException
{
fireEvent( WikiPageEvent.POST_TRANSLATE_BEGIN, context );
for( Iterator i = m_pageFilters.iterator(); i.hasNext(); )
{
PageFilter f = (PageFilter) i.next();
pageData = f.postTranslate( context, pageData );
}
fireEvent( WikiPageEvent.POST_TRANSLATE_END, context );
return pageData;
}
/**
* Does the filtering before a save to the page repository.
*/
public String doPreSaveFiltering( WikiContext context, String pageData )
throws FilterException
{
fireEvent( WikiPageEvent.PRE_SAVE_BEGIN, context );
for( Iterator i = m_pageFilters.iterator(); i.hasNext(); )
{
PageFilter f = (PageFilter) i.next();
pageData = f.preSave( context, pageData );
}
fireEvent( WikiPageEvent.PRE_SAVE_END, context );
return pageData;
}
/**
* Does the page filtering after the page has been saved.
*/
public void doPostSaveFiltering( WikiContext context, String pageData )
throws FilterException
{
fireEvent( WikiPageEvent.POST_SAVE_BEGIN, context );
for( Iterator i = m_pageFilters.iterator(); i.hasNext(); )
{
PageFilter f = (PageFilter) i.next();
// log.info("POSTSAVE: "+f.toString() );
f.postSave( context, pageData );
}
fireEvent( WikiPageEvent.POST_SAVE_END, context );
}
public List getFilterList()
{
return m_pageFilters;
}
// events processing .......................................................
/**
* Fires a WikiPageEvent of the provided type and WikiContext.
* Invalid WikiPageEvent types are ignored.
*
* @see com.ecyrd.jspwiki.event.WikiPageEvent
* @param type the WikiPageEvent type to be fired.
* @param context the WikiContext of the event.
*/
public final void fireEvent( int type, WikiContext context )
{
if ( WikiEventManager.isListening(this) && WikiPageEvent.isValidType(type) )
{
WikiEventManager.fireEvent(this,
new WikiPageEvent(m_engine,type,context.getPage().getName()) );
}
}
}