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 if( log.isDebugEnabled() ) { 150 log.debug( "Final name = "+name ); 151 } 152 return name; 153 } 154 155 /** 156 * Returns the full name (/templates/foo/bar) for name=bar, template=foo. 157 * 158 * @param template The name of the template. 159 * @param name The name of the resource. 160 * @return The full name for a template. 161 */ 162 private static String makeFullJSPName( final String template, final String name ) { 163 return "/" + DIRECTORY + "/" + template + "/" + name; 164 } 165 166 /** {@inheritDoc} */ 167 @Override 168 public String findJSP( final PageContext pageContext, final String template, final String name ) { 169 if( name == null || template == null ) { 170 log.fatal("findJSP() was asked to find a null template or name (" + template + "," + name + ")." + " JSP page '" + 171 ( ( HttpServletRequest )pageContext.getRequest() ).getRequestURI() + "'" ); 172 throw new InternalWikiException( "Illegal arguments to findJSP(); please check logs." ); 173 } 174 175 return findResource( pageContext.getServletContext(), template, name ); 176 } 177 178 /** {@inheritDoc} */ 179 @Override 180 public String findResource( final Context ctx, final String template, final String name ) { 181 if( m_engine.getServletContext() != null ) { 182 return findResource( m_engine.getServletContext(), template, name ); 183 } 184 185 return getPath( template ) + "/" + name; 186 } 187 188 /* 189 * Returns a property, as defined in the template. The evaluation is lazy, i.e. the properties are not loaded until the template is 190 * actually used for the first time. 191 */ 192 /* 193 public String getTemplateProperty( WikiContext context, String key ) 194 { 195 String template = context.getTemplate(); 196 197 try 198 { 199 Properties props = (Properties)m_propertyCache.getFromCache( template, -1 ); 200 201 if( props == null ) 202 { 203 try 204 { 205 props = getTemplateProperties( template ); 206 207 m_propertyCache.putInCache( template, props ); 208 } 209 catch( IOException e ) 210 { 211 log.warn("IO Exception while reading template properties",e); 212 213 return null; 214 } 215 } 216 217 return props.getProperty( key ); 218 } 219 catch( NeedsRefreshException ex ) 220 { 221 // FIXME 222 return null; 223 } 224 } 225*/ 226 /** 227 * Returns an absolute path to a given template. 228 */ 229 private static String getPath( final String template ) { 230 return "/" + DIRECTORY + "/" + template + "/"; 231 } 232 233 /** {@inheritDoc} */ 234 @Override 235 public Set< String > listSkins( final PageContext pageContext, final String template ) { 236 final String place = makeFullJSPName( template, SKIN_DIRECTORY ); 237 final ServletContext sContext = pageContext.getServletContext(); 238 final Set< String > skinSet = sContext.getResourcePaths( place ); 239 final Set< String > resultSet = new TreeSet<>(); 240 241 if( log.isDebugEnabled() ) { 242 log.debug( "Listings skins from " + place ); 243 } 244 245 if( skinSet != null ) { 246 final String[] skins = skinSet.toArray( new String[]{} ); 247 for( final String skin : skins ) { 248 final String[] s = StringUtils.split( skin, "/" ); 249 if( s.length > 2 && skin.endsWith( "/" ) ) { 250 final String skinName = s[ s.length - 1 ]; 251 resultSet.add( skinName ); 252 if( log.isDebugEnabled() ) { 253 log.debug( "...adding skin '" + skinName + "'" ); 254 } 255 } 256 } 257 } 258 259 return resultSet; 260 } 261 262 263 /** {@inheritDoc} */ 264 @Override 265 public Map< String, String > listTimeFormats( final PageContext pageContext ) { 266 final Context context = Context.findContext( pageContext ); 267 final Properties props = m_engine.getWikiProperties(); 268 final ArrayList< String > tfArr = new ArrayList<>(40); 269 final LinkedHashMap< String, String > resultMap = new LinkedHashMap<>(); 270 271 /* filter timeformat properties */ 272 for( final Enumeration< ? > e = props.propertyNames(); e.hasMoreElements(); ) { 273 final String name = ( String )e.nextElement(); 274 if( name.startsWith( TIMEFORMATPROPERTIES ) ) { 275 tfArr.add( name ); 276 } 277 } 278 279 /* fetch actual formats */ 280 if( tfArr.size() == 0 ) {/* no props found - make sure some default formats are avail */ 281 tfArr.add( "dd-MMM-yy" ); 282 tfArr.add( "d-MMM-yyyy" ); 283 tfArr.add( "EEE, dd-MMM-yyyy, zzzz" ); 284 } else { 285 Collections.sort( tfArr ); 286 287 for (int i = 0; i < tfArr.size(); i++) { 288 tfArr.set(i, props.getProperty(tfArr.get(i))); 289 } 290 } 291 292 final String prefTimeZone = Preferences.getPreference( context, "TimeZone" ); 293 final TimeZone tz = TimeZone.getTimeZone( prefTimeZone ); 294 295 final Date d = new Date(); // current date 296 try { 297 // dummy format pattern 298 final SimpleDateFormat fmt = Preferences.getDateFormat( context, TimeFormat.DATETIME ); 299 fmt.setTimeZone( tz ); 300 301 for( final String s : tfArr ) { 302 try { 303 fmt.applyPattern( s ); 304 resultMap.put( s, fmt.format( d ) ); 305 } catch( final IllegalArgumentException e ) { 306 } // skip parameter 307 } 308 } catch( final IllegalArgumentException e ) {} // skip parameter 309 310 return resultMap; 311 } 312 313 /* 314 * Always returns a valid property map. 315 */ 316 /* 317 private Properties getTemplateProperties( String templateName ) 318 throws IOException 319 { 320 Properties p = new Properties(); 321 322 ServletContext context = m_engine.getServletContext(); 323 324 InputStream propertyStream = context.getResourceAsStream(getPath(templateName)+PROPERTYFILE); 325 326 if( propertyStream != null ) 327 { 328 p.load( propertyStream ); 329 330 propertyStream.close(); 331 } 332 else 333 { 334 log.debug("Template '"+templateName+"' does not have a propertyfile '"+PROPERTYFILE+"'."); 335 } 336 337 return p; 338 } 339*/ 340 341 /** {@inheritDoc} */ 342 @Override 343 public Collection< WikiModuleInfo > modules() { 344 return new ArrayList<>(); 345 } 346 347 /** {@inheritDoc} */ 348 @Override 349 public WikiModuleInfo getModuleInfo( final String moduleName ) { 350 return null; 351 } 352 353}