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