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; 020 021import org.apache.commons.lang3.StringUtils; 022import org.apache.commons.lang3.time.StopWatch; 023import org.apache.log4j.Logger; 024import org.apache.log4j.PropertyConfigurator; 025import org.apache.wiki.api.engine.AdminBeanManager; 026import org.apache.wiki.api.engine.FilterManager; 027import org.apache.wiki.api.engine.PluginManager; 028import org.apache.wiki.api.exceptions.FilterException; 029import org.apache.wiki.api.exceptions.NoRequiredPropertyException; 030import org.apache.wiki.api.exceptions.NoSuchVariableException; 031import org.apache.wiki.api.exceptions.ProviderException; 032import org.apache.wiki.api.exceptions.WikiException; 033import org.apache.wiki.attachment.Attachment; 034import org.apache.wiki.attachment.AttachmentManager; 035import org.apache.wiki.auth.AuthenticationManager; 036import org.apache.wiki.auth.AuthorizationManager; 037import org.apache.wiki.auth.UserManager; 038import org.apache.wiki.auth.acl.AclManager; 039import org.apache.wiki.auth.authorize.GroupManager; 040import org.apache.wiki.content.PageRenamer; 041import org.apache.wiki.diff.DifferenceManager; 042import org.apache.wiki.event.WikiEngineEvent; 043import org.apache.wiki.event.WikiEventListener; 044import org.apache.wiki.event.WikiEventManager; 045import org.apache.wiki.event.WikiPageEvent; 046import org.apache.wiki.event.WikiPageRenameEvent; 047import org.apache.wiki.i18n.InternationalizationManager; 048import org.apache.wiki.pages.PageManager; 049import org.apache.wiki.pages.PageTimeComparator; 050import org.apache.wiki.parser.MarkupParser; 051import org.apache.wiki.parser.WikiDocument; 052import org.apache.wiki.providers.WikiPageProvider; 053import org.apache.wiki.render.RenderingManager; 054import org.apache.wiki.rss.RSSGenerator; 055import org.apache.wiki.rss.RSSThread; 056import org.apache.wiki.search.SearchManager; 057import org.apache.wiki.tasks.TasksManager; 058import org.apache.wiki.ui.Command; 059import org.apache.wiki.ui.CommandResolver; 060import org.apache.wiki.ui.EditorManager; 061import org.apache.wiki.ui.TemplateManager; 062import org.apache.wiki.ui.progress.ProgressManager; 063import org.apache.wiki.url.URLConstructor; 064import org.apache.wiki.util.ClassUtil; 065import org.apache.wiki.util.PropertyReader; 066import org.apache.wiki.util.TextUtil; 067import org.apache.wiki.workflow.Decision; 068import org.apache.wiki.workflow.DecisionRequiredException; 069import org.apache.wiki.workflow.Fact; 070import org.apache.wiki.workflow.Step; 071import org.apache.wiki.workflow.Workflow; 072import org.apache.wiki.workflow.WorkflowBuilder; 073import org.apache.wiki.workflow.WorkflowManager; 074 075import javax.servlet.ServletConfig; 076import javax.servlet.ServletContext; 077import javax.servlet.http.HttpServletRequest; 078import java.io.File; 079import java.io.IOException; 080import java.io.UnsupportedEncodingException; 081import java.net.MalformedURLException; 082import java.net.URL; 083import java.net.URLDecoder; 084import java.net.URLEncoder; 085import java.nio.charset.Charset; 086import java.nio.charset.StandardCharsets; 087import java.security.Principal; 088import java.util.ArrayList; 089import java.util.Collection; 090import java.util.Date; 091import java.util.Enumeration; 092import java.util.List; 093import java.util.Locale; 094import java.util.Map; 095import java.util.Properties; 096import java.util.Set; 097import java.util.TimeZone; 098import java.util.TreeSet; 099import java.util.concurrent.ConcurrentHashMap; 100 101 102/** 103 * Provides Wiki services to the JSP page. 104 * 105 * <P> 106 * This is the main interface through which everything should go. 107 * 108 * <P> 109 * Using this class: Always get yourself an instance from JSP page 110 * by using the WikiEngine.getInstance() method. Never create a new 111 * WikiEngine() from scratch, unless you're writing tests. 112 * <p> 113 * There's basically only a single WikiEngine for each web application, and 114 * you should always get it using the WikiEngine.getInstance() method. 115 */ 116public class WikiEngine 117{ 118 private static final String ATTR_WIKIENGINE = "org.apache.wiki.WikiEngine"; 119 120 private static final Logger log = Logger.getLogger(WikiEngine.class); 121 122 /** True, if log4j has been configured. */ 123 // FIXME: If you run multiple applications, the first application 124 // to run defines where the log goes. Not what we want. 125 private static boolean c_configured = false; 126 127 /** Stores properties. */ 128 private Properties m_properties; 129 130 /** The default inlining pattern. Currently "*.png" */ 131 public static final String DEFAULT_INLINEPATTERN = "*.png"; 132 133 /** The name used for the default template. The value is {@value}. */ 134 public static final String DEFAULT_TEMPLATE_NAME = "default"; 135 136 /** Property for application name */ 137 public static final String PROP_APPNAME = "jspwiki.applicationName"; 138 139 /** This property defines the inline image pattern. It's current value is {@value} */ 140 public static final String PROP_INLINEIMAGEPTRN = "jspwiki.translatorReader.inlinePattern"; 141 142 /** Property start for any interwiki reference. */ 143 public static final String PROP_INTERWIKIREF = "jspwiki.interWikiRef."; 144 145 /** If true, then the user name will be stored with the page data.*/ 146 public static final String PROP_STOREUSERNAME= "jspwiki.storeUserName"; 147 148 /** Define the used encoding. Currently supported are ISO-8859-1 and UTF-8 */ 149 public static final String PROP_ENCODING = "jspwiki.encoding"; 150 151 /** Do not use encoding in WikiJSPFilter, default is false for most servers. 152 Double negative, cause for most servers you don't need the property */ 153 public static final String PROP_NO_FILTER_ENCODING = "jspwiki.nofilterencoding"; 154 155 /** The name for the property which allows you to set the current reference 156 * style. The value is {@value}. 157 */ 158 public static final String PROP_REFSTYLE = "jspwiki.referenceStyle"; 159 160 /** Property name for the "spaces in titles" -hack. */ 161 public static final String PROP_BEAUTIFYTITLE = "jspwiki.breakTitleWithSpaces"; 162 163 /** Property name for where the jspwiki work directory should be. 164 If not specified, reverts to ${java.tmpdir}. */ 165 public static final String PROP_WORKDIR = "jspwiki.workDir"; 166 167 /** The name of the cookie that gets stored to the user browser. */ 168 public static final String PREFS_COOKIE_NAME = "JSPWikiUserProfile"; 169 170 /** Property name for the "match english plurals" -hack. */ 171 public static final String PROP_MATCHPLURALS = "jspwiki.translatorReader.matchEnglishPlurals"; 172 173 /** Property name for the template that is used. */ 174 public static final String PROP_TEMPLATEDIR = "jspwiki.templateDir"; 175 176 /** Property name for the default front page. */ 177 public static final String PROP_FRONTPAGE = "jspwiki.frontPage"; 178 179 /** Property name for setting the url generator instance */ 180 181 public static final String PROP_URLCONSTRUCTOR = "jspwiki.urlConstructor"; 182 183 /** If this property is set to false, all filters are disabled when translating. */ 184 public static final String PROP_RUNFILTERS = "jspwiki.runFilters"; 185 186 /** Does the work in renaming pages. */ 187 private PageRenamer m_pageRenamer = null; 188 189 /** The name of the property containing the ACLManager implementing class. 190 * The value is {@value}. */ 191 public static final String PROP_ACL_MANAGER_IMPL = "jspwiki.aclManager"; 192 193 /** If this property is set to false, we don't allow the creation of empty pages */ 194 public static final String PROP_ALLOW_CREATION_OF_EMPTY_PAGES = "jspwiki.allowCreationOfEmptyPages"; 195 196 /** Should the user info be saved with the page data as well? */ 197 private boolean m_saveUserInfo = true; 198 199 /** If true, uses UTF8 encoding for all data */ 200 private boolean m_useUTF8 = true; 201 202 /** Store the file path to the basic URL. When we're not running as 203 a servlet, it defaults to the user's current directory. */ 204 private String m_rootPath = System.getProperty("user.dir"); 205 206 /** Stores references between wikipages. */ 207 private ReferenceManager m_referenceManager = null; 208 209 /** Stores the Plugin manager */ 210 private PluginManager m_pluginManager; 211 212 /** Stores the Variable manager */ 213 private VariableManager m_variableManager; 214 215 /** Stores the Attachment manager */ 216 private AttachmentManager m_attachmentManager = null; 217 218 /** Stores the Page manager */ 219 private PageManager m_pageManager = null; 220 221 /** Stores the authorization manager */ 222 private AuthorizationManager m_authorizationManager = null; 223 224 /** Stores the authentication manager.*/ 225 private AuthenticationManager m_authenticationManager = null; 226 227 /** Stores the ACL manager. */ 228 private AclManager m_aclManager = null; 229 230 /** Resolves wiki actions, JSPs and special pages. */ 231 private CommandResolver m_commandResolver = null; 232 233 private TemplateManager m_templateManager = null; 234 235 /** Does all our diffs for us. */ 236 private DifferenceManager m_differenceManager; 237 238 /** Handlers page filters. */ 239 private FilterManager m_filterManager; 240 241 /** Stores the Search manager */ 242 private SearchManager m_searchManager = null; 243 244 /** Facade for managing users */ 245 private UserManager m_userManager = null; 246 247 /** Facade for managing users */ 248 private GroupManager m_groupManager = null; 249 250 private RenderingManager m_renderingManager; 251 252 private EditorManager m_editorManager; 253 254 private InternationalizationManager m_internationalizationManager; 255 256 private ProgressManager m_progressManager; 257 258 private TasksManager m_tasksManager; 259 260 /** Constructs URLs */ 261 private URLConstructor m_urlConstructor; 262 263 /** Generates RSS feed when requested. */ 264 private RSSGenerator m_rssGenerator; 265 266 /** The RSS file to generate. */ 267 private String m_rssFile; 268 269 /** Store the ServletContext that we're in. This may be null if WikiEngine 270 is not running inside a servlet container (i.e. when testing). */ 271 private ServletContext m_servletContext = null; 272 273 /** If true, all titles will be cleaned. */ 274 private boolean m_beautifyTitle = false; 275 276 /** Stores the template path. This is relative to "templates". */ 277 private String m_templateDir; 278 279 /** The default front page name. Defaults to "Main". */ 280 private String m_frontPage; 281 282 /** The time when this engine was started. */ 283 private Date m_startTime; 284 285 /** The location where the work directory is. */ 286 private String m_workDir; 287 288 /** Each engine has their own application id. */ 289 private String m_appid = ""; 290 291 private boolean m_isConfigured = false; // Flag. 292 293 294 /** Each engine has its own workflow manager. */ 295 private WorkflowManager m_workflowMgr = null; 296 297 private AdminBeanManager m_adminBeanManager; 298 299 /** Stores wikiengine attributes. */ 300 private Map<String,Object> m_attributes = new ConcurrentHashMap<>(); 301 302 /** 303 * Gets a WikiEngine related to this servlet. Since this method 304 * is only called from JSP pages (and JspInit()) to be specific, 305 * we throw a RuntimeException if things don't work. 306 * 307 * @param config The ServletConfig object for this servlet. 308 * 309 * @return A WikiEngine instance. 310 * @throws InternalWikiException in case something fails. This 311 * is a RuntimeException, so be prepared for it. 312 */ 313 314 // FIXME: It seems that this does not work too well, jspInit() 315 // does not react to RuntimeExceptions, or something... 316 317 public static synchronized WikiEngine getInstance( ServletConfig config ) 318 throws InternalWikiException 319 { 320 return getInstance( config.getServletContext(), null ); 321 } 322 323 /** 324 * Gets a WikiEngine related to the servlet. Works like getInstance(ServletConfig), 325 * but does not force the Properties object. This method is just an optional way 326 * of initializing a WikiEngine for embedded JSPWiki applications; normally, you 327 * should use getInstance(ServletConfig). 328 * 329 * @param config The ServletConfig of the webapp servlet/JSP calling this method. 330 * @param props A set of properties, or null, if we are to load JSPWiki's default 331 * jspwiki.properties (this is the usual case). 332 * 333 * @return One well-behaving WikiEngine instance. 334 */ 335 public static synchronized WikiEngine getInstance( ServletConfig config, 336 Properties props ) 337 { 338 return getInstance( config.getServletContext(), props ); 339 } 340 341 /** 342 * Gets a WikiEngine related to the servlet. Works just like getInstance( ServletConfig ) 343 * 344 * @param context The ServletContext of the webapp servlet/JSP calling this method. 345 * @param props A set of properties, or null, if we are to load JSPWiki's default 346 * jspwiki.properties (this is the usual case). 347 * 348 * @return One fully functional, properly behaving WikiEngine. 349 * @throws InternalWikiException If the WikiEngine instantiation fails. 350 */ 351 352 // FIXME: Potential make-things-easier thingy here: no need to fetch the wikiengine anymore 353 // Wiki.jsp.jspInit() [really old code]; it's probably even faster to fetch it 354 // using this method every time than go to pageContext.getAttribute(). 355 356 public static synchronized WikiEngine getInstance( ServletContext context, 357 Properties props ) 358 throws InternalWikiException 359 { 360 WikiEngine engine = (WikiEngine) context.getAttribute( ATTR_WIKIENGINE ); 361 362 if( engine == null ) 363 { 364 String appid = Integer.toString(context.hashCode()); //FIXME: Kludge, use real type. 365 366 context.log(" Assigning new engine to "+appid); 367 try 368 { 369 if( props == null ) 370 { 371 props = PropertyReader.loadWebAppProps( context ); 372 } 373 374 engine = new WikiEngine( context, appid, props ); 375 context.setAttribute( ATTR_WIKIENGINE, engine ); 376 } 377 catch( Exception e ) 378 { 379 context.log( "ERROR: Failed to create a Wiki engine: "+e.getMessage() ); 380 log.error( "ERROR: Failed to create a Wiki engine, stacktrace follows " , e); 381 throw new InternalWikiException( "No wiki engine, check logs." , e); 382 } 383 384 } 385 386 return engine; 387 } 388 389 390 /** 391 * Instantiate the WikiEngine using a given set of properties. 392 * Use this constructor for testing purposes only. 393 * 394 * @param properties A set of properties to use to initialize this WikiEngine. 395 * @throws WikiException If the initialization fails. 396 */ 397 public WikiEngine( Properties properties ) 398 throws WikiException 399 { 400 initialize( properties ); 401 } 402 403 /** 404 * Instantiate using this method when you're running as a servlet and 405 * WikiEngine will figure out where to look for the property 406 * file. 407 * Do not use this method - use WikiEngine.getInstance() instead. 408 * 409 * @param context A ServletContext. 410 * @param appid An Application ID. This application is an unique random string which 411 * is used to recognize this WikiEngine. 412 * @param props The WikiEngine configuration. 413 * @throws WikiException If the WikiEngine construction fails. 414 */ 415 protected WikiEngine( ServletContext context, String appid, Properties props ) 416 throws WikiException 417 { 418 super(); 419 m_servletContext = context; 420 m_appid = appid; 421 422 // Stash the WikiEngine in the servlet context 423 if ( context != null ) 424 { 425 context.setAttribute( ATTR_WIKIENGINE, this ); 426 m_rootPath = context.getRealPath("/"); 427 } 428 429 try 430 { 431 // 432 // Note: May be null, if JSPWiki has been deployed in a WAR file. 433 // 434 initialize( props ); 435 log.info("Root path for this Wiki is: '"+m_rootPath+"'"); 436 } 437 catch( Exception e ) 438 { 439 String msg = Release.APPNAME+": Unable to load and setup properties from jspwiki.properties. "+e.getMessage(); 440 if ( context != null ) 441 { 442 context.log( msg ); 443 } 444 throw new WikiException( msg, e ); 445 } 446 } 447 448 /** 449 * Does all the real initialization. 450 */ 451 private void initialize( Properties props ) 452 throws WikiException 453 { 454 m_startTime = new Date(); 455 m_properties = props; 456 457 // 458 // Initialize log4j. However, make sure that we don't initialize it multiple times. 459 // By default we load the log4j config statements from jspwiki.properties, unless 460 // the property jspwiki.use.external.logconfig=true, in that case we let log4j figure out the 461 // logging configuration. 462 // 463 if( !c_configured ) 464 { 465 String useExternalLogConfig = TextUtil.getStringProperty(props,"jspwiki.use.external.logconfig","false"); 466 if( useExternalLogConfig == null || useExternalLogConfig.equals("false")) 467 { 468 PropertyConfigurator.configure( props ); 469 } 470 c_configured = true; 471 } 472 473 log.info("*******************************************"); 474 log.info(Release.APPNAME+" "+Release.getVersionString()+" starting. Whee!"); 475 476 fireEvent( WikiEngineEvent.INITIALIZING ); // begin initialization 477 478 log.debug("Java version: "+System.getProperty("java.runtime.version")); 479 log.debug("Java vendor: "+System.getProperty("java.vm.vendor")); 480 log.debug("OS: "+System.getProperty("os.name")+" "+System.getProperty("os.version")+" "+System.getProperty("os.arch")); 481 log.debug("Default server locale: "+Locale.getDefault()); 482 log.debug("Default server timezone: "+TimeZone.getDefault().getDisplayName(true, TimeZone.LONG)); 483 484 if( m_servletContext != null ) 485 { 486 log.info("Servlet container: "+m_servletContext.getServerInfo() ); 487 if( m_servletContext.getMajorVersion() < 2 || 488 (m_servletContext.getMajorVersion() == 2 && m_servletContext.getMinorVersion() < 4) ) 489 { 490 throw new InternalWikiException("I require a container which supports at least version 2.4 of Servlet specification"); 491 } 492 } 493 494 log.debug("Configuring WikiEngine..."); 495 496 // Initializes the CommandResolver 497 m_commandResolver = new CommandResolver( this, props ); 498 499 // 500 // Create and find the default working directory. 501 // 502 m_workDir = TextUtil.getStringProperty( props, PROP_WORKDIR, null ); 503 504 if( m_workDir == null ) { 505 m_workDir = System.getProperty("java.io.tmpdir", "."); 506 m_workDir += File.separator+Release.APPNAME+"-"+m_appid; 507 } 508 509 try { 510 File f = new File( m_workDir ); 511 f.mkdirs(); 512 513 // 514 // A bunch of sanity checks 515 // 516 if( !f.exists() ) throw new WikiException("Work directory does not exist: "+m_workDir); 517 if( !f.canRead() ) throw new WikiException("No permission to read work directory: "+m_workDir); 518 if( !f.canWrite() ) throw new WikiException("No permission to write to work directory: "+m_workDir); 519 if( !f.isDirectory() ) throw new WikiException("jspwiki.workDir does not point to a directory: "+m_workDir); 520 } catch( SecurityException e ) { 521 log.fatal( "Unable to find or create the working directory: "+m_workDir, e ); 522 throw new IllegalArgumentException( "Unable to find or create the working dir: " + m_workDir, e ); 523 } 524 525 log.info("JSPWiki working directory is '"+m_workDir+"'"); 526 527 m_saveUserInfo = TextUtil.getBooleanProperty( props, PROP_STOREUSERNAME, m_saveUserInfo ); 528 m_useUTF8 = StandardCharsets.UTF_8.name().equals( TextUtil.getStringProperty( props, PROP_ENCODING, StandardCharsets.ISO_8859_1.name() ) ); 529 m_beautifyTitle = TextUtil.getBooleanProperty( props, PROP_BEAUTIFYTITLE, m_beautifyTitle ); 530 m_templateDir = TextUtil.getStringProperty( props, PROP_TEMPLATEDIR, "default" ); 531 enforceValidTemplateDirectory(); 532 m_frontPage = TextUtil.getStringProperty( props, PROP_FRONTPAGE, "Main" ); 533 534 // 535 // Initialize the important modules. Any exception thrown by the 536 // managers means that we will not start up. 537 // 538 539 // FIXME: This part of the code is getting unwieldy. We must think 540 // of a better way to do the startup-sequence. 541 try 542 { 543 Class< ? > urlclass = ClassUtil.findClass( "org.apache.wiki.url", 544 TextUtil.getStringProperty( props, PROP_URLCONSTRUCTOR, "DefaultURLConstructor" ) ); 545 m_urlConstructor = (URLConstructor) urlclass.getDeclaredConstructor().newInstance(); 546 m_urlConstructor.initialize( this, props ); 547 548 m_pageManager = ClassUtil.getMappedObject( PageManager.class.getName(), this, props ); 549 m_pluginManager = ClassUtil.getMappedObject( PluginManager.class.getName(), this, props ); 550 m_differenceManager = ClassUtil.getMappedObject( DifferenceManager.class.getName(), this, props ); 551 m_attachmentManager = ClassUtil.getMappedObject( AttachmentManager.class.getName(), this, props ); 552 m_variableManager = ClassUtil.getMappedObject( VariableManager.class.getName(), props ); 553 m_renderingManager = ClassUtil.getMappedObject( RenderingManager.class.getName() ); 554 m_searchManager = ClassUtil.getMappedObject( SearchManager.class.getName(), this, props ); 555 m_authenticationManager = ClassUtil.getMappedObject( AuthenticationManager.class.getName() ); 556 m_authorizationManager = ClassUtil.getMappedObject( AuthorizationManager.class.getName() ); 557 m_userManager = ClassUtil.getMappedObject( UserManager.class.getName() ); 558 m_groupManager = ClassUtil.getMappedObject( GroupManager.class.getName() ); 559 m_editorManager = ClassUtil.getMappedObject( EditorManager.class.getName(), this ); 560 m_editorManager.initialize( props ); 561 562 m_progressManager = new ProgressManager(); 563 564 // Initialize the authentication, authorization, user and acl managers 565 m_authenticationManager.initialize( this, props ); 566 m_authorizationManager.initialize( this, props ); 567 m_userManager.initialize( this, props ); 568 m_groupManager.initialize( this, props ); 569 m_aclManager = getAclManager(); 570 571 // Start the Workflow manager 572 m_workflowMgr = ClassUtil.getMappedObject(WorkflowManager.class.getName()); 573 m_workflowMgr.initialize(this, props); 574 m_tasksManager = ClassUtil.getMappedObject(TasksManager.class.getName()); 575 576 m_internationalizationManager = ClassUtil.getMappedObject(InternationalizationManager.class.getName(),this); 577 m_templateManager = ClassUtil.getMappedObject(TemplateManager.class.getName(), this, props ); 578 579 // Since we want to use a page filters initilize() method 580 // as a engine startup listener where we can initialize global event listeners, 581 // it must be called lastly, so that all object references in the engine 582 // are availabe to the initialize() method 583 m_filterManager = ClassUtil.getMappedObject(FilterManager.class.getName(), this, props ); 584 585 m_adminBeanManager = ClassUtil.getMappedObject(AdminBeanManager.class.getName(),this); 586 587 // RenderingManager depends on FilterManager events. 588 m_renderingManager.initialize( this, props ); 589 590 // 591 // ReferenceManager has the side effect of loading all 592 // pages. Therefore after this point, all page attributes 593 // are available. 594 // 595 // initReferenceManager is indirectly using m_filterManager, therefore 596 // it has to be called after it was initialized. 597 // 598 initReferenceManager(); 599 600 // 601 // Hook the different manager routines into the system. 602 // 603 m_filterManager.addPageFilter(m_referenceManager, -1001 ); 604 m_filterManager.addPageFilter(m_searchManager, -1002 ); 605 } 606 607 catch( RuntimeException e ) 608 { 609 // RuntimeExceptions may occur here, even if they shouldn't. 610 log.fatal( "Failed to start managers.", e ); 611 throw new WikiException( "Failed to start managers: " + e.getMessage(), e ); 612 } 613 catch (ClassNotFoundException e) 614 { 615 log.fatal( "JSPWiki could not start, URLConstructor was not found: " + e.getMessage(), e ); 616 throw new WikiException(e.getMessage(), e ); 617 } 618 catch (InstantiationException e) 619 { 620 log.fatal( "JSPWiki could not start, URLConstructor could not be instantiated: " + e.getMessage(), e ); 621 throw new WikiException(e.getMessage(), e ); 622 } 623 catch (IllegalAccessException e) 624 { 625 log.fatal( "JSPWiki could not start, URLConstructor cannot be accessed: " + e.getMessage(), e ); 626 throw new WikiException(e.getMessage(), e ); 627 } 628 catch( Exception e ) 629 { 630 // Final catch-all for everything 631 log.fatal( "JSPWiki could not start, due to an unknown exception when starting.",e ); 632 throw new WikiException( "Failed to start. Caused by: " + e.getMessage() + 633 "; please check log files for better information.", e ); 634 } 635 636 // 637 // Initialize the good-to-have-but-not-fatal modules. 638 // 639 try 640 { 641 if( TextUtil.getBooleanProperty( props, 642 RSSGenerator.PROP_GENERATE_RSS, 643 false ) ) 644 { 645 m_rssGenerator = (RSSGenerator)ClassUtil.getMappedObject(RSSGenerator.class.getName(), this, props ); 646 } 647 648 m_pageRenamer = (PageRenamer)ClassUtil.getMappedObject(PageRenamer.class.getName(), this, props ); 649 } 650 catch( Exception e ) 651 { 652 log.error( "Unable to start RSS generator - JSPWiki will still work, "+ 653 "but there will be no RSS feed.", e ); 654 } 655 656 // Start the RSS generator & generator thread 657 if( m_rssGenerator != null ) 658 { 659 m_rssFile = TextUtil.getStringProperty( props, 660 RSSGenerator.PROP_RSSFILE, "rss.rdf" ); 661 File rssFile=null; 662 if (m_rssFile.startsWith(File.separator)) 663 { 664 // honor absolute pathnames: 665 rssFile = new File(m_rssFile ); 666 } 667 else 668 { 669 // relative path names are anchored from the webapp root path: 670 rssFile = new File( getRootPath(), m_rssFile ); 671 } 672 int rssInterval = TextUtil.getIntegerProperty( props, 673 RSSGenerator.PROP_INTERVAL, 3600 ); 674 RSSThread rssThread = new RSSThread( this, rssFile, rssInterval ); 675 rssThread.start(); 676 } 677 678 fireEvent( WikiEngineEvent.INITIALIZED ); // initialization complete 679 680 log.info("WikiEngine configured."); 681 m_isConfigured = true; 682 } 683 684 /** 685 * Checks if the template directory specified in the wiki's properties actually exists. If it doesn't, then {@code m_templateDir} is 686 * set to {@link #DEFAULT_TEMPLATE_NAME}. 687 * <p> 688 * This checks the existence of the <tt>ViewTemplate.jsp</tt> file, which exists in every template using {@code m_servletContext.getRealPath("/")}. 689 * <p> 690 * {@code m_servletContext.getRealPath("/")} can return {@code null} on certain servers/conditions (f.ex, packed wars), an extra check 691 * against {@code m_servletContext.getResource} is made. 692 */ 693 void enforceValidTemplateDirectory() { 694 if( m_servletContext != null ) { 695 final String viewTemplate = "templates" + File.separator + getTemplateDir() + File.separator + "ViewTemplate.jsp"; 696 boolean exists = new File( m_servletContext.getRealPath("/") + viewTemplate ).exists(); 697 if( !exists ) { 698 try { 699 URL url = m_servletContext.getResource( viewTemplate ); 700 exists = url != null && StringUtils.isNotEmpty( url.getFile() ); 701 } catch( MalformedURLException e ) { 702 exists = false; 703 } 704 } 705 if( !exists ) { 706 log.warn( getTemplateDir() + " template not found, updating WikiEngine's default template to " + DEFAULT_TEMPLATE_NAME ); 707 m_templateDir = DEFAULT_TEMPLATE_NAME; 708 } 709 } 710 } 711 712 /** 713 * Initializes the reference manager. Scans all existing WikiPages for 714 * internal links and adds them to the ReferenceManager object. 715 * 716 * @throws WikiException If the reference manager initialization fails. 717 */ 718 public void initReferenceManager() throws WikiException { 719 try { 720 ArrayList<WikiPage> pages = new ArrayList<>(); 721 pages.addAll( m_pageManager.getAllPages() ); 722 pages.addAll( m_attachmentManager.getAllAttachments() ); 723 724 // Build a new manager with default key lists. 725 if( m_referenceManager == null ) 726 { 727 m_referenceManager = ClassUtil.getMappedObject(ReferenceManager.class.getName(), this ); 728 m_referenceManager.initialize( pages ); 729 } 730 731 } catch( ProviderException e ) { 732 log.fatal("PageProvider is unable to list pages: ", e); 733 } catch( ReflectiveOperationException | IllegalArgumentException e ) { 734 throw new WikiException( "Could not instantiate ReferenceManager: " + e.getMessage(), e ); 735 } 736 } 737 738 /** 739 * Returns the set of properties that the WikiEngine was initialized 740 * with. Note that this method returns a direct reference, so it's possible 741 * to manipulate the properties. However, this is not advised unless you 742 * really know what you're doing. 743 * 744 * @return The wiki properties 745 */ 746 747 public Properties getWikiProperties() 748 { 749 return m_properties; 750 } 751 752 /** 753 * Returns the JSPWiki working directory set with "jspwiki.workDir". 754 * 755 * @since 2.1.100 756 * @return The working directory. 757 */ 758 public String getWorkDir() 759 { 760 return m_workDir; 761 } 762 763 /** 764 * Returns the current template directory. 765 * 766 * @since 1.9.20 767 * @return The template directory as initialized by the engine. 768 */ 769 public String getTemplateDir() 770 { 771 return m_templateDir; 772 } 773 774 /** 775 * Returns the current TemplateManager. 776 * 777 * @return A TemplateManager instance. 778 */ 779 public TemplateManager getTemplateManager() 780 { 781 return m_templateManager; 782 } 783 784 /** 785 * Returns the base URL, telling where this Wiki actually lives. 786 * 787 * @since 1.6.1 788 * @return The Base URL. 789 */ 790 public String getBaseURL() { 791 return m_servletContext.getContextPath(); 792 } 793 794 /** 795 * Returns the moment when this engine was started. 796 * 797 * @since 2.0.15. 798 * @return The start time of this wiki. 799 */ 800 public Date getStartTime() 801 { 802 return (Date)m_startTime.clone(); 803 } 804 805 /** 806 * <p> 807 * Returns the basic absolute URL to a page, without any modifications. You 808 * may add any parameters to this. 809 * </p> 810 * <p> 811 * Since 2.3.90 it is safe to call this method with <code>null</code> 812 * pageName, in which case it will default to the front page. 813 * </p> 814 * @since 2.0.3 815 * @param pageName The name of the page. May be null, in which case defaults to the front page. 816 * @return An absolute URL to the page. 817 */ 818 public String getViewURL( String pageName ) { 819 if( pageName == null ) { 820 pageName = getFrontPage(); 821 } 822 return getURLConstructor().makeURL( WikiContext.VIEW, pageName, "absolute".equals( PROP_REFSTYLE ), null ); 823 } 824 825 /** 826 * Returns an URL if a WikiContext is not available. 827 * 828 * @param context The WikiContext (VIEW, EDIT, etc...) 829 * @param pageName Name of the page, as usual 830 * @param params List of parameters. May be null, if no parameters. 831 * @param absolute If true, will generate an absolute URL regardless of properties setting. 832 * @return An URL (absolute or relative). 833 */ 834 public String getURL( final String context, String pageName, final String params, final boolean absolute ) { 835 if( pageName == null ) { 836 pageName = getFrontPage(); 837 } 838 return m_urlConstructor.makeURL( context, pageName, absolute, params ); 839 } 840 841 /** 842 * Returns the default front page, if no page is used. 843 * 844 * @return The front page name. 845 */ 846 847 public String getFrontPage() 848 { 849 return m_frontPage; 850 } 851 852 /** 853 * Returns the ServletContext that this particular WikiEngine was 854 * initialized with. <B>It may return null</B>, if the WikiEngine is not 855 * running inside a servlet container! 856 * 857 * @since 1.7.10 858 * @return ServletContext of the WikiEngine, or null. 859 */ 860 861 public ServletContext getServletContext() 862 { 863 return m_servletContext; 864 } 865 866 /** 867 * Returns an URL to some other Wiki that we know. 868 * 869 * @param wikiName The name of the other wiki. 870 * @return null, if no such reference was found. 871 */ 872 public String getInterWikiURL( final String wikiName ) 873 { 874 return TextUtil.getStringProperty(m_properties,PROP_INTERWIKIREF+wikiName,null); 875 } 876 877 /** 878 * Returns a collection of all supported InterWiki links. 879 * 880 * @return A Collection of Strings. 881 */ 882 public Collection< String > getAllInterWikiLinks() 883 { 884 ArrayList< String > list = new ArrayList< >(); 885 886 for( Enumeration< ? > i = m_properties.propertyNames(); i.hasMoreElements(); ) 887 { 888 String prop = ( String )i.nextElement(); 889 890 if( prop.startsWith( PROP_INTERWIKIREF ) ) 891 { 892 list.add( prop.substring( prop.lastIndexOf( "." ) + 1 ) ); 893 } 894 } 895 896 return list; 897 } 898 899 /** 900 * Returns a collection of all image types that get inlined. 901 * 902 * @return A Collection of Strings with a regexp pattern. 903 */ 904 public Collection< String > getAllInlinedImagePatterns() 905 { 906 Properties props = getWikiProperties(); 907 ArrayList<String> ptrnlist = new ArrayList<>(); 908 909 for( Enumeration< ? > e = props.propertyNames(); e.hasMoreElements(); ) 910 { 911 String name = ( String )e.nextElement(); 912 913 if( name.startsWith( PROP_INLINEIMAGEPTRN ) ) 914 { 915 String ptrn = TextUtil.getStringProperty( props, name, null ); 916 917 ptrnlist.add( ptrn ); 918 } 919 } 920 921 if( ptrnlist.size() == 0 ) 922 { 923 ptrnlist.add( DEFAULT_INLINEPATTERN ); 924 } 925 926 return ptrnlist; 927 } 928 929 /** 930 * <p>If the page is a special page, then returns a direct URL 931 * to that page. Otherwise returns <code>null</code>. 932 * This method delegates requests to 933 * {@link org.apache.wiki.ui.CommandResolver#getSpecialPageReference(String)}. 934 * </p> 935 * <p> 936 * Special pages are defined in jspwiki.properties using the jspwiki.specialPage 937 * setting. They're typically used to give Wiki page names to e.g. custom JSP 938 * pages. 939 * </p> 940 * 941 * @param original The page to check 942 * @return A reference to the page, or null, if there's no special page. 943 */ 944 public String getSpecialPageReference( String original ) 945 { 946 return m_commandResolver.getSpecialPageReference( original ); 947 } 948 949 /** 950 * Returns the name of the application. 951 * 952 * @return A string describing the name of this application. 953 */ 954 955 // FIXME: Should use servlet context as a default instead of a constant. 956 public String getApplicationName() 957 { 958 String appName = TextUtil.getStringProperty(m_properties,PROP_APPNAME,Release.APPNAME); 959 960 return MarkupParser.cleanLink( appName ); 961 } 962 963 /** 964 * Beautifies the title of the page by appending spaces in suitable 965 * places, if the user has so decreed in the properties when constructing 966 * this WikiEngine. However, attachment names are only beautified by 967 * the name. 968 * 969 * @param title The title to beautify 970 * @return A beautified title (or, if beautification is off, 971 * returns the title without modification) 972 * @since 1.7.11 973 */ 974 public String beautifyTitle( String title ) 975 { 976 if( m_beautifyTitle ) 977 { 978 try 979 { 980 Attachment att = m_attachmentManager.getAttachmentInfo(title); 981 982 if(att == null) 983 { 984 return TextUtil.beautifyString( title ); 985 } 986 987 String parent = TextUtil.beautifyString( att.getParentName() ); 988 989 return parent + "/" + att.getFileName(); 990 } 991 catch( ProviderException e ) 992 { 993 return title; 994 } 995 } 996 997 return title; 998 } 999 1000 /** 1001 * Beautifies the title of the page by appending non-breaking spaces 1002 * in suitable places. This is really suitable only for HTML output, 1003 * as it uses the &nbsp; -character. 1004 * 1005 * @param title The title to beautify 1006 * @return A beautified title. 1007 * @since 2.1.127 1008 */ 1009 public String beautifyTitleNoBreak( String title ) 1010 { 1011 if( m_beautifyTitle ) 1012 { 1013 return TextUtil.beautifyString( title, " " ); 1014 } 1015 1016 return title; 1017 } 1018 1019 /** 1020 * Returns true, if the requested page (or an alias) exists. Will consider 1021 * any version as existing. Will also consider attachments. 1022 * 1023 * @param page WikiName of the page. 1024 * @return true, if page (or attachment) exists. 1025 */ 1026 public boolean pageExists( String page ) 1027 { 1028 Attachment att = null; 1029 1030 try 1031 { 1032 if( m_commandResolver.getSpecialPageReference(page) != null ) return true; 1033 1034 if( getFinalPageName( page ) != null ) 1035 { 1036 return true; 1037 } 1038 1039 att = getAttachmentManager().getAttachmentInfo( (WikiContext)null, page ); 1040 } 1041 catch( ProviderException e ) 1042 { 1043 log.debug("pageExists() failed to find attachments",e); 1044 } 1045 1046 return att != null; 1047 } 1048 1049 /** 1050 * Returns true, if the requested page (or an alias) exists with the 1051 * requested version. 1052 * 1053 * @param page Page name 1054 * @param version Page version 1055 * @return True, if page (or alias, or attachment) exists 1056 * @throws ProviderException If the provider fails. 1057 */ 1058 public boolean pageExists( String page, int version ) 1059 throws ProviderException 1060 { 1061 if( m_commandResolver.getSpecialPageReference(page) != null ) return true; 1062 1063 String finalName = getFinalPageName( page ); 1064 1065 boolean isThere = false; 1066 1067 if( finalName != null ) 1068 { 1069 // 1070 // Go and check if this particular version of this page 1071 // exists. 1072 // 1073 isThere = m_pageManager.pageExists( finalName, version ); 1074 } 1075 1076 if( isThere == false ) 1077 { 1078 // 1079 // Go check if such an attachment exists. 1080 // 1081 try 1082 { 1083 isThere = getAttachmentManager().getAttachmentInfo( (WikiContext)null, page, version ) != null; 1084 } 1085 catch( ProviderException e ) 1086 { 1087 log.debug("pageExists() failed to find attachments",e); 1088 } 1089 } 1090 1091 return isThere; 1092 } 1093 1094 /** 1095 * Returns true, if the requested page (or an alias) exists, with the 1096 * specified version in the WikiPage. 1097 * 1098 * @param page A WikiPage object describing the name and version. 1099 * @return true, if the page (or alias, or attachment) exists. 1100 * @throws ProviderException If something goes badly wrong. 1101 * @since 2.0 1102 */ 1103 public boolean pageExists( WikiPage page ) 1104 throws ProviderException 1105 { 1106 if( page != null ) 1107 { 1108 return pageExists( page.getName(), page.getVersion() ); 1109 } 1110 return false; 1111 } 1112 1113 /** 1114 * Returns the correct page name, or null, if no such 1115 * page can be found. Aliases are considered. This 1116 * method simply delegates to 1117 * {@link org.apache.wiki.ui.CommandResolver#getFinalPageName(String)}. 1118 * @since 2.0 1119 * @param page Page name. 1120 * @return The rewritten page name, or null, if the page does not exist. 1121 * @throws ProviderException If something goes wrong in the backend. 1122 */ 1123 public String getFinalPageName( String page ) 1124 throws ProviderException 1125 { 1126 return m_commandResolver.getFinalPageName( page ); 1127 } 1128 1129 /** 1130 * Turns a WikiName into something that can be 1131 * called through using an URL. 1132 * 1133 * @since 1.4.1 1134 * @param pagename A name. Can be actually any string. 1135 * @return A properly encoded name. 1136 * @see #decodeName(String) 1137 */ 1138 public String encodeName( String pagename ) 1139 { 1140 try 1141 { 1142 return URLEncoder.encode( pagename, m_useUTF8 ? "UTF-8" : "ISO-8859-1" ); 1143 } 1144 catch( UnsupportedEncodingException e ) 1145 { 1146 throw new InternalWikiException( "ISO-8859-1 not a supported encoding!?! Your platform is borked." , e); 1147 } 1148 } 1149 1150 /** 1151 * Decodes a URL-encoded request back to regular life. This properly heeds 1152 * the encoding as defined in the settings file. 1153 * 1154 * @param pagerequest The URL-encoded string to decode 1155 * @return A decoded string. 1156 * @see #encodeName(String) 1157 */ 1158 public String decodeName( final String pagerequest ) { 1159 try { 1160 return URLDecoder.decode( pagerequest, m_useUTF8 ? "UTF-8" : "ISO-8859-1" ); 1161 } catch( UnsupportedEncodingException e ) { 1162 throw new InternalWikiException("ISO-8859-1 not a supported encoding!?! Your platform is borked.", e); 1163 } 1164 } 1165 1166 /** 1167 * Returns the IANA name of the character set encoding we're 1168 * supposed to be using right now. 1169 * 1170 * @since 1.5.3 1171 * @return The content encoding (either UTF-8 or ISO-8859-1). 1172 */ 1173 public Charset getContentEncoding() { 1174 if( m_useUTF8 ) { 1175 return StandardCharsets.UTF_8; 1176 } 1177 return StandardCharsets.ISO_8859_1; 1178 } 1179 1180 /** 1181 * Returns the {@link org.apache.wiki.workflow.WorkflowManager} associated with this 1182 * WikiEngine. If the WIkiEngine has not been initialized, this method will return 1183 * <code>null</code>. 1184 * @return the task queue 1185 */ 1186 public WorkflowManager getWorkflowManager() 1187 { 1188 return m_workflowMgr; 1189 } 1190 1191 /** 1192 * Returns the un-HTMLized text of the latest version of a page. 1193 * This method also replaces the < and & -characters with 1194 * their respective HTML entities, thus making it suitable 1195 * for inclusion on an HTML page. If you want to have the 1196 * page text without any conversions, use getPureText(). 1197 * 1198 * @param page WikiName of the page to fetch. 1199 * @return WikiText. 1200 */ 1201 public String getText( String page ) 1202 { 1203 return getText( page, WikiPageProvider.LATEST_VERSION ); 1204 } 1205 1206 /** 1207 * Returns the un-HTMLized text of the given version of a page. 1208 * This method also replaces the < and & -characters with 1209 * their respective HTML entities, thus making it suitable 1210 * for inclusion on an HTML page. If you want to have the 1211 * page text without any conversions, use getPureText(). 1212 * 1213 * 1214 * @param page WikiName of the page to fetch 1215 * @param version Version of the page to fetch 1216 * @return WikiText. 1217 */ 1218 public String getText( String page, int version ) 1219 { 1220 String result = getPureText( page, version ); 1221 1222 // 1223 // Replace ampersand first, or else all quotes and stuff 1224 // get replaced as well with " etc. 1225 // 1226 /* 1227 result = TextUtil.replaceString( result, "&", "&" ); 1228 */ 1229 1230 result = TextUtil.replaceEntities( result ); 1231 1232 return result; 1233 } 1234 1235 /** 1236 * Returns the un-HTMLized text of the given version of a page in 1237 * the given context. USE THIS METHOD if you don't know what 1238 * doing. 1239 * <p> 1240 * This method also replaces the < and & -characters with 1241 * their respective HTML entities, thus making it suitable 1242 * for inclusion on an HTML page. If you want to have the 1243 * page text without any conversions, use getPureText(). 1244 * 1245 * @since 1.9.15. 1246 * @param context The WikiContext 1247 * @param page A page reference (not an attachment) 1248 * @return The page content as HTMLized String. 1249 * @see #getPureText(WikiPage) 1250 */ 1251 public String getText( WikiContext context, WikiPage page ) 1252 { 1253 return getText( page.getName(), page.getVersion() ); 1254 } 1255 1256 1257 /** 1258 * Returns the pure text of a page, no conversions. Use this 1259 * if you are writing something that depends on the parsing 1260 * of the page. Note that you should always check for page 1261 * existence through pageExists() before attempting to fetch 1262 * the page contents. 1263 * 1264 * @param page The name of the page to fetch. 1265 * @param version If WikiPageProvider.LATEST_VERSION, then uses the 1266 * latest version. 1267 * @return The page contents. If the page does not exist, 1268 * returns an empty string. 1269 */ 1270 // FIXME: Should throw an exception on unknown page/version? 1271 public String getPureText( String page, int version ) 1272 { 1273 String result = null; 1274 1275 try 1276 { 1277 result = m_pageManager.getPageText( page, version ); 1278 } 1279 catch( ProviderException e ) 1280 { 1281 // FIXME 1282 } 1283 finally 1284 { 1285 if( result == null ) 1286 result = ""; 1287 } 1288 1289 return result; 1290 } 1291 1292 /** 1293 * Returns the pure text of a page, no conversions. Use this 1294 * if you are writing something that depends on the parsing 1295 * the page. Note that you should always check for page 1296 * existence through pageExists() before attempting to fetch 1297 * the page contents. 1298 * 1299 * @param page A handle to the WikiPage 1300 * @return String of WikiText. 1301 * @since 2.1.13. 1302 */ 1303 public String getPureText( WikiPage page ) 1304 { 1305 return getPureText( page.getName(), page.getVersion() ); 1306 } 1307 1308 /** 1309 * Returns the converted HTML of the page using a different 1310 * context than the default context. 1311 * 1312 * @param context A WikiContext in which you wish to render this page in. 1313 * @param page WikiPage reference. 1314 * @return HTML-rendered version of the page. 1315 */ 1316 1317 public String getHTML( WikiContext context, WikiPage page ) 1318 { 1319 String pagedata = null; 1320 1321 pagedata = getPureText( page.getName(), page.getVersion() ); 1322 1323 String res = textToHTML( context, pagedata ); 1324 1325 return res; 1326 } 1327 1328 /** 1329 * Returns the converted HTML of the page. 1330 * 1331 * @param page WikiName of the page to convert. 1332 * @return HTML-rendered version of the page. 1333 */ 1334 public String getHTML( String page ) 1335 { 1336 return getHTML( page, WikiPageProvider.LATEST_VERSION ); 1337 } 1338 1339 /** 1340 * Returns the converted HTML of the page's specific version. 1341 * The version must be a positive integer, otherwise the current 1342 * version is returned. 1343 * 1344 * @param pagename WikiName of the page to convert. 1345 * @param version Version number to fetch 1346 * @return HTML-rendered page text. 1347 */ 1348 public String getHTML( String pagename, int version ) 1349 { 1350 WikiPage page = getPage( pagename, version ); 1351 1352 WikiContext context = new WikiContext( this, 1353 page ); 1354 context.setRequestContext( WikiContext.NONE ); 1355 1356 String res = getHTML( context, page ); 1357 1358 return res; 1359 } 1360 1361 /** 1362 * Converts raw page data to HTML. 1363 * 1364 * @param pagedata Raw page data to convert to HTML 1365 * @param context The WikiContext in which the page is to be rendered 1366 * @return Rendered page text 1367 */ 1368 public String textToHTML( WikiContext context, String pagedata ) 1369 { 1370 String result = ""; 1371 1372 boolean runFilters = "true".equals(m_variableManager.getValue(context,PROP_RUNFILTERS,"true")); 1373 1374 StopWatch sw = new StopWatch(); 1375 sw.start(); 1376 try 1377 { 1378 if( runFilters ) 1379 pagedata = m_filterManager.doPreTranslateFiltering( context, pagedata ); 1380 1381 result = m_renderingManager.getHTML( context, pagedata ); 1382 1383 if( runFilters ) 1384 result = m_filterManager.doPostTranslateFiltering( context, result ); 1385 } 1386 catch( FilterException e ) 1387 { 1388 // FIXME: Don't yet know what to do 1389 } 1390 sw.stop(); 1391 if( log.isDebugEnabled() ) 1392 log.debug("Page "+context.getRealPage().getName()+" rendered, took "+sw ); 1393 1394 return result; 1395 } 1396 1397 /** 1398 * Protected method that signals that the WikiEngine will be 1399 * shut down by the servlet container. It is called by 1400 * {@link WikiServlet#destroy()}. When this method is called, 1401 * it fires a "shutdown" WikiEngineEvent to all registered 1402 * listeners. 1403 */ 1404 protected void shutdown() 1405 { 1406 fireEvent( WikiEngineEvent.SHUTDOWN ); 1407 m_filterManager.destroy(); 1408 } 1409 1410 /** 1411 * Reads a WikiPageful of data from a String and returns all links 1412 * internal to this Wiki in a Collection. 1413 * 1414 * @param page The WikiPage to scan 1415 * @param pagedata The page contents 1416 * @return a Collection of Strings 1417 */ 1418 public Collection< String > scanWikiLinks( WikiPage page, String pagedata ) { 1419 LinkCollector localCollector = new LinkCollector(); 1420 1421 textToHTML( new WikiContext( this, page ), 1422 pagedata, 1423 localCollector, 1424 null, 1425 localCollector, 1426 false, 1427 true ); 1428 1429 return localCollector.getLinks(); 1430 } 1431 1432 /** 1433 * Just convert WikiText to HTML. 1434 * 1435 * @param context The WikiContext in which to do the conversion 1436 * @param pagedata The data to render 1437 * @param localLinkHook Is called whenever a wiki link is found 1438 * @param extLinkHook Is called whenever an external link is found 1439 * 1440 * @return HTML-rendered page text. 1441 */ 1442 1443 public String textToHTML( WikiContext context, 1444 String pagedata, 1445 StringTransmutator localLinkHook, 1446 StringTransmutator extLinkHook ) 1447 { 1448 return textToHTML( context, pagedata, localLinkHook, extLinkHook, null, true, false ); 1449 } 1450 1451 /** 1452 * Just convert WikiText to HTML. 1453 * 1454 * @param context The WikiContext in which to do the conversion 1455 * @param pagedata The data to render 1456 * @param localLinkHook Is called whenever a wiki link is found 1457 * @param extLinkHook Is called whenever an external link is found 1458 * @param attLinkHook Is called whenever an attachment link is found 1459 * @return HTML-rendered page text. 1460 */ 1461 1462 public String textToHTML( WikiContext context, 1463 String pagedata, 1464 StringTransmutator localLinkHook, 1465 StringTransmutator extLinkHook, 1466 StringTransmutator attLinkHook ) 1467 { 1468 return textToHTML( context, pagedata, localLinkHook, extLinkHook, attLinkHook, true, false ); 1469 } 1470 1471 /** 1472 * Helper method for doing the HTML translation. 1473 * 1474 * @param context The WikiContext in which to do the conversion 1475 * @param pagedata The data to render 1476 * @param localLinkHook Is called whenever a wiki link is found 1477 * @param extLinkHook Is called whenever an external link is found 1478 * @param parseAccessRules Parse the access rules if we encounter them 1479 * @param justParse Just parses the pagedata, does not actually render. In this case, 1480 * this methods an empty string. 1481 * @return HTML-rendered page text. 1482 1483 */ 1484 private String textToHTML( WikiContext context, 1485 String pagedata, 1486 StringTransmutator localLinkHook, 1487 StringTransmutator extLinkHook, 1488 StringTransmutator attLinkHook, 1489 boolean parseAccessRules, 1490 boolean justParse ) 1491 { 1492 String result = ""; 1493 1494 if( pagedata == null ) 1495 { 1496 log.error("NULL pagedata to textToHTML()"); 1497 return null; 1498 } 1499 1500 boolean runFilters = "true".equals(m_variableManager.getValue(context,PROP_RUNFILTERS,"true")); 1501 1502 try 1503 { 1504 StopWatch sw = new StopWatch(); 1505 sw.start(); 1506 1507 if( runFilters && m_filterManager != null ) 1508 pagedata = m_filterManager.doPreTranslateFiltering( context, pagedata ); 1509 1510 MarkupParser mp = m_renderingManager.getParser( context, pagedata ); 1511 mp.addLocalLinkHook( localLinkHook ); 1512 mp.addExternalLinkHook( extLinkHook ); 1513 mp.addAttachmentLinkHook( attLinkHook ); 1514 1515 if( !parseAccessRules ) mp.disableAccessRules(); 1516 1517 WikiDocument doc = mp.parse(); 1518 1519 // 1520 // In some cases it's better just to parse, not to render 1521 // 1522 if( !justParse ) 1523 { 1524 result = m_renderingManager.getHTML( context, doc ); 1525 1526 if( runFilters && m_filterManager != null ) 1527 result = m_filterManager.doPostTranslateFiltering( context, result ); 1528 } 1529 1530 sw.stop(); 1531 1532 if( log.isDebugEnabled() ) 1533 log.debug("Page "+context.getRealPage().getName()+" rendered, took "+sw ); 1534 } 1535 catch( IOException e ) 1536 { 1537 log.error( "Failed to scan page data: ", e ); 1538 } 1539 catch( FilterException e ) 1540 { 1541 log.error( "page filter threw exception: ", e ); 1542 // FIXME: Don't yet know what to do 1543 } 1544 1545 return result; 1546 } 1547 1548 /** 1549 * Updates all references for the given page. 1550 * 1551 * @param page wiki page for which references should be updated 1552 */ 1553 public void updateReferences( WikiPage page ) 1554 { 1555 String pageData = getPureText( page.getName(), WikiProvider.LATEST_VERSION ); 1556 1557 m_referenceManager.updateReferences( page.getName(), 1558 scanWikiLinks( page, pageData ) ); 1559 } 1560 1561 1562 /** 1563 * Writes the WikiText of a page into the page repository. If the <code>jspwiki.properties</code> file contains 1564 * the property <code>jspwiki.approver.workflow.saveWikiPage</code> and its value resolves to a valid user, 1565 * {@link org.apache.wiki.auth.authorize.Group} or {@link org.apache.wiki.auth.authorize.Role}, this method will 1566 * place a {@link org.apache.wiki.workflow.Decision} in the approver's workflow inbox and throw a 1567 * {@link org.apache.wiki.workflow.DecisionRequiredException}. If the submitting user is authenticated and the 1568 * page save is rejected, a notification will be placed in the user's decision queue. 1569 * 1570 * @since 2.1.28 1571 * @param context The current WikiContext 1572 * @param text The Wiki markup for the page. 1573 * @throws WikiException if the save operation encounters an error during the save operation. If the page-save 1574 * operation requires approval, the exception will be of type {@link org.apache.wiki.workflow.DecisionRequiredException}. 1575 * Individual PageFilters, such as the {@link org.apache.wiki.filters.SpamFilter} may also throw a 1576 * {@link org.apache.wiki.api.exceptions.RedirectException}. 1577 */ 1578 public void saveText( WikiContext context, String text ) throws WikiException { 1579 // Check if page data actually changed; bail if not 1580 WikiPage page = context.getPage(); 1581 String oldText = getPureText( page ); 1582 String proposedText = TextUtil.normalizePostData( text ); 1583 if ( oldText != null && oldText.equals( proposedText ) ) { 1584 return; 1585 } 1586 1587 // Check if creation of empty pages is allowed; bail if not 1588 boolean allowEmpty = TextUtil.getBooleanProperty( m_properties, PROP_ALLOW_CREATION_OF_EMPTY_PAGES, false ); 1589 if ( !allowEmpty && !pageExists( page ) && text.trim().equals( "" ) ) { 1590 return; 1591 } 1592 1593 // Create approval workflow for page save; add the diffed, proposed and old text versions as 1594 // Facts for the approver (if approval is required). If submitter is authenticated, any reject 1595 // messages will appear in his/her workflow inbox. 1596 WorkflowBuilder builder = WorkflowBuilder.getBuilder( this ); 1597 Principal submitter = context.getCurrentUser(); 1598 Step prepTask = m_tasksManager.buildPreSaveWikiPageTask( context, proposedText ); 1599 Step completionTask = m_tasksManager.buildSaveWikiPageTask(); 1600 String diffText = m_differenceManager.makeDiff( context, oldText, proposedText ); 1601 boolean isAuthenticated = context.getWikiSession().isAuthenticated(); 1602 Fact[] facts = new Fact[ 5 ]; 1603 facts[ 0 ] = new Fact( WorkflowManager.WF_WP_SAVE_FACT_PAGE_NAME, page.getName() ); 1604 facts[ 1 ] = new Fact( WorkflowManager.WF_WP_SAVE_FACT_DIFF_TEXT, diffText ); 1605 facts[ 2 ] = new Fact( WorkflowManager.WF_WP_SAVE_FACT_PROPOSED_TEXT, proposedText ); 1606 facts[ 3 ] = new Fact( WorkflowManager.WF_WP_SAVE_FACT_CURRENT_TEXT, oldText); 1607 facts[ 4 ] = new Fact( WorkflowManager.WF_WP_SAVE_FACT_IS_AUTHENTICATED, Boolean.valueOf( isAuthenticated ) ); 1608 String rejectKey = isAuthenticated ? WorkflowManager.WF_WP_SAVE_REJECT_MESSAGE_KEY : null; 1609 Workflow workflow = builder.buildApprovalWorkflow( submitter, 1610 WorkflowManager.WF_WP_SAVE_APPROVER, 1611 prepTask, 1612 WorkflowManager.WF_WP_SAVE_DECISION_MESSAGE_KEY, 1613 facts, 1614 completionTask, 1615 rejectKey ); 1616 m_workflowMgr.start( workflow ); 1617 1618 // Let callers know if the page-save requires approval 1619 if ( workflow.getCurrentStep() instanceof Decision ) { 1620 throw new DecisionRequiredException( "The page contents must be approved before they become active." ); 1621 } 1622 } 1623 1624 /** 1625 * Returns the number of pages in this Wiki 1626 * @return The total number of pages. 1627 */ 1628 public int getPageCount() 1629 { 1630 return m_pageManager.getTotalPageCount(); 1631 } 1632 1633 /** 1634 * Returns the provider name. 1635 * @return The full class name of the current page provider. 1636 */ 1637 1638 public String getCurrentProvider() 1639 { 1640 return m_pageManager.getProvider().getClass().getName(); 1641 } 1642 1643 /** 1644 * Return information about current provider. This method just calls 1645 * the corresponding PageManager method, which in turn calls the 1646 * provider method. 1647 * 1648 * @return A textual description of the current provider. 1649 * @since 1.6.4 1650 */ 1651 public String getCurrentProviderInfo() 1652 { 1653 return m_pageManager.getProviderDescription(); 1654 } 1655 1656 /** 1657 * Returns a Collection of WikiPages, sorted in time 1658 * order of last change (i.e. first object is the most 1659 * recently changed). This method also includes attachments. 1660 * 1661 * @return Set of WikiPage objects. 1662 */ 1663 1664 // FIXME: Should really get a Date object and do proper comparisons. This is terribly wasteful. 1665 public Set< WikiPage > getRecentChanges() 1666 { 1667 try { 1668 Collection<WikiPage> pages = m_pageManager.getAllPages(); 1669 Collection<Attachment> atts = m_attachmentManager.getAllAttachments(); 1670 1671 TreeSet<WikiPage> sortedPages = new TreeSet<>( new PageTimeComparator() ); 1672 1673 sortedPages.addAll( pages ); 1674 sortedPages.addAll( atts ); 1675 1676 return sortedPages; 1677 } catch( ProviderException e ) { 1678 log.error( "Unable to fetch all pages: ",e); 1679 return null; 1680 } 1681 } 1682 1683 /** 1684 * Finds the corresponding WikiPage object based on the page name. It always finds 1685 * the latest version of a page. 1686 * 1687 * @param pagereq The name of the page to look for. 1688 * @return A WikiPage object, or null, if the page by the name could not be found. 1689 */ 1690 1691 public WikiPage getPage( String pagereq ) 1692 { 1693 return getPage( pagereq, WikiProvider.LATEST_VERSION ); 1694 } 1695 1696 /** 1697 * Finds the corresponding WikiPage object base on the page name and version. 1698 * 1699 * @param pagereq The name of the page to look for. 1700 * @param version The version number to look for. May be WikiProvider.LATEST_VERSION, 1701 * in which case it will look for the latest version (and this method then becomes 1702 * the equivalent of getPage(String). 1703 * 1704 * @return A WikiPage object, or null, if the page could not be found; or if there 1705 * is no such version of the page. 1706 * @since 1.6.7. 1707 */ 1708 1709 public WikiPage getPage( String pagereq, int version ) 1710 { 1711 try 1712 { 1713 WikiPage p = m_pageManager.getPageInfo( pagereq, version ); 1714 1715 if( p == null ) 1716 { 1717 p = m_attachmentManager.getAttachmentInfo( (WikiContext)null, pagereq ); 1718 } 1719 1720 return p; 1721 } 1722 catch( ProviderException e ) 1723 { 1724 log.error( "Unable to fetch page info",e); 1725 return null; 1726 } 1727 } 1728 1729 1730 /** 1731 * Returns a Collection of WikiPages containing the version history of a page. 1732 * 1733 * @param page Name of the page to look for 1734 * @return an ordered List of WikiPages, each corresponding to a different revision of the page. 1735 */ 1736 public List< ? extends WikiPage > getVersionHistory( String page ) { 1737 List< ? extends WikiPage > c = null; 1738 1739 try { 1740 c = m_pageManager.getVersionHistory( page ); 1741 1742 if( c == null ) { 1743 c = m_attachmentManager.getVersionHistory( page ); 1744 } 1745 } catch( ProviderException e ) { 1746 log.error( "FIXME", e ); 1747 } 1748 1749 return c; 1750 } 1751 1752 /** 1753 * Returns a diff of two versions of a page. 1754 * <p> 1755 * Note that the API was changed in 2.6 to provide a WikiContext object! 1756 * 1757 * @param context The WikiContext of the page you wish to get a diff from 1758 * @param version1 Version number of the old page. If WikiPageProvider.LATEST_VERSION (-1), then uses current page. 1759 * @param version2 Version number of the new page. If WikiPageProvider.LATEST_VERSION (-1), then uses current page. 1760 * 1761 * @return A HTML-ized difference between two pages. If there is no difference, returns an empty string. 1762 */ 1763 public String getDiff( WikiContext context, int version1, int version2 ) 1764 { 1765 String page = context.getPage().getName(); 1766 String page1 = getPureText( page, version1 ); 1767 String page2 = getPureText( page, version2 ); 1768 1769 // Kludge to make diffs for new pages to work this way. 1770 1771 if( version1 == WikiPageProvider.LATEST_VERSION ) 1772 { 1773 page1 = ""; 1774 } 1775 1776 String diff = m_differenceManager.makeDiff( context, page1, page2 ); 1777 1778 return diff; 1779 } 1780 1781 /** 1782 * Returns this object's ReferenceManager. 1783 * @return The current ReferenceManager instance. 1784 * 1785 * @since 1.6.1 1786 */ 1787 public ReferenceManager getReferenceManager() 1788 { 1789 return m_referenceManager; 1790 } 1791 1792 /** 1793 * Returns the current rendering manager for this wiki application. 1794 * 1795 * @since 2.3.27 1796 * @return A RenderingManager object. 1797 */ 1798 public RenderingManager getRenderingManager() 1799 { 1800 return m_renderingManager; 1801 } 1802 1803 /** 1804 * Returns the current plugin manager. 1805 * 1806 * In 2.10 the PluginManager will be returned instead of the generic 1807 * 1808 * @since 1.6.1 1809 * @return The current PluginManager instance 1810 */ 1811 @SuppressWarnings("unchecked") 1812 public < T extends PluginManager > T getPluginManager() 1813 { 1814 return (T)m_pluginManager; 1815 } 1816 1817 /** 1818 * Returns the current variable manager. 1819 * @return The current VariableManager. 1820 */ 1821 1822 public VariableManager getVariableManager() 1823 { 1824 return m_variableManager; 1825 } 1826 1827 /** 1828 * Shortcut to getVariableManager().getValue(). However, this method does not 1829 * throw a NoSuchVariableException, but returns null in case the variable does 1830 * not exist. 1831 * 1832 * @param context WikiContext to look the variable in 1833 * @param name Name of the variable to look for 1834 * @return Variable value, or null, if there is no such variable. 1835 * @since 2.2 1836 */ 1837 public String getVariable( WikiContext context, String name ) { 1838 try { 1839 return m_variableManager.getValue( context, name ); 1840 } catch( NoSuchVariableException e ) { 1841 return null; 1842 } 1843 } 1844 1845 /** 1846 * Returns the current PageManager which is responsible for storing 1847 * and managing WikiPages. 1848 * 1849 * @return The current PageManager instance. 1850 */ 1851 public PageManager getPageManager() 1852 { 1853 return m_pageManager; 1854 } 1855 1856 /** 1857 * Returns the CommandResolver for this wiki engine. 1858 * @return the resolver 1859 */ 1860 public CommandResolver getCommandResolver() 1861 { 1862 return m_commandResolver; 1863 } 1864 1865 /** 1866 * Returns the current AttachmentManager, which is responsible for 1867 * storing and managing attachments. 1868 * 1869 * @since 1.9.31. 1870 * @return The current AttachmentManager instance 1871 */ 1872 public AttachmentManager getAttachmentManager() 1873 { 1874 return m_attachmentManager; 1875 } 1876 1877 /** 1878 * Returns the currently used authorization manager. 1879 * 1880 * @return The current AuthorizationManager instance 1881 */ 1882 public AuthorizationManager getAuthorizationManager() 1883 { 1884 return m_authorizationManager; 1885 } 1886 1887 /** 1888 * Returns the currently used authentication manager. 1889 * 1890 * @return The current AuthenticationManager instance. 1891 */ 1892 public AuthenticationManager getAuthenticationManager() 1893 { 1894 return m_authenticationManager; 1895 } 1896 1897 /** 1898 * Returns the manager responsible for the filters. 1899 * @since 2.1.88 1900 * @return The current FilterManager instance 1901 */ 1902 @SuppressWarnings("unchecked") 1903 public < T extends FilterManager > T getFilterManager() 1904 { 1905 return (T)m_filterManager; 1906 } 1907 1908 /** 1909 * Returns the manager responsible for searching the Wiki. 1910 * @since 2.2.21 1911 * @return The current SearchManager instance 1912 */ 1913 public SearchManager getSearchManager() 1914 { 1915 return m_searchManager; 1916 } 1917 1918 /** 1919 * Returns the progress manager we're using 1920 * @return A ProgressManager 1921 * @since 2.6 1922 */ 1923 public ProgressManager getProgressManager() 1924 { 1925 return m_progressManager; 1926 } 1927 1928 /** 1929 * Figure out to which page we are really going to. Considers 1930 * special page names from the jspwiki.properties, and possible aliases. 1931 * This method delgates requests to 1932 * {@link org.apache.wiki.WikiContext#getRedirectURL()}. 1933 * @param context The Wiki Context in which the request is being made. 1934 * @return A complete URL to the new page to redirect to 1935 * @since 2.2 1936 */ 1937 1938 public String getRedirectURL( final WikiContext context ) 1939 { 1940 return context.getRedirectURL(); 1941 } 1942 1943 /** 1944 * Shortcut to create a WikiContext from a supplied HTTP request, 1945 * using a default wiki context. 1946 * @param request the HTTP request 1947 * @param requestContext the default context to use 1948 * @return a new WikiContext object. 1949 * 1950 * @see org.apache.wiki.ui.CommandResolver 1951 * @see org.apache.wiki.ui.Command 1952 * @since 2.1.15. 1953 */ 1954 // FIXME: We need to have a version which takes a fixed page name as well, or check it elsewhere. 1955 public WikiContext createContext( final HttpServletRequest request, final String requestContext ) { 1956 if( !m_isConfigured ) { 1957 throw new InternalWikiException( "WikiEngine has not been properly started. It is likely that the configuration is faulty. Please check all logs for the possible reason." ); 1958 } 1959 1960 // Build the wiki context 1961 final Command command = m_commandResolver.findCommand( request, requestContext ); 1962 return new WikiContext( this, request, command ); 1963 } 1964 1965 /** 1966 * Deletes a page or an attachment completely, including all versions. If the page 1967 * does not exist, does nothing. 1968 * 1969 * @param pageName The name of the page. 1970 * @throws ProviderException If something goes wrong. 1971 */ 1972 public void deletePage( final String pageName ) throws ProviderException { 1973 final WikiPage p = getPage( pageName ); 1974 if( p != null ) { 1975 if( p instanceof Attachment ) { 1976 m_attachmentManager.deleteAttachment( ( Attachment )p ); 1977 } else { 1978 final Collection< String > refTo = m_referenceManager.findRefersTo( pageName ); 1979 // May return null, if the page does not exist or has not been indexed yet. 1980 1981 if( m_attachmentManager.hasAttachments( p ) ) { 1982 final List< Attachment > attachments = m_attachmentManager.listAttachments( p ); 1983 for( final Attachment attachment : attachments ) { 1984 if( refTo != null ) { 1985 refTo.remove( attachment.getName() ); 1986 } 1987 1988 m_attachmentManager.deleteAttachment( attachment ); 1989 } 1990 } 1991 m_pageManager.deletePage( p ); 1992 firePageEvent( WikiPageEvent.PAGE_DELETED, pageName ); 1993 } 1994 } 1995 } 1996 1997 /** 1998 * Deletes a specific version of a page or an attachment. 1999 * 2000 * @param page The page object. 2001 * @throws ProviderException If something goes wrong. 2002 */ 2003 public void deleteVersion( final WikiPage page ) throws ProviderException { 2004 if( page instanceof Attachment ) { 2005 m_attachmentManager.deleteVersion( (Attachment) page ); 2006 } else { 2007 m_pageManager.deleteVersion( page ); 2008 } 2009 } 2010 2011 /** 2012 * Returns the URL of the global RSS file. May be null, if the 2013 * RSS file generation is not operational. 2014 * @since 1.7.10 2015 * @return The global RSS url 2016 */ 2017 public String getGlobalRSSURL() 2018 { 2019 if( m_rssGenerator != null && m_rssGenerator.isEnabled() ) 2020 { 2021 return getBaseURL()+ "/" + m_rssFile; 2022 } 2023 2024 return null; 2025 } 2026 2027 /** 2028 * Returns the root path. The root path is where the WikiEngine is 2029 * located in the file system. 2030 * 2031 * @since 2.2 2032 * @return A path to where the Wiki is installed in the local filesystem. 2033 */ 2034 public String getRootPath() 2035 { 2036 return m_rootPath; 2037 } 2038 2039 /** 2040 * @since 2.2.6 2041 * @return the URL constructor 2042 */ 2043 public URLConstructor getURLConstructor() 2044 { 2045 return m_urlConstructor; 2046 } 2047 2048 /** 2049 * Returns the RSSGenerator. If the property <code>jspwiki.rss.generate</code> 2050 * has not been set to <code>true</code>, this method will return <code>null</code>, 2051 * <em>and callers should check for this value.</em> 2052 * @since 2.1.165 2053 * @return the RSS generator 2054 */ 2055 public RSSGenerator getRSSGenerator() 2056 { 2057 return m_rssGenerator; 2058 } 2059 2060 /** 2061 * Renames, or moves, a wiki page. Can also alter referring wiki 2062 * links to point to the renamed page. 2063 * 2064 * @param context The context during which this rename takes 2065 * place. 2066 * @param renameFrom Name of the source page. 2067 * @param renameTo Name of the destination page. 2068 * @param changeReferrers If true, then changes any referring links 2069 * to point to the renamed page. 2070 * 2071 * @return The name of the page that the source was renamed to. 2072 * 2073 * @throws WikiException In the case of an error, such as the destination 2074 * page already existing. 2075 */ 2076 public String renamePage( WikiContext context, 2077 String renameFrom, 2078 String renameTo, 2079 boolean changeReferrers) 2080 throws WikiException 2081 { 2082 String newPageName = m_pageRenamer.renamePage(context, renameFrom, renameTo, changeReferrers); 2083 firePageRenameEvent(renameFrom, newPageName); 2084 return newPageName; 2085 } 2086 2087 /** 2088 * Returns the PageRenamer employed by this WikiEngine. 2089 * @since 2.5.141 2090 * @return The current PageRenamer instance. 2091 */ 2092 public PageRenamer getPageRenamer() 2093 { 2094 return m_pageRenamer; 2095 } 2096 2097 /** 2098 * Returns the UserManager employed by this WikiEngine. 2099 * @since 2.3 2100 * @return The current UserManager instance. 2101 */ 2102 public UserManager getUserManager() 2103 { 2104 return m_userManager; 2105 } 2106 2107 /** 2108 * Returns the TasksManager employed by this WikiEngine. 2109 * @return The current TasksManager instance. 2110 */ 2111 public TasksManager getTasksManager() 2112 { 2113 return m_tasksManager; 2114 } 2115 2116 /** 2117 * Returns the GroupManager employed by this WikiEngine. 2118 * @since 2.3 2119 * @return The current GroupManager instance 2120 */ 2121 public GroupManager getGroupManager() 2122 { 2123 return m_groupManager; 2124 } 2125 2126 /** 2127 * Returns the current {@link AdminBeanManager}. 2128 * 2129 * @return The current {@link AdminBeanManager}. 2130 * @since 2.6 2131 */ 2132 public AdminBeanManager getAdminBeanManager() { 2133 return m_adminBeanManager; 2134 } 2135 2136 /** 2137 * Returns the AclManager employed by this WikiEngine. 2138 * The AclManager is lazily initialized. 2139 * <p> 2140 * The AclManager implementing class may be set by the 2141 * System property {@link #PROP_ACL_MANAGER_IMPL}. 2142 * </p> 2143 * 2144 * @since 2.3 2145 * @return The current AclManager. 2146 */ 2147 public AclManager getAclManager() 2148 { 2149 if( m_aclManager == null ) 2150 { 2151 try 2152 { 2153 String s = m_properties.getProperty( PROP_ACL_MANAGER_IMPL, ClassUtil.getMappedClass( AclManager.class.getName() ).getName() ); 2154 m_aclManager = ClassUtil.getMappedObject(s); // TODO: I am not sure whether this is the right call 2155 m_aclManager.initialize( this, m_properties ); 2156 } 2157 catch ( ReflectiveOperationException | IllegalArgumentException e ) 2158 { 2159 log.fatal( "unable to instantiate class for AclManager: " + e.getMessage() ); 2160 throw new InternalWikiException( "Cannot instantiate AclManager, please check logs.", e ); 2161 } 2162 } 2163 return m_aclManager; 2164 } 2165 2166 /** 2167 * Returns the DifferenceManager so that texts can be compared. 2168 * @return the difference manager 2169 */ 2170 public DifferenceManager getDifferenceManager() 2171 { 2172 return m_differenceManager; 2173 } 2174 2175 /** 2176 * Returns the current EditorManager instance. 2177 * 2178 * @return The current EditorManager. 2179 */ 2180 public EditorManager getEditorManager() 2181 { 2182 return m_editorManager; 2183 } 2184 2185 /** 2186 * Returns the current i18n manager. 2187 * 2188 * @return The current Intertan... Interante... Internatatializ... Whatever. 2189 */ 2190 public InternationalizationManager getInternationalizationManager() 2191 { 2192 return m_internationalizationManager; 2193 } 2194 2195 /** 2196 * Registers a WikiEventListener with this instance. 2197 * @param listener the event listener 2198 */ 2199 public final synchronized void addWikiEventListener( WikiEventListener listener ) 2200 { 2201 WikiEventManager.addWikiEventListener( this, listener ); 2202 } 2203 2204 /** 2205 * Un-registers a WikiEventListener with this instance. 2206 * @param listener the event listener 2207 */ 2208 public final synchronized void removeWikiEventListener( WikiEventListener listener ) 2209 { 2210 WikiEventManager.removeWikiEventListener( this, listener ); 2211 } 2212 2213 /** 2214 * Fires a WikiEngineEvent to all registered listeners. 2215 * @param type the event type 2216 */ 2217 protected final void fireEvent( int type ) 2218 { 2219 if ( WikiEventManager.isListening(this) ) 2220 { 2221 WikiEventManager.fireEvent(this,new WikiEngineEvent(this,type)); 2222 } 2223 } 2224 2225 /** 2226 * Fires a WikiPageEvent to all registered listeners. 2227 * @param type the event type 2228 */ 2229 protected final void firePageEvent( int type, String pageName ) 2230 { 2231 if ( WikiEventManager.isListening(this) ) 2232 { 2233 WikiEventManager.fireEvent(this,new WikiPageEvent(this,type,pageName)); 2234 } 2235 } 2236 2237 /** 2238 * Fires a WikiPageRenameEvent to all registered listeners. 2239 * @param oldName the former page name 2240 * @param newName the new page name 2241 */ 2242 protected final void firePageRenameEvent(String oldName, String newName ) 2243 { 2244 if ( WikiEventManager.isListening(this) ) 2245 { 2246 WikiEventManager.fireEvent(this,new WikiPageRenameEvent(this,oldName,newName)); 2247 } 2248 } 2249 2250 /** 2251 * Adds an attribute to the engine for the duration of this engine. The 2252 * value is not persisted. 2253 * 2254 * @since 2.4.91 2255 * @param key the attribute name 2256 * @param value the value 2257 */ 2258 public void setAttribute( String key, Object value ) 2259 { 2260 m_attributes.put( key, value ); 2261 } 2262 2263 /** 2264 * Gets an attribute from the engine. 2265 * 2266 * @param key the attribute name 2267 * @return the value 2268 */ 2269 public Object getAttribute( String key ) 2270 { 2271 return m_attributes.get( key ); 2272 } 2273 2274 /** 2275 * Removes an attribute. 2276 * 2277 * @param key The key of the attribute to remove. 2278 * @return The previous attribute, if it existed. 2279 */ 2280 public Object removeAttribute( String key ) 2281 { 2282 return m_attributes.remove( key ); 2283 } 2284 2285 /** 2286 * Returns a WatchDog for current thread. 2287 * 2288 * @return The current thread WatchDog. 2289 * @since 2.4.92 2290 */ 2291 public WatchDog getCurrentWatchDog() 2292 { 2293 return WatchDog.getCurrentWatchDog(this); 2294 } 2295 2296}