blob: 0ce2d02bf8f0c1d2735f771a0d6fe9ac716b04b4 [file] [log] [blame]
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you 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 org.apache.wiki;
import org.apache.log4j.Logger;
import org.apache.wiki.api.engine.FilterManager;
import org.apache.wiki.api.exceptions.NoSuchVariableException;
import org.apache.wiki.api.filters.PageFilter;
import org.apache.wiki.i18n.InternationalizationManager;
import org.apache.wiki.modules.InternalModule;
import org.apache.wiki.parser.LinkParsingOperations;
import org.apache.wiki.preferences.Preferences;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.lang.reflect.Method;
import java.security.Principal;
import java.util.Date;
import java.util.List;
import java.util.Properties;
import java.util.ResourceBundle;
/**
* Manages variables. Variables are case-insensitive. A list of all available variables is on a Wiki page called "WikiVariables".
*
* @since 1.9.20.
*/
public class VariableManager {
private static Logger log = Logger.getLogger( VariableManager.class );
// FIXME: These are probably obsolete.
public static final String VAR_ERROR = "error";
public static final String VAR_MSG = "msg";
/**
* Contains a list of those properties that shall never be shown. Put names here in lower case.
*/
static final String[] THE_BIG_NO_NO_LIST = {
"jspwiki.auth.masterpassword"
};
/**
* Creates a VariableManager object using the property list given.
* @param props The properties.
*/
public VariableManager( final Properties props )
{
}
/**
* Parses the link and finds a value. This is essentially used once
* {@link LinkParsingOperations#isVariableLink(String)} has found that
* the link text actually contains a variable. For example, you could
* pass in "{$username}" and get back "JanneJalkanen".
*
* @param context The WikiContext
* @param link The link text containing the variable name.
* @return The variable value.
* @throws IllegalArgumentException If the format is not valid (does not start with "{$", is zero length, etc.)
* @throws NoSuchVariableException If a variable is not known.
*/
public String parseAndGetValue( final WikiContext context, final String link ) throws IllegalArgumentException, NoSuchVariableException {
if( !link.startsWith( "{$" ) ) {
throw new IllegalArgumentException( "Link does not start with {$" );
}
if( !link.endsWith( "}" ) ) {
throw new IllegalArgumentException( "Link does not end with }" );
}
final String varName = link.substring( 2, link.length() - 1 );
return getValue( context, varName.trim() );
}
/**
* This method does in-place expansion of any variables. However, the expansion is not done twice, that is,
* a variable containing text $variable will not be expanded.
* <P>
* The variables should be in the same format ({$variablename} as in the web pages.
*
* @param context The WikiContext of the current page.
* @param source The source string.
* @return The source string with variables expanded.
*/
// FIXME: somewhat slow.
public String expandVariables( final WikiContext context, final String source ) {
final StringBuilder result = new StringBuilder();
for( int i = 0; i < source.length(); i++ ) {
if( source.charAt(i) == '{' ) {
if( i < source.length()-2 && source.charAt(i+1) == '$' ) {
final int end = source.indexOf( '}', i );
if( end != -1 ) {
final String varname = source.substring( i+2, end );
String value;
try {
value = getValue( context, varname );
} catch( final NoSuchVariableException | IllegalArgumentException e ) {
value = e.getMessage();
}
result.append( value );
i = end;
}
} else {
result.append( '{' );
}
} else {
result.append( source.charAt(i) );
}
}
return result.toString();
}
/**
* Returns the value of a named variable. See {@link #getValue(WikiContext, String)}. The only difference is that
* this method does not throw an exception, but it returns the given default value instead.
*
* @param context WikiContext
* @param varName The name of the variable
* @param defValue A default value.
* @return The variable value, or if not found, the default value.
*/
public String getValue( final WikiContext context, final String varName, final String defValue ) {
try {
return getValue( context, varName );
} catch( final NoSuchVariableException e ) {
return defValue;
}
}
/**
* Shortcut to getValue(). However, this method does not throw a NoSuchVariableException, but returns null
* in case the variable does not exist.
*
* @param context WikiContext to look the variable in
* @param name Name of the variable to look for
* @return Variable value, or null, if there is no such variable.
* @since 2.2 on WikiEngine, moved to VariableManager on 2.11.0
*/
public String getVariable( final WikiContext context, final String name ) {
return getValue( context, name, null );
}
/**
* Returns a value of the named variable. The resolving order is
* <ol>
* <li>Known "constant" name, such as "pagename", etc. This is so
* that pages could not override certain constants.
* <li>WikiContext local variable. This allows a programmer to
* set a parameter which cannot be overridden by user.
* <li>HTTP Session
* <li>HTTP Request parameters
* <li>WikiPage variable. As set by the user with the SET directive.
* <li>jspwiki.properties
* </ol>
*
* Use this method only whenever you really need to have a parameter that
* can be overridden by anyone using the wiki.
*
* @param context The WikiContext
* @param varName Name of the variable.
*
* @return The variable value.
*
* @throws IllegalArgumentException If the name is somehow broken.
* @throws NoSuchVariableException If a variable is not known.
*/
public String getValue( final WikiContext context, final String varName ) throws IllegalArgumentException, NoSuchVariableException {
if( varName == null ) {
throw new IllegalArgumentException( "Null variable name." );
}
if( varName.length() == 0 ) {
throw new IllegalArgumentException( "Zero length variable name." );
}
// Faster than doing equalsIgnoreCase()
final String name = varName.toLowerCase();
for( final String value : THE_BIG_NO_NO_LIST ) {
if( name.equals( value ) ) {
return ""; // FIXME: Should this be something different?
}
}
try {
//
// Using reflection to get system variables adding a new system variable
// now only involves creating a new method in the SystemVariables class
// with a name starting with get and the first character of the name of
// the variable capitalized. Example:
// public String getMysysvar(){
// return "Hello World";
// }
//
final SystemVariables sysvars = new SystemVariables( context );
final String methodName = "get" + Character.toUpperCase( name.charAt( 0 ) ) + name.substring( 1 );
final Method method = sysvars.getClass().getMethod( methodName );
return ( String )method.invoke( sysvars );
} catch( final NoSuchMethodException e1 ) {
//
// It is not a system var. Time to handle the other cases.
//
// Check if such a context variable exists, returning its string representation.
//
if( ( context.getVariable( varName ) ) != null ) {
return context.getVariable( varName ).toString();
}
//
// Well, I guess it wasn't a final straw. We also allow variables from the session and the request (in this order).
//
final HttpServletRequest req = context.getHttpRequest();
if( req != null && req.getSession() != null ) {
final HttpSession session = req.getSession();
try {
String s = ( String )session.getAttribute( varName );
if( s != null ) {
return s;
}
s = context.getHttpParameter( varName );
if( s != null ) {
return s;
}
} catch( final ClassCastException e ) {
log.debug( "Not a String: " + varName );
}
}
//
// And the final straw: see if the current page has named metadata.
//
final WikiPage pg = context.getPage();
if( pg != null ) {
final Object metadata = pg.getAttribute( varName );
if( metadata != null ) {
return metadata.toString();
}
}
//
// And the final straw part 2: see if the "real" current page has named metadata. This allows
// a parent page to control a inserted page through defining variables
//
final WikiPage rpg = context.getRealPage();
if( rpg != null ) {
final Object metadata = rpg.getAttribute( varName );
if( metadata != null ) {
return metadata.toString();
}
}
//
// Next-to-final straw: attempt to fetch using property name. We don't allow fetching any other
// properties than those starting with "jspwiki.". I know my own code, but I can't vouch for bugs
// in other people's code... :-)
//
if( varName.startsWith("jspwiki.") ) {
final Properties props = context.getEngine().getWikiProperties();
final String s = props.getProperty( varName );
if( s != null ) {
return s;
}
}
//
// Final defaults for some known quantities.
//
if( varName.equals( VAR_ERROR ) || varName.equals( VAR_MSG ) ) {
return "";
}
throw new NoSuchVariableException( "No variable " + varName + " defined." );
} catch( final Exception e ) {
log.info("Interesting exception: cannot fetch variable value", e );
}
return "";
}
/**
* This class provides the implementation for the different system variables.
* It is called via Reflection - any access to a variable called $xxx is mapped
* to getXxx() on this class.
* <p>
* This is a lot neater than using a huge if-else if branching structure
* that we used to have before.
* <p>
* Note that since we are case insensitive for variables, and VariableManager
* calls var.toLowerCase(), the getters for the variables do not have
* capitalization anywhere. This may look a bit odd, but then again, this
* is not meant to be a public class.
*
* @since 2.7.0
*/
@SuppressWarnings( "unused" )
private static class SystemVariables
{
private final WikiContext m_context;
public SystemVariables( final WikiContext context )
{
m_context=context;
}
public String getPagename()
{
return m_context.getPage().getName();
}
public String getApplicationname()
{
return m_context.getEngine().getApplicationName();
}
public String getJspwikiversion()
{
return Release.getVersionString();
}
public String getEncoding() {
return m_context.getEngine().getContentEncoding().displayName();
}
public String getTotalpages() {
return Integer.toString( m_context.getEngine().getPageCount() );
}
public String getPageprovider()
{
return m_context.getEngine().getCurrentProvider();
}
public String getPageproviderdescription()
{
return m_context.getEngine().getCurrentProviderInfo();
}
public String getAttachmentprovider() {
final WikiProvider p = m_context.getEngine().getAttachmentManager().getCurrentProvider();
return (p != null) ? p.getClass().getName() : "-";
}
public String getAttachmentproviderdescription() {
final WikiProvider p = m_context.getEngine().getAttachmentManager().getCurrentProvider();
return (p != null) ? p.getProviderInfo() : "-";
}
public String getInterwikilinks() {
final StringBuilder res = new StringBuilder();
for( final String link : m_context.getEngine().getAllInterWikiLinks() ) {
if( res.length() > 0 ) {
res.append( ", " );
}
res.append( link );
res.append( " --> " );
res.append( m_context.getEngine().getInterWikiURL( link ) );
}
return res.toString();
}
public String getInlinedimages() {
final StringBuilder res = new StringBuilder();
for( final String ptrn : m_context.getEngine().getAllInlinedImagePatterns() ) {
if( res.length() > 0 ) {
res.append( ", " );
}
res.append( ptrn );
}
return res.toString();
}
public String getPluginpath() {
final String s = m_context.getEngine().getPluginManager().getPluginSearchPath();
return ( s == null ) ? "-" : s;
}
public String getBaseurl()
{
return m_context.getEngine().getBaseURL();
}
public String getUptime() {
final Date now = new Date();
long secondsRunning = ( now.getTime() - m_context.getEngine().getStartTime().getTime() ) / 1_000L;
final long seconds = secondsRunning % 60;
final long minutes = (secondsRunning /= 60) % 60;
final long hours = (secondsRunning /= 60) % 24;
final long days = secondsRunning /= 24;
return days + "d, " + hours + "h " + minutes + "m " + seconds + "s";
}
public String getLoginstatus() {
final WikiSession session = m_context.getWikiSession();
return Preferences.getBundle( m_context, InternationalizationManager.CORE_BUNDLE ).getString( "varmgr." + session.getStatus() );
}
public String getUsername() {
final Principal wup = m_context.getCurrentUser();
final ResourceBundle rb = Preferences.getBundle( m_context, InternationalizationManager.CORE_BUNDLE );
return wup != null ? wup.getName() : rb.getString( "varmgr.not.logged.in" );
}
public String getRequestcontext()
{
return m_context.getRequestContext();
}
public String getPagefilters() {
final FilterManager fm = m_context.getEngine().getFilterManager();
final List< PageFilter > filters = fm.getFilterList();
final StringBuilder sb = new StringBuilder();
for( final PageFilter pf : filters ) {
final String f = pf.getClass().getName();
if( pf instanceof InternalModule ) {
continue;
}
if( sb.length() > 0 ) {
sb.append( ", " );
}
sb.append( f );
}
return sb.toString();
}
}
}