blob: f2f827a994fba49ebac1935593019b4637df6459 [file] [log] [blame]
/*
JSPWiki - a JSP-based WikiWiki clone.
Copyright (C) 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.ui;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import javax.servlet.ServletContext;
import javax.servlet.jsp.PageContext;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import com.ecyrd.jspwiki.WikiContext;
import com.ecyrd.jspwiki.WikiEngine;
import com.ecyrd.jspwiki.modules.ModuleManager;
import com.opensymphony.oscache.base.Cache;
import com.opensymphony.oscache.base.NeedsRefreshException;
/**
* This class takes care of managing JSPWiki templates.
*
* @since 2.1.62
* @author Janne Jalkanen
*/
public class TemplateManager
extends ModuleManager
{
/**
* Requests a JavaScript function to be called during window.onload.
*/
public static final String RESOURCE_JSFUNCTION = "jsfunction";
/**
* Requests a stylesheet to be inserted.
*/
public static final String RESOURCE_STYLESHEET = "stylesheet";
/**
* Requests a script to be loaded.
*/
public static final String RESOURCE_SCRIPT = "script";
/** The default directory for the properties. */
public static final String DIRECTORY = "templates";
public static final String DEFAULT_TEMPLATE = "default";
/** Name of the file that contains the properties.*/
public static final String PROPERTYFILE = "template.properties";
public static final String RESOURCE_INCLUDES = "jspwiki.resourceincludes";
private WikiEngine m_engine;
private Cache m_propertyCache;
protected static Logger log = Logger.getLogger( TemplateManager.class );
public TemplateManager( WikiEngine engine, Properties properties )
{
m_engine = 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;
}
/**
* An utility method for finding a JSP page. It searches only under
* either current context or by the absolute name.
*/
public String findJSP( PageContext pageContext, String name )
{
ServletContext sContext = pageContext.getServletContext();
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;
}
/**
* Removes the template part of a name.
*/
private 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;
}
private final String makeFullJSPName( String template, String name )
{
return "/"+DIRECTORY+"/"+template+"/"+name;
}
/**
* Attempts to locate a JSP page 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.
*/
public String findJSP( PageContext pageContext, String template, String name )
{
ServletContext sContext = pageContext.getServletContext();
if( name.charAt(0) == '/' )
{
// This is already a full path
return findJSP( pageContext, name );
}
String fullname = makeFullJSPName( template, name );
InputStream is = sContext.getResourceAsStream( fullname );
if( is == null )
{
String defname = makeFullJSPName( DEFAULT_TEMPLATE, name );
is = sContext.getResourceAsStream( defname );
if( is != null )
fullname = defname;
else
fullname = null;
}
if( is != null ) try { is.close(); } catch( IOException e ) {}
return fullname;
}
/**
* 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;
}
}
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 template
* @return Set of Strings with the skin names.
* @since 2.3.26
*/
public Set listSkins( PageContext pageContext, String template )
{
String place = makeFullJSPName( template, "skins" );
ServletContext sContext = pageContext.getServletContext();
Set skinSet = sContext.getResourcePaths( place );
TreeSet resultSet = new TreeSet();
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 > 1 )
{
String skinName = s[s.length-1];
resultSet.add( skinName );
log.debug("..."+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 comment format.
*
* @param type
* @return
*/
public static String getMarker( String type )
{
if( type.equals(RESOURCE_JSFUNCTION) )
{
return "/* INCLUDERESOURCES ("+type+") */";
}
return "<!-- INCLUDERESOURCES ("+type+") -->";
}
/**
* Adds a resource request to the current request context.
*/
public static void addResourceRequest( WikiContext ctx, String type, String path )
{
HashMap resourcemap = (HashMap) ctx.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 == RESOURCE_SCRIPT )
{
resourceString = "<script type='text/javascript' src='"+path+"'></script>";
}
else if( type == RESOURCE_STYLESHEET )
{
resourceString = "<link rel='stylesheet' type='text/css' src='"+path+"' />";
}
else if( type == RESOURCE_JSFUNCTION )
{
resourceString = path;
}
if( resourceString != null )
{
resources.add( resourceString );
}
log.debug("Request to add a resource: "+resourceString);
resourcemap.put( type, resources );
ctx.setVariable( RESOURCE_INCLUDES, resourcemap );
}
/**
* Returns resource requests for a particular type. If there are no resources,
* returns an empty array.
*/
public static String[] getResourceRequests( WikiContext ctx, String type )
{
HashMap hm = (HashMap) ctx.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
* @return
*/
public static String[] getResourceTypes( WikiContext ctx )
{
String[] res = new String[0];
if( ctx != null )
{
HashMap hm = (HashMap) ctx.getVariable( RESOURCE_INCLUDES );
if( hm != null )
{
Set keys = hm.keySet();
res = (String[]) keys.toArray( res );
}
}
return res;
}
}