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 */
019 package org.apache.wiki.ui;
020
021 import java.io.IOException;
022 import java.io.InputStream;
023 import java.text.SimpleDateFormat;
024 import java.util.ArrayList;
025 import java.util.Collection;
026 import java.util.Collections;
027 import java.util.Date;
028 import java.util.Enumeration;
029 import java.util.HashMap;
030 import java.util.LinkedHashMap;
031 import java.util.List;
032 import java.util.Locale;
033 import java.util.Map;
034 import java.util.Properties;
035 import java.util.ResourceBundle;
036 import java.util.Set;
037 import java.util.TimeZone;
038 import java.util.TreeSet;
039 import java.util.Vector;
040
041 import javax.servlet.ServletContext;
042 import javax.servlet.http.HttpServletRequest;
043 import javax.servlet.jsp.PageContext;
044 import javax.servlet.jsp.jstl.fmt.LocaleSupport;
045
046 import org.apache.commons.lang.StringUtils;
047 import org.apache.log4j.Logger;
048 import org.apache.wiki.InternalWikiException;
049 import org.apache.wiki.WikiContext;
050 import org.apache.wiki.WikiEngine;
051 import org.apache.wiki.i18n.InternationalizationManager;
052 import org.apache.wiki.modules.ModuleManager;
053 import org.apache.wiki.preferences.Preferences;
054 import org.apache.wiki.preferences.Preferences.TimeFormat;
055 import org.apache.wiki.util.ClassUtil;
056
057
058 /**
059 * This class takes care of managing JSPWiki templates. This class also provides
060 * the ResourceRequest mechanism.
061 *
062 * @since 2.1.62
063 */
064 public class TemplateManager extends ModuleManager {
065
066 private static final String SKIN_DIRECTORY = "skins";
067
068 /**
069 * Requests a JavaScript function to be called during window.onload. Value is {@value}.
070 */
071 public static final String RESOURCE_JSFUNCTION = "jsfunction";
072
073 /**
074 * Requests a JavaScript associative array with all localized strings.
075 */
076 public static final String RESOURCE_JSLOCALIZEDSTRINGS = "jslocalizedstrings";
077
078 /**
079 * Requests a stylesheet to be inserted. Value is {@value}.
080 */
081 public static final String RESOURCE_STYLESHEET = "stylesheet";
082
083 /**
084 * Requests a script to be loaded. Value is {@value}.
085 */
086 public static final String RESOURCE_SCRIPT = "script";
087
088 /**
089 * Requests inlined CSS. Value is {@value}.
090 */
091 public static final String RESOURCE_INLINECSS = "inlinecss";
092
093 /** The default directory for the properties. Value is {@value}. */
094 public static final String DIRECTORY = "templates";
095
096 /** The name of the default template. Value is {@value}. */
097 public static final String DEFAULT_TEMPLATE = "default";
098
099 /** Name of the file that contains the properties. */
100
101 public static final String PROPERTYFILE = "template.properties";
102
103 /** Location of I18N Resource bundles, and path prefix and suffixes */
104
105 public static final String I18NRESOURCE_PREFIX = "templates/default_";
106
107 public static final String I18NRESOURCE_SUFFIX = ".properties";
108
109 /** The default (en) RESOURCE name and id. */
110
111 public static final String I18NRESOURCE_EN = "templates/default.properties";
112 public static final String I18NRESOURCE_EN_ID = "en";
113
114 /** I18N string to mark the default locale */
115
116 public static final String I18NDEFAULT_LOCALE = "prefs.user.language.default";
117
118 /** I18N string to mark the server timezone */
119
120 public static final String I18NSERVER_TIMEZONE = "prefs.user.timezone.server";
121
122 /** Prefix of the default timeformat properties. */
123
124 public static final String TIMEFORMATPROPERTIES = "jspwiki.defaultprefs.timeformat.";
125
126 /**
127 * The name under which the resource includes map is stored in the
128 * WikiContext.
129 */
130 public static final String RESOURCE_INCLUDES = "jspwiki.resourceincludes";
131
132 // private Cache m_propertyCache;
133
134 protected static final Logger log = Logger.getLogger(TemplateManager.class);
135
136 /** Requests a HTTP header. Value is {@value}. */
137 public static final String RESOURCE_HTTPHEADER = "httpheader";
138
139 /**
140 * Creates a new TemplateManager. There is typically one manager per engine.
141 *
142 * @param engine The owning engine.
143 * @param properties The property list used to initialize this.
144 */
145 public TemplateManager( WikiEngine engine, Properties properties )
146 {
147 super(engine);
148
149 //
150 // Uses the unlimited cache.
151 //
152 // m_propertyCache = new Cache( true, false );
153 }
154
155 /**
156 * Check the existence of a template.
157 */
158 // FIXME: Does not work yet
159 public boolean templateExists( String templateName )
160 {
161 ServletContext context = m_engine.getServletContext();
162
163 InputStream in = context.getResourceAsStream( getPath(templateName)+"ViewTemplate.jsp");
164
165 if( in != null )
166 {
167 try
168 {
169 in.close();
170 }
171 catch (IOException e)
172 {
173 }
174
175 return true;
176 }
177
178 return false;
179 }
180
181 /**
182 * Tries to locate a given resource from the template directory. If the
183 * given resource is not found under the current name, returns the
184 * path to the corresponding one in the default template.
185 *
186 * @param sContext The servlet context
187 * @param name The name of the resource
188 * @return The name of the resource which was found.
189 */
190 private static String findResource( ServletContext sContext, String name )
191 {
192 InputStream is = sContext.getResourceAsStream( name );
193
194 if( is == null )
195 {
196 String defname = makeFullJSPName( DEFAULT_TEMPLATE,
197 removeTemplatePart(name) );
198 is = sContext.getResourceAsStream( defname );
199
200 if( is != null )
201 name = defname;
202 else
203 name = null;
204 }
205
206 if( is != null )
207 {
208 try
209 {
210 is.close();
211 }
212 catch( IOException e )
213 {}
214 }
215
216 return name;
217 }
218
219 /**
220 * Attempts to find a resource from the given template, and if it's not found
221 * attempts to locate it from the default template.
222 * @param sContext
223 * @param template
224 * @param name
225 * @return the Resource for the given template and name.
226 */
227 private static String findResource( ServletContext sContext, String template, String name )
228 {
229 if( name.charAt(0) == '/' )
230 {
231 // This is already a full path
232 return findResource( sContext, name );
233 }
234
235 String fullname = makeFullJSPName( template, name );
236
237 return findResource( sContext, fullname );
238 }
239
240 /**
241 * An utility method for finding a JSP page. It searches only under
242 * either current context or by the absolute name.
243 *
244 * @param pageContext the JSP PageContext
245 * @param name The name of the JSP page to look for (e.g "Wiki.jsp")
246 * @return The context path to the resource
247 */
248 public String findJSP( PageContext pageContext, String name )
249 {
250 ServletContext sContext = pageContext.getServletContext();
251
252 return findResource( sContext, name );
253 }
254
255 /**
256 * Removes the template part of a name.
257 */
258 private static String removeTemplatePart( String name )
259 {
260 int idx = 0;
261 if( name.startsWith( "/" ) ) idx = 1;
262
263 idx = name.indexOf('/', idx);
264 if( idx != -1 )
265 {
266 idx = name.indexOf('/', idx+1); // Find second "/"
267
268 if( idx != -1 )
269 {
270 name = name.substring( idx+1 );
271 }
272 }
273
274 log.info( "Final name = "+name );
275 return name;
276 }
277
278 /**
279 * Returns the full name (/templates/foo/bar) for name=bar, template=foo.
280 *
281 * @param template The name of the template.
282 * @param name The name of the resource.
283 * @return The full name for a template.
284 */
285 private static String makeFullJSPName( String template, String name )
286 {
287 return "/"+DIRECTORY+"/"+template+"/"+name;
288 }
289
290 /**
291 * Attempts to locate a resource under the given template. If that template
292 * does not exist, or the page does not exist under that template, will
293 * attempt to locate a similarly named file under the default template.
294 * <p>
295 * Even though the name suggests only JSP files can be located, but in fact
296 * this method can find also other resources than JSP files.
297 *
298 * @param pageContext The JSP PageContext
299 * @param template From which template we should seek initially?
300 * @param name Which resource are we looking for (e.g. "ViewTemplate.jsp")
301 * @return path to the JSP page; null, if it was not found.
302 */
303 public String findJSP( PageContext pageContext, String template, String name )
304 {
305 if( name == null || template == null )
306 {
307 log.fatal("findJSP() was asked to find a null template or name ("+template+","+name+")."+
308 " JSP page '"+
309 ((HttpServletRequest)pageContext.getRequest()).getRequestURI()+"'");
310 throw new InternalWikiException("Illegal arguments to findJSP(); please check logs.");
311 }
312
313 return findResource( pageContext.getServletContext(), template, name );
314 }
315
316 /**
317 * Attempts to locate a resource under the given template. This matches the
318 * functionality findJSP(), but uses the WikiContext as the argument. If there
319 * is no servlet context (i.e. this is embedded), will just simply return
320 * a best-guess.
321 * <p>
322 * This method is typically used to locate any resource, including JSP pages, images,
323 * scripts, etc.
324 *
325 * @since 2.6
326 * @param ctx the wiki context
327 * @param template the name of the template to use
328 * @param name the name of the resource to fine
329 * @return the path to the resource
330 */
331 public String findResource( WikiContext ctx, String template, String name )
332 {
333 if( m_engine.getServletContext() != null )
334 {
335 return findResource( m_engine.getServletContext(), template, name );
336 }
337
338 return getPath(template)+"/"+name;
339 }
340
341 /**
342 * Returns a property, as defined in the template. The evaluation
343 * is lazy, i.e. the properties are not loaded until the template is
344 * actually used for the first time.
345 */
346 /*
347 public String getTemplateProperty( WikiContext context, String key )
348 {
349 String template = context.getTemplate();
350
351 try
352 {
353 Properties props = (Properties)m_propertyCache.getFromCache( template, -1 );
354
355 if( props == null )
356 {
357 try
358 {
359 props = getTemplateProperties( template );
360
361 m_propertyCache.putInCache( template, props );
362 }
363 catch( IOException e )
364 {
365 log.warn("IO Exception while reading template properties",e);
366
367 return null;
368 }
369 }
370
371 return props.getProperty( key );
372 }
373 catch( NeedsRefreshException ex )
374 {
375 // FIXME
376 return null;
377 }
378 }
379 */
380 /**
381 * Returns an absolute path to a given template.
382 */
383 private static String getPath( String template )
384 {
385 return "/"+DIRECTORY+"/"+template+"/";
386 }
387
388 /**
389 * Lists the skins available under this template. Returns an
390 * empty Set, if there are no extra skins available. Note that
391 * this method does not check whether there is anything actually
392 * in the directories, it just lists them. This may change
393 * in the future.
394 *
395 * @param pageContext the JSP PageContext
396 * @param template The template to search
397 * @return Set of Strings with the skin names.
398 * @since 2.3.26
399 */
400 @SuppressWarnings("unchecked")
401 public Set listSkins( PageContext pageContext, String template )
402 {
403 String place = makeFullJSPName( template, SKIN_DIRECTORY );
404
405 ServletContext sContext = pageContext.getServletContext();
406
407 Set<String> skinSet = sContext.getResourcePaths( place );
408 TreeSet<String> resultSet = new TreeSet<String>();
409
410 if( log.isDebugEnabled() ) log.debug( "Listings skins from "+place );
411
412 if( skinSet != null )
413 {
414 String[] skins = {};
415
416 skins = skinSet.toArray(skins);
417
418 for (int i = 0; i < skins.length; i++)
419 {
420 String[] s = StringUtils.split(skins[i], "/");
421
422 if (s.length > 2 && skins[i].endsWith("/"))
423 {
424 String skinName = s[s.length - 1];
425 resultSet.add(skinName);
426 if (log.isDebugEnabled())
427 log.debug("...adding skin '" + skinName + "'");
428 }
429 }
430 }
431
432 return resultSet;
433 }
434
435 /**
436 * List all installed i18n language properties by classpath searching for files like :
437 * templates/default_*.properties
438 * templates/default.properties
439 *
440 * @param pageContext
441 * @return map of installed Languages
442 * @since 2.7.x
443 */
444 public Map listLanguages(PageContext pageContext)
445 {
446 Map< String, String > resultMap = new LinkedHashMap< String, String >();
447 String clientLanguage = ((HttpServletRequest) pageContext.getRequest()).getLocale().toString();
448
449 List< String > entries = ClassUtil.classpathEntriesUnder( DIRECTORY );
450 for( String name : entries ) {
451 if ( name.equals( I18NRESOURCE_EN ) ||
452 (name.startsWith( I18NRESOURCE_PREFIX ) && name.endsWith( I18NRESOURCE_SUFFIX ) ) )
453 {
454 if (name.equals( I18NRESOURCE_EN )) {
455 name = I18NRESOURCE_EN_ID;
456 } else {
457 name = name.substring(I18NRESOURCE_PREFIX.length(), name.lastIndexOf(I18NRESOURCE_SUFFIX));
458 }
459 Locale locale = new Locale(name.substring(0, 2), ((name.indexOf("_") == -1) ? "" : name.substring(3, 5)));
460 String defaultLanguage = "";
461 if (clientLanguage.startsWith(name))
462 {
463 defaultLanguage = LocaleSupport.getLocalizedMessage(pageContext, I18NDEFAULT_LOCALE);
464 }
465 resultMap.put(name, locale.getDisplayName(locale) + " " + defaultLanguage);
466 }
467 }
468
469 return resultMap;
470 }
471
472
473 /**
474 * List all available timeformats, read from the jspwiki.properties
475 *
476 * @param pageContext
477 * @return map of TimeFormats
478 * @since 2.7.x
479 */
480 public Map listTimeFormats(PageContext pageContext)
481 {
482 WikiContext context = WikiContext.findContext( pageContext );
483 Properties props = m_engine.getWikiProperties();
484 ArrayList<String> tfArr = new ArrayList<String>(40);
485 LinkedHashMap<String,String> resultMap = new LinkedHashMap<String,String>();
486
487 /* filter timeformat properties */
488 for (Enumeration e = props.propertyNames(); e.hasMoreElements();)
489 {
490 String name = (String) e.nextElement();
491
492 if (name.startsWith(TIMEFORMATPROPERTIES))
493 {
494 tfArr.add(name);
495 }
496 }
497
498 /* fetch actual formats */
499 if (tfArr.size() == 0) /*
500 * no props found - make sure some default
501 * formats are avail
502 */
503 {
504 tfArr.add("dd-MMM-yy");
505 tfArr.add("d-MMM-yyyy");
506 tfArr.add("EEE, dd-MMM-yyyy, zzzz");
507 }
508 else
509 {
510 Collections.sort(tfArr);
511
512 for (int i = 0; i < tfArr.size(); i++)
513 {
514 tfArr.set(i, props.getProperty(tfArr.get(i)));
515 }
516 }
517
518 String prefTimeZone = Preferences.getPreference( context, "TimeZone" );
519 //TimeZone tz = TimeZone.getDefault();
520 TimeZone tz = TimeZone.getTimeZone(prefTimeZone);
521 /*try
522 {
523 tz.setRawOffset(Integer.parseInt(prefTimeZone));
524 }
525 catch (Exception e)
526 {
527 }*/
528
529 Date d = new Date(); // current date
530 try
531 {
532 // dummy format pattern
533 SimpleDateFormat fmt = Preferences.getDateFormat( context, TimeFormat.DATETIME );
534 fmt.setTimeZone(tz);
535
536 for (int i = 0; i < tfArr.size(); i++)
537 {
538 try
539 {
540 String f = tfArr.get(i);
541 fmt.applyPattern(f);
542
543 resultMap.put(f, fmt.format(d));
544 }
545 catch (IllegalArgumentException e)
546 {
547 } // skip parameter
548 }
549 }
550 catch (IllegalArgumentException e)
551 {
552 } // skip parameter
553
554 return resultMap;
555 }
556
557 /**
558 * List all timezones, with special marker for server timezone
559 *
560 * @param pageContext
561 * @return map of TimeZones
562 * @since 2.7.x
563 */
564 public Map listTimeZones(PageContext pageContext)
565 {
566 LinkedHashMap<String,String> resultMap = new LinkedHashMap<String,String>();
567
568 String[][] tzs = { { "GMT-12", "Enitwetok, Kwajalien" },
569 { "GMT-11", "Nome, Midway Island, Samoa" },
570 { "GMT-10", "Hawaii" },
571 { "GMT-9", "Alaska" },
572 { "GMT-8", "Pacific Time" },
573 { "GMT-7", "Mountain Time" },
574 { "GMT-6", "Central Time, Mexico City" },
575 { "GMT-5", "Eastern Time, Bogota, Lima, Quito" },
576 { "GMT-4", "Atlantic Time, Caracas, La Paz" },
577 { "GMT-3:30", "Newfoundland" },
578 { "GMT-3", "Brazil, Buenos Aires, Georgetown, Falkland Is." },
579 { "GMT-2", "Mid-Atlantic, Ascention Is., St Helena" },
580 { "GMT-1", "Azores, Cape Verde Islands" },
581 { "GMT", "Casablanca, Dublin, Edinburgh, London, Lisbon, Monrovia" },
582 { "GMT+1", "Berlin, Brussels, Copenhagen, Madrid, Paris, Rome" },
583 { "GMT+2", "Helsinki, Athens, Kaliningrad, South Africa, Warsaw" },
584 { "GMT+3", "Baghdad, Riyadh, Moscow, Nairobi" },
585 { "GMT+3:30", "Tehran" },
586 { "GMT+4", "Adu Dhabi, Baku, Muscat, Tbilisi" },
587 { "GMT+4:30", "Kabul" },
588 { "GMT+5", "Islamabad, Karachi, Tashkent" },
589 { "GMT+5:30", "Bombay, Calcutta, Madras, New Delhi" },
590 { "GMT+6", "Almaty, Colomba, Dhakra" },
591 { "GMT+7", "Bangkok, Hanoi, Jakarta" },
592 { "GMT+8", "Beijing, Hong Kong, Perth, Singapore, Taipei" },
593 { "GMT+9", "Osaka, Sapporo, Seoul, Tokyo, Yakutsk" },
594 { "GMT+9:30", "Adelaide, Darwin" },
595 { "GMT+10", "Melbourne, Papua New Guinea, Sydney, Vladivostok" },
596 { "GMT+11", "Magadan, New Caledonia, Solomon Islands" },
597 { "GMT+12", "Auckland, Wellington, Fiji, Marshall Island" } };
598
599 java.util.TimeZone servertz = java.util.TimeZone.getDefault();
600
601 for( int i = 0; i < tzs.length; i++ )
602 {
603 String tzID = tzs[i][0];
604 java.util.TimeZone tz = java.util.TimeZone.getTimeZone(tzID);
605
606 String serverTimeZone = "";
607
608 if( servertz.getRawOffset() == tz.getRawOffset() )
609 {
610 serverTimeZone = LocaleSupport.getLocalizedMessage(pageContext, I18NSERVER_TIMEZONE);
611 tzID = servertz.getID();
612 }
613
614 resultMap.put(tzID, "(" + tzs[i][0] + ") "+tzs[i][1] + " " + serverTimeZone);
615 }
616
617 return resultMap;
618 }
619
620 /**
621 * Always returns a valid property map.
622 */
623 /*
624 private Properties getTemplateProperties( String templateName )
625 throws IOException
626 {
627 Properties p = new Properties();
628
629 ServletContext context = m_engine.getServletContext();
630
631 InputStream propertyStream = context.getResourceAsStream(getPath(templateName)+PROPERTYFILE);
632
633 if( propertyStream != null )
634 {
635 p.load( propertyStream );
636
637 propertyStream.close();
638 }
639 else
640 {
641 log.debug("Template '"+templateName+"' does not have a propertyfile '"+PROPERTYFILE+"'.");
642 }
643
644 return p;
645 }
646 */
647 /**
648 * Returns the include resources marker for a given type. This is in a
649 * HTML or Javascript comment format.
650 *
651 * @param context the wiki context
652 * @param type the marker
653 * @return the generated marker comment
654 */
655 public static String getMarker(WikiContext context, String type )
656 {
657 if( type.equals(RESOURCE_JSLOCALIZEDSTRINGS) )
658 {
659 return getJSLocalizedStrings( context );
660 }
661 else if( type.equals(RESOURCE_JSFUNCTION) )
662 {
663 return "/* INCLUDERESOURCES ("+type+") */";
664 }
665 return "<!-- INCLUDERESOURCES ("+type+") -->";
666 }
667
668 /**
669 * Extract all i18n strings in the javascript domain. (javascript.*)
670 * Returns a javascript snippet which defines the LocalizedStings array.
671 *
672 * @param context the {@link WikiContext}
673 * @return Javascript snippet which defines the LocalizedStrings array
674 * @since 2.5.108
675 */
676 private static String getJSLocalizedStrings( WikiContext context )
677 {
678 StringBuffer sb = new StringBuffer();
679
680 sb.append( "var LocalizedStrings = {\n");
681
682 ResourceBundle rb = Preferences.getBundle( context, InternationalizationManager.DEF_TEMPLATE );
683
684 boolean first = true;
685
686 for( Enumeration< String > en = rb.getKeys(); en.hasMoreElements(); )
687 {
688 String key = en.nextElement();
689
690 if( key.startsWith("javascript") )
691 {
692 if( first )
693 {
694 first = false;
695 }
696 else
697 {
698 sb.append( ",\n" );
699 }
700 sb.append( "\""+key+"\":\""+rb.getString(key)+"\"" );
701 }
702 }
703 sb.append("\n};\n");
704
705 return( sb.toString() );
706 }
707
708 /**
709 * Adds a resource request to the current request context.
710 * The content will be added at the resource-type marker
711 * (see IncludeResourcesTag) in WikiJSPFilter.
712 * <p>
713 * The resources can be of different types. For RESOURCE_SCRIPT and RESOURCE_STYLESHEET
714 * this is an URI path to the resource (a script file or an external stylesheet)
715 * that needs to be included. For RESOURCE_INLINECSS
716 * the resource should be something that can be added between <style></style> in the
717 * header file (commonheader.jsp). For RESOURCE_JSFUNCTION it is the name of the Javascript
718 * function that should be run at page load.
719 * <p>
720 * The IncludeResourceTag inserts code in the template files, which is then filled
721 * by the WikiFilter after the request has been rendered but not yet sent to the recipient.
722 * <p>
723 * Note that ALL resource requests get rendered, so this method does not check if
724 * the request already exists in the resources. Therefore, if you have a plugin which
725 * makes a new resource request every time, you'll end up with multiple resource requests
726 * rendered. It's thus a good idea to make this request only once during the page
727 * life cycle.
728 *
729 * @param ctx The current wiki context
730 * @param type What kind of a request should be added?
731 * @param resource The resource to add.
732 */
733 @SuppressWarnings("unchecked")
734 public static void addResourceRequest( WikiContext ctx, String type, String resource )
735 {
736 HashMap<String,Vector<String>> resourcemap = (HashMap<String,Vector<String>>) ctx.getVariable( RESOURCE_INCLUDES );
737
738 if( resourcemap == null )
739 {
740 resourcemap = new HashMap<String,Vector<String>>();
741 }
742
743 Vector<String> resources = resourcemap.get( type );
744
745 if( resources == null )
746 {
747 resources = new Vector<String>();
748 }
749
750 String resourceString = null;
751
752 if( type.equals(RESOURCE_SCRIPT) )
753 {
754 resourceString = "<script type='text/javascript' src='"+resource+"'></script>";
755 }
756 else if( type.equals(RESOURCE_STYLESHEET) )
757 {
758 resourceString = "<link rel='stylesheet' type='text/css' href='"+resource+"' />";
759 }
760 else if( type.equals(RESOURCE_INLINECSS) )
761 {
762 resourceString = "<style type='text/css'>\n"+resource+"\n</style>\n";
763 }
764 else if( type.equals(RESOURCE_JSFUNCTION) )
765 {
766 resourceString = resource;
767 }
768 else if( type.equals(RESOURCE_HTTPHEADER) )
769 {
770 resourceString = resource;
771 }
772
773 if( resourceString != null )
774 {
775 resources.add( resourceString );
776 }
777
778 log.debug("Request to add a resource: "+resourceString);
779
780 resourcemap.put( type, resources );
781 ctx.setVariable( RESOURCE_INCLUDES, resourcemap );
782 }
783
784 /**
785 * Returns resource requests for a particular type. If there are no resources,
786 * returns an empty array.
787 *
788 * @param ctx WikiContext
789 * @param type The resource request type
790 * @return a String array for the resource requests
791 */
792
793 @SuppressWarnings("unchecked")
794 public static String[] getResourceRequests( WikiContext ctx, String type )
795 {
796 HashMap<String,Vector<String>> hm = (HashMap<String,Vector<String>>) ctx.getVariable( RESOURCE_INCLUDES );
797
798 if( hm == null ) return new String[0];
799
800 Vector<String> resources = hm.get( type );
801
802 if( resources == null ) return new String[0];
803
804 String[] res = new String[resources.size()];
805
806 return resources.toArray( res );
807 }
808
809 /**
810 * Returns all those types that have been requested so far.
811 *
812 * @param ctx the wiki context
813 * @return the array of types requested
814 */
815 @SuppressWarnings("unchecked")
816 public static String[] getResourceTypes( WikiContext ctx )
817 {
818 String[] res = new String[0];
819
820 if( ctx != null )
821 {
822 HashMap<String,String> hm = (HashMap<String,String>) ctx.getVariable( RESOURCE_INCLUDES );
823
824 if( hm != null )
825 {
826 Set<String> keys = hm.keySet();
827
828 res = keys.toArray( res );
829 }
830 }
831
832 return res;
833 }
834
835 /**
836 * Returns an empty collection, since at the moment the TemplateManager
837 * does not manage any modules.
838 *
839 * @return {@inheritDoc}
840 */
841 public Collection modules()
842 {
843 return new ArrayList();
844 }
845 }