    001/*
    002    Licensed to the Apache Software Foundation (ASF) under one
    003    or more contributor license agreements.  See the NOTICE file
    004    distributed with this work for additional information
    005    regarding copyright ownership.  The ASF licenses this file
    006    to you under the Apache License, Version 2.0 (the
    007    "License"); you may not use this file except in compliance
    008    with the License.  You may obtain a copy of the License at
    009
    010       http://www.apache.org/licenses/LICENSE-2.0
    011
    012    Unless required by applicable law or agreed to in writing,
    013    software distributed under the License is distributed on an
    014    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    015    KIND, either express or implied.  See the License for the
    016    specific language governing permissions and limitations
    017    under the License.
    018 */
    019package org.apache.wiki.ui;
    020
    021import org.apache.commons.lang3.StringUtils;
    022import org.apache.logging.log4j.LogManager;
    023import org.apache.logging.log4j.Logger;
    024import org.apache.wiki.InternalWikiException;
    025import org.apache.wiki.api.core.Context;
    026import org.apache.wiki.api.core.Engine;
    027import org.apache.wiki.modules.BaseModuleManager;
    028import org.apache.wiki.modules.WikiModuleInfo;
    029import org.apache.wiki.preferences.Preferences;
    030import org.apache.wiki.preferences.Preferences.TimeFormat;
    031
    032import javax.servlet.ServletContext;
    033import javax.servlet.http.HttpServletRequest;
    034import javax.servlet.jsp.PageContext;
    035import java.io.IOException;
    036import java.io.InputStream;
    037import java.text.SimpleDateFormat;
    038import java.util.ArrayList;
    039import java.util.Collection;
    040import java.util.Collections;
    041import java.util.Date;
    042import java.util.Enumeration;
    043import java.util.LinkedHashMap;
    044import java.util.Map;
    045import java.util.Properties;
    046import java.util.Set;
    047import java.util.TimeZone;
    048import java.util.TreeSet;
    049
    050
    051/**
    052 *  This class takes care of managing JSPWiki templates.  This class also provides the ResourceRequest mechanism.
    053 *
    054 *  @since 2.1.62
    055 */
    056public class DefaultTemplateManager extends BaseModuleManager implements TemplateManager {
    057
    058    private static final Logger LOG = LogManager.getLogger( DefaultTemplateManager.class );
    059
    060    /**
    061     *  Creates a new TemplateManager.  There is typically one manager per engine.
    062     *
    063     *  @param engine The owning engine.
    064     *  @param properties The property list used to initialize this.
    065     */
    066    public DefaultTemplateManager( final Engine engine, final Properties properties ) {
    067        super( engine );
    068    }
    069
    070    /** {@inheritDoc} */
    071    @Override
    072    // FIXME: Does not work yet
    073    public boolean templateExists( final String templateName ) {
    074        final ServletContext context = m_engine.getServletContext();
    075        try( final InputStream in = context.getResourceAsStream( getPath( templateName ) + "ViewTemplate.jsp" ) ) {
    076            if( in != null ) {
    077                return true;
    078            }
    079        } catch( final IOException e ) {
    080            LOG.error( e.getMessage(), e );
    081        }
    082        return false;
    083    }
    084
    085    /**
    086     *  Tries to locate a given resource from the template directory. If the given resource is not found under the current name, returns the
    087     *  path to the corresponding one in the default template.
    088     *
    089     *  @param sContext The servlet context
    090     *  @param name The name of the resource
    091     *  @return The name of the resource which was found.
    092     */
    093    private static String findResource( final ServletContext sContext, final String name ) {
    094        String resourceName = name;
    095        try( final InputStream is = sContext.getResourceAsStream( resourceName ) ) {
    096            if( is == null ) {
    097                final String defname = makeFullJSPName( DEFAULT_TEMPLATE, removeTemplatePart( resourceName ) );
    098                try( final InputStream iis = sContext.getResourceAsStream( defname ) ) {
    099                    resourceName = iis != null ? defname : null;
    100                }
    101            }
    102        } catch( final IOException e ) {
    103            LOG.error( "unable to open " + name + " as resource stream", e );
    104        }
    105        return resourceName;
    106    }
    107
    108    /**
    109     *  Attempts to find a resource from the given template, and if it's not found
    110     *  attempts to locate it from the default template.
    111     * @param sContext servlet context used to search for the resource
    112     * @param template template used to search for the resource
    113     * @param name resource name
    114     * @return the Resource for the given template and name.
    115     */
    116    private static String findResource( final ServletContext sContext, final String template, final String name ) {
    117        if( name.charAt(0) == '/' ) {
    118            // This is already a full path
    119            return findResource( sContext, name );
    120        }
    121        final String fullname = makeFullJSPName( template, name );
    122        return findResource( sContext, fullname );
    123    }
    124
    125    /** {@inheritDoc} */
    126    @Override
    127    public String findJSP( final PageContext pageContext, final String name ) {
    128        final ServletContext sContext = pageContext.getServletContext();
    129        return findResource( sContext, name );
    130    }
    131
    132    /**
    133     *  Removes the template part of a name.
    134     */
    135    private static String removeTemplatePart( String name ) {
    136        int idx = 0;
    137        if( name.startsWith( "/" ) ) {
    138            idx = 1;
    139        }
    140
    141        idx = name.indexOf('/', idx);
    142        if( idx != -1 ) {
    143            idx = name.indexOf('/', idx+1); // Find second "/"
    144            if( idx != -1 ) {
    145                name = name.substring( idx+1 );
    146            }
    147        }
    148
    149        LOG.debug( "Final name = {}", name );
    150        return name;
    151    }
    152
    153    /**
    154     *  Returns the full name (/templates/foo/bar) for name=bar, template=foo.
    155     *
    156     * @param template The name of the template.
    157     * @param name The name of the resource.
    158     * @return The full name for a template.
    159     */
    160    private static String makeFullJSPName( final String template, final String name ) {
    161        return "/" + DIRECTORY + "/" + template + "/" + name;
    162    }
    163
    164    /** {@inheritDoc} */
    165    @Override
    166    public String findJSP( final PageContext pageContext, final String template, final String name ) {
    167        if( name == null || template == null ) {
    168            LOG.fatal("findJSP() was asked to find a null template or name (" + template + "," + name + ")." + " JSP page '" +
    169                      ( ( HttpServletRequest )pageContext.getRequest() ).getRequestURI() + "'" );
    170            throw new InternalWikiException( "Illegal arguments to findJSP(); please check logs." );
    171        }
    172
    173        return findResource( pageContext.getServletContext(), template, name );
    174    }
    175
    176    /** {@inheritDoc} */
    177    @Override
    178    public String findResource( final Context ctx, final String template, final String name ) {
    179        if( m_engine.getServletContext() != null ) {
    180            return findResource( m_engine.getServletContext(), template, name );
    181        }
    182
    183        return getPath( template ) + "/" + name;
    184    }
    185
    186    /**
    187     *  Returns an absolute path to a given template.
    188     */
    189    private static String getPath( final String template ) {
    190        return "/" + DIRECTORY + "/" + template + "/";
    191    }
    192
    193    /** {@inheritDoc} */
    194    @Override
    195    public Set< String > listSkins( final PageContext pageContext, final String template ) {
    196        final String place = makeFullJSPName( template, SKIN_DIRECTORY );
    197        final ServletContext sContext = pageContext.getServletContext();
    198        final Set< String > skinSet = sContext.getResourcePaths( place );
    199        final Set< String > resultSet = new TreeSet<>();
    200
    201        LOG.debug( "Listings skins from {}", place );
    202        if( skinSet != null ) {
    203            final String[] skins = skinSet.toArray( new String[]{} );
    204            for( final String skin : skins ) {
    205                final String[] s = StringUtils.split( skin, "/" );
    206                if( s.length > 2 && skin.endsWith( "/" ) ) {
    207                    final String skinName = s[ s.length - 1 ];
    208                    resultSet.add( skinName );
    209                    LOG.debug( "...adding skin '{}'", skinName );
    210                }
    211            }
    212        }
    213
    214        return resultSet;
    215    }
    216
    217    /** {@inheritDoc} */
    218    @Override
    219    public Map< String, String > listTimeFormats( final PageContext pageContext ) {
    220        final Context context = Context.findContext( pageContext );
    221        final Properties props = m_engine.getWikiProperties();
    222        final ArrayList< String > tfArr = new ArrayList<>(40);
    223        final LinkedHashMap< String, String > resultMap = new LinkedHashMap<>();
    224
    225        /* filter timeformat properties */
    226        for( final Enumeration< ? > e = props.propertyNames(); e.hasMoreElements(); ) {
    227            final String name = ( String )e.nextElement();
    228            if( name.startsWith( TIMEFORMATPROPERTIES ) ) {
    229                tfArr.add( name );
    230            }
    231        }
    232
    233        /* fetch actual formats */
    234        if(tfArr.isEmpty())  {/* no props found - make sure some default formats are avail */
    235            tfArr.add( "dd-MMM-yy" );
    236            tfArr.add( "d-MMM-yyyy" );
    237            tfArr.add( "EEE, dd-MMM-yyyy, zzzz" );
    238        } else {
    239            Collections.sort( tfArr );
    240
    241            tfArr.replaceAll(props::getProperty);
    242        }
    243
    244        final String prefTimeZone = Preferences.getPreference( context, "TimeZone" );
    245        final TimeZone tz = TimeZone.getTimeZone( prefTimeZone );
    246
    247        final Date d = new Date(); // current date
    248        try {
    249            // dummy format pattern
    250            final SimpleDateFormat fmt = Preferences.getDateFormat( context, TimeFormat.DATETIME );
    251            fmt.setTimeZone( tz );
    252
    253            for( final String s : tfArr ) {
    254                try {
    255                    fmt.applyPattern( s );
    256                    resultMap.put( s, fmt.format( d ) );
    257                } catch( final IllegalArgumentException e ) {
    258                } // skip parameter
    259            }
    260        } catch( final IllegalArgumentException e ) {} // skip parameter
    261
    262        return resultMap;
    263    }
    264
    265    /** {@inheritDoc} */
    266    @Override
    267    public Collection< WikiModuleInfo > modules() {
    268        return new ArrayList<>();
    269    }
    270
    271    /** {@inheritDoc} */
    272    @Override
    273    public WikiModuleInfo getModuleInfo( final String moduleName ) {
    274        return null;
    275    }
    276
    277}