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