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 }