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.log4j.Logger; 023import org.apache.wiki.InternalWikiException; 024import org.apache.wiki.api.core.Context; 025import org.apache.wiki.api.core.Engine; 026import org.apache.wiki.modules.BaseModuleManager; 027import org.apache.wiki.modules.WikiModuleInfo; 028import org.apache.wiki.preferences.Preferences; 029import org.apache.wiki.preferences.Preferences.TimeFormat; 030 031import javax.servlet.ServletContext; 032import javax.servlet.http.HttpServletRequest; 033import javax.servlet.jsp.PageContext; 034import java.io.IOException; 035import java.io.InputStream; 036import java.text.SimpleDateFormat; 037import java.util.ArrayList; 038import java.util.Collection; 039import java.util.Collections; 040import java.util.Date; 041import java.util.Enumeration; 042import java.util.LinkedHashMap; 043import java.util.Map; 044import java.util.Properties; 045import java.util.Set; 046import java.util.TimeZone; 047import java.util.TreeSet; 048 049 050/** 051 * This class takes care of managing JSPWiki templates. This class also provides the ResourceRequest mechanism. 052 * 053 * @since 2.1.62 054 */ 055public class DefaultTemplateManager extends BaseModuleManager implements TemplateManager { 056 057 private static final Logger log = Logger.getLogger( DefaultTemplateManager.class ); 058 059 /** 060 * Creates a new TemplateManager. There is typically one manager per engine. 061 * 062 * @param engine The owning engine. 063 * @param properties The property list used to initialize this. 064 */ 065 public DefaultTemplateManager( final Engine engine, final Properties properties ) { 066 super( engine ); 067 } 068 069 /** {@inheritDoc} */ 070 @Override 071 // FIXME: Does not work yet 072 public boolean templateExists( final String templateName ) { 073 final ServletContext context = m_engine.getServletContext(); 074 try( final InputStream in = context.getResourceAsStream( getPath( templateName ) + "ViewTemplate.jsp" ) ) { 075 if( in != null ) { 076 return true; 077 } 078 } catch( final IOException e ) { 079 log.error( e.getMessage(), e ); 080 } 081 return false; 082 } 083 084 /** 085 * Tries to locate a given resource from the template directory. If the given resource is not found under the current name, returns the 086 * path to the corresponding one in the default template. 087 * 088 * @param sContext The servlet context 089 * @param name The name of the resource 090 * @return The name of the resource which was found. 091 */ 092 private static String findResource( final ServletContext sContext, final String name ) { 093 String resourceName = name; 094 try( final InputStream is = sContext.getResourceAsStream( resourceName ) ) { 095 if( is == null ) { 096 final String defname = makeFullJSPName( DEFAULT_TEMPLATE, removeTemplatePart( resourceName ) ); 097 try( final InputStream iis = sContext.getResourceAsStream( defname ) ) { 098 resourceName = iis != null ? defname : null; 099 } 100 } 101 } catch( final IOException e ) { 102 log.error( "unable to open " + name + " as resource stream", e ); 103 } 104 return resourceName; 105 } 106 107 /** 108 * Attempts to find a resource from the given template, and if it's not found 109 * attempts to locate it from the default template. 110 * @param sContext servlet context used to search for the resource 111 * @param template template used to search for the resource 112 * @param name resource name 113 * @return the Resource for the given template and name. 114 */ 115 private static String findResource( final ServletContext sContext, final String template, final String name ) { 116 if( name.charAt(0) == '/' ) { 117 // This is already a full path 118 return findResource( sContext, name ); 119 } 120 final String fullname = makeFullJSPName( template, name ); 121 return findResource( sContext, fullname ); 122 } 123 124 /** {@inheritDoc} */ 125 @Override 126 public String findJSP( final PageContext pageContext, final String name ) { 127 final ServletContext sContext = pageContext.getServletContext(); 128 return findResource( sContext, name ); 129 } 130 131 /** 132 * Removes the template part of a name. 133 */ 134 private static String removeTemplatePart( String name ) { 135 int idx = 0; 136 if( name.startsWith( "/" ) ) { 137 idx = 1; 138 } 139 140 idx = name.indexOf('/', idx); 141 if( idx != -1 ) { 142 idx = name.indexOf('/', idx+1); // Find second "/" 143 if( idx != -1 ) { 144 name = name.substring( idx+1 ); 145 } 146 } 147 148 if( log.isDebugEnabled() ) { 149 log.debug( "Final name = "+name ); 150 } 151 return name; 152 } 153 154 /** 155 * Returns the full name (/templates/foo/bar) for name=bar, template=foo. 156 * 157 * @param template The name of the template. 158 * @param name The name of the resource. 159 * @return The full name for a template. 160 */ 161 private static String makeFullJSPName( final String template, final String name ) { 162 return "/" + DIRECTORY + "/" + template + "/" + name; 163 } 164 165 /** {@inheritDoc} */ 166 @Override 167 public String findJSP( final PageContext pageContext, final String template, final String name ) { 168 if( name == null || template == null ) { 169 log.fatal("findJSP() was asked to find a null template or name (" + template + "," + name + ")." + " JSP page '" + 170 ( ( HttpServletRequest )pageContext.getRequest() ).getRequestURI() + "'" ); 171 throw new InternalWikiException( "Illegal arguments to findJSP(); please check logs." ); 172 } 173 174 return findResource( pageContext.getServletContext(), template, name ); 175 } 176 177 /** {@inheritDoc} */ 178 @Override 179 public String findResource( final Context ctx, final String template, final String name ) { 180 if( m_engine.getServletContext() != null ) { 181 return findResource( m_engine.getServletContext(), template, name ); 182 } 183 184 return getPath( template ) + "/" + name; 185 } 186 187 /* 188 * Returns a property, as defined in the template. The evaluation is lazy, i.e. the properties are not loaded until the template is 189 * actually used for the first time. 190 */ 191 /* 192 public String getTemplateProperty( WikiContext context, String key ) 193 { 194 String template = context.getTemplate(); 195 196 try 197 { 198 Properties props = (Properties)m_propertyCache.getFromCache( template, -1 ); 199 200 if( props == null ) 201 { 202 try 203 { 204 props = getTemplateProperties( template ); 205 206 m_propertyCache.putInCache( template, props ); 207 } 208 catch( IOException e ) 209 { 210 log.warn("IO Exception while reading template properties",e); 211 212 return null; 213 } 214 } 215 216 return props.getProperty( key ); 217 } 218 catch( NeedsRefreshException ex ) 219 { 220 // FIXME 221 return null; 222 } 223 } 224*/ 225 /** 226 * Returns an absolute path to a given template. 227 */ 228 private static String getPath( final String template ) { 229 return "/" + DIRECTORY + "/" + template + "/"; 230 } 231 232 /** {@inheritDoc} */ 233 @Override 234 public Set< String > listSkins( final PageContext pageContext, final String template ) { 235 final String place = makeFullJSPName( template, SKIN_DIRECTORY ); 236 final ServletContext sContext = pageContext.getServletContext(); 237 final Set< String > skinSet = sContext.getResourcePaths( place ); 238 final Set< String > resultSet = new TreeSet<>(); 239 240 if( log.isDebugEnabled() ) { 241 log.debug( "Listings skins from " + place ); 242 } 243 244 if( skinSet != null ) { 245 final String[] skins = skinSet.toArray( new String[]{} ); 246 for( final String skin : skins ) { 247 final String[] s = StringUtils.split( skin, "/" ); 248 if( s.length > 2 && skin.endsWith( "/" ) ) { 249 final String skinName = s[ s.length - 1 ]; 250 resultSet.add( skinName ); 251 if( log.isDebugEnabled() ) { 252 log.debug( "...adding skin '" + skinName + "'" ); 253 } 254 } 255 } 256 } 257 258 return resultSet; 259 } 260 261 262 /** {@inheritDoc} */ 263 @Override 264 public Map< String, String > listTimeFormats( final PageContext pageContext ) { 265 final Context context = Context.findContext( pageContext ); 266 final Properties props = m_engine.getWikiProperties(); 267 final ArrayList< String > tfArr = new ArrayList<>(40); 268 final LinkedHashMap< String, String > resultMap = new LinkedHashMap<>(); 269 270 /* filter timeformat properties */ 271 for( final Enumeration< ? > e = props.propertyNames(); e.hasMoreElements(); ) { 272 final String name = ( String )e.nextElement(); 273 if( name.startsWith( TIMEFORMATPROPERTIES ) ) { 274 tfArr.add( name ); 275 } 276 } 277 278 /* fetch actual formats */ 279 if( tfArr.size() == 0 ) {/* no props found - make sure some default formats are avail */ 280 tfArr.add( "dd-MMM-yy" ); 281 tfArr.add( "d-MMM-yyyy" ); 282 tfArr.add( "EEE, dd-MMM-yyyy, zzzz" ); 283 } else { 284 Collections.sort( tfArr ); 285 286 for (int i = 0; i < tfArr.size(); i++) { 287 tfArr.set(i, props.getProperty(tfArr.get(i))); 288 } 289 } 290 291 final String prefTimeZone = Preferences.getPreference( context, "TimeZone" ); 292 final TimeZone tz = TimeZone.getTimeZone( prefTimeZone ); 293 294 final Date d = new Date(); // current date 295 try { 296 // dummy format pattern 297 final SimpleDateFormat fmt = Preferences.getDateFormat( context, TimeFormat.DATETIME ); 298 fmt.setTimeZone( tz ); 299 300 for( final String s : tfArr ) { 301 try { 302 final String f = s; 303 fmt.applyPattern( f ); 304 resultMap.put( f, 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}