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.size() == 0 ) {/* 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 for (int i = 0; i < tfArr.size(); i++) { 242 tfArr.set(i, props.getProperty(tfArr.get(i))); 243 } 244 } 245 246 final String prefTimeZone = Preferences.getPreference( context, "TimeZone" ); 247 final TimeZone tz = TimeZone.getTimeZone( prefTimeZone ); 248 249 final Date d = new Date(); // current date 250 try { 251 // dummy format pattern 252 final SimpleDateFormat fmt = Preferences.getDateFormat( context, TimeFormat.DATETIME ); 253 fmt.setTimeZone( tz ); 254 255 for( final String s : tfArr ) { 256 try { 257 fmt.applyPattern( s ); 258 resultMap.put( s, fmt.format( d ) ); 259 } catch( final IllegalArgumentException e ) { 260 } // skip parameter 261 } 262 } catch( final IllegalArgumentException e ) {} // skip parameter 263 264 return resultMap; 265 } 266 267 /** {@inheritDoc} */ 268 @Override 269 public Collection< WikiModuleInfo > modules() { 270 return new ArrayList<>(); 271 } 272 273 /** {@inheritDoc} */ 274 @Override 275 public WikiModuleInfo getModuleInfo( final String moduleName ) { 276 return null; 277 } 278 279}