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