001/*
002    Licensed to the Apache Software Foundation (ASF) under one
003    or more contributor license agreements.  See the NOTICE file
004    distributed with this work for additional information
005    regarding copyright ownership.  The ASF licenses this file
006    to you under the Apache License, Version 2.0 (the
007    "License"); you may not use this file except in compliance
008    with the License.  You may obtain a copy of the License at
009
010       http://www.apache.org/licenses/LICENSE-2.0
011
012    Unless required by applicable law or agreed to in writing,
013    software distributed under the License is distributed on an
014    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015    KIND, either express or implied.  See the License for the
016    specific language governing permissions and limitations
017    under the License.
018 */
019package org.apache.wiki;
020
021import org.apache.log4j.Logger;
022import org.apache.wiki.auth.NoSuchPrincipalException;
023import org.apache.wiki.auth.UserManager;
024import org.apache.wiki.auth.WikiPrincipal;
025import org.apache.wiki.auth.permissions.AllPermission;
026import org.apache.wiki.auth.user.UserDatabase;
027import org.apache.wiki.tags.WikiTagBase;
028import org.apache.wiki.ui.Command;
029import org.apache.wiki.ui.CommandResolver;
030import org.apache.wiki.ui.GroupCommand;
031import org.apache.wiki.ui.Installer;
032import org.apache.wiki.ui.PageCommand;
033import org.apache.wiki.ui.WikiCommand;
034import org.apache.wiki.util.TextUtil;
035
036import javax.servlet.http.HttpServletRequest;
037import javax.servlet.http.HttpSession;
038import javax.servlet.jsp.PageContext;
039import java.security.Permission;
040import java.security.Principal;
041import java.util.HashMap;
042import java.util.PropertyPermission;
043
044/**
045 *  <p>Provides state information throughout the processing of a page.  A
046 *  WikiContext is born when the JSP pages that are the main entry
047 *  points, are invoked.  The JSPWiki engine creates the new
048 *  WikiContext, which basically holds information about the page, the
049 *  handling engine, and in which context (view, edit, etc) the
050 *  call was done.</p>
051 *  <p>A WikiContext also provides request-specific variables, which can
052 *  be used to communicate between plugins on the same page, or
053 *  between different instances of the same plugin.  A WikiContext
054 *  variable is valid until the processing of the page has ended.  For
055 *  an example, please see the Counter plugin.</p>
056 *  <p>When a WikiContext is created, it automatically associates a
057 *  {@link WikiSession} object with the user's HttpSession. The
058 *  WikiSession contains information about the user's authentication
059 *  status, and is consulted by {@link #getCurrentUser()}.
060 *  object</p>
061 *  <p>Do not cache the page object that you get from the WikiContext; always
062 *  use getPage()!</p>
063 *
064 *  @see org.apache.wiki.plugin.Counter
065 *
066 */
067public class WikiContext implements Cloneable, Command {
068
069    private    Command m_command = null;
070
071    private    WikiPage   m_page;
072    private    WikiPage   m_realPage;
073    private    WikiEngine m_engine;
074    private    String     m_template = "default";
075
076    private    HashMap<String,Object> m_variableMap = new HashMap<>();
077
078    /** Stores the HttpServletRequest.  May be null, if the request did not come from a servlet. */
079    protected  HttpServletRequest m_request = null;
080
081    private    WikiSession m_session = null;
082
083    /** User is administering JSPWiki (Install, SecurityConfig). */
084    public static final String INSTALL = WikiCommand.INSTALL.getRequestContext();
085
086    /** The VIEW context - the user just wants to view the page contents. */
087    public static final String VIEW = PageCommand.VIEW.getRequestContext();
088
089    /** User wants to view or administer workflows. */
090    public static final String WORKFLOW = WikiCommand.WORKFLOW.getRequestContext();
091
092    /** The EDIT context - the user is editing the page. */
093    public static final String EDIT = PageCommand.EDIT.getRequestContext();
094
095    /** User is preparing for a login/authentication. */
096    public static final String LOGIN = WikiCommand.LOGIN.getRequestContext();
097
098    /** User is preparing to log out. */
099    public static final String LOGOUT = WikiCommand.LOGOUT.getRequestContext();
100
101    /** JSPWiki wants to display a message. */
102    public static final String MESSAGE = WikiCommand.MESSAGE.getRequestContext();
103
104    /** User is viewing a DIFF between the two versions of the page. */
105    public static final String DIFF = PageCommand.DIFF.getRequestContext();
106
107    /** User is viewing page history. */
108    public static final String INFO = PageCommand.INFO.getRequestContext();
109
110    /** User is previewing the changes he just made. */
111    public static final String PREVIEW = PageCommand.PREVIEW.getRequestContext();
112
113    /** User has an internal conflict, and does quite not know what to
114        do. Please provide some counseling. */
115    public static final String CONFLICT = PageCommand.CONFLICT.getRequestContext();
116
117    /** An error has been encountered and the user needs to be informed. */
118    public static final String ERROR = WikiCommand.ERROR.getRequestContext();
119
120    /** User is uploading something. */
121    public static final String UPLOAD = PageCommand.UPLOAD.getRequestContext();
122
123    /** User is commenting something. */
124    public static final String COMMENT = PageCommand.COMMENT.getRequestContext();
125
126    /** User is searching for content. */
127    public static final String FIND = WikiCommand.FIND.getRequestContext();
128
129    /** User wishes to create a new group */
130    public static final String CREATE_GROUP = WikiCommand.CREATE_GROUP.getRequestContext();
131
132    /** User is deleting an existing group. */
133    public static final String DELETE_GROUP = GroupCommand.DELETE_GROUP.getRequestContext();
134
135    /** User is editing an existing group. */
136    public static final String EDIT_GROUP = GroupCommand.EDIT_GROUP.getRequestContext();
137
138    /** User is viewing an existing group */
139    public static final String VIEW_GROUP = GroupCommand.VIEW_GROUP.getRequestContext();
140
141    /** User is editing preferences */
142    public static final String PREFS = WikiCommand.PREFS.getRequestContext();
143
144    /** User is renaming a page. */
145    public static final String RENAME = PageCommand.RENAME.getRequestContext();
146
147    /** User is deleting a page or an attachment. */
148    public static final String DELETE = PageCommand.DELETE.getRequestContext();
149
150    /** User is downloading an attachment. */
151    public static final String ATTACH = PageCommand.ATTACH.getRequestContext();
152
153    /** RSS feed is being generated. */
154    public static final String RSS = PageCommand.RSS.getRequestContext();
155
156    /** This is not a JSPWiki context, use it to access static files. */
157    public static final String NONE = PageCommand.NONE.getRequestContext();
158
159    /** Same as NONE; this is just a clarification. */
160    public static final String OTHER = PageCommand.OTHER.getRequestContext();
161
162    /** User is doing administrative things. */
163    public static final String ADMIN = WikiCommand.ADMIN.getRequestContext();
164
165    private static final Logger log = Logger.getLogger( WikiContext.class );
166
167    private static final Permission DUMMY_PERMISSION = new PropertyPermission( "os.name", "read" );
168
169    /**
170     *  Create a new WikiContext for the given WikiPage. Delegates to
171     * {@link #WikiContext(WikiEngine, HttpServletRequest, WikiPage)}.
172     *  @param engine The WikiEngine that is handling the request.
173     *  @param page   The WikiPage.  If you want to create a
174     *  WikiContext for an older version of a page, you must use this
175     *  constructor.
176     */
177    public WikiContext( WikiEngine engine, WikiPage page )
178    {
179        this( engine, null, findCommand( engine, null, page ) );
180    }
181
182    /**
183     * <p>
184     * Creates a new WikiContext for the given WikiEngine, Command and
185     * HttpServletRequest.
186     * </p>
187     * <p>
188     * This constructor will also look up the HttpSession associated with the
189     * request, and determine if a WikiSession object is present. If not, a new
190     * one is created.
191     * </p>
192     * @param engine The WikiEngine that is handling the request
193     * @param request The HttpServletRequest that should be associated with this
194     *            context. This parameter may be <code>null</code>.
195     * @param command the command
196     * @throws IllegalArgumentException if <code>engine</code> or
197     *             <code>command</code> are <code>null</code>
198     */
199    public WikiContext( WikiEngine engine, HttpServletRequest request, Command command )
200        throws IllegalArgumentException
201    {
202        super();
203        if ( engine == null || command == null )
204        {
205            throw new IllegalArgumentException( "Parameter engine and command must not be null." );
206        }
207
208        m_engine = engine;
209        m_request = request;
210        m_session = WikiSession.getWikiSession( engine, request );
211        m_command = command;
212
213        // If PageCommand, get the WikiPage
214        if( command instanceof PageCommand )
215        {
216            m_page = (WikiPage)((PageCommand)command).getTarget();
217        }
218
219        // If page not supplied, default to front page to avoid NPEs
220        if( m_page == null )
221        {
222            m_page = m_engine.getPage( m_engine.getFrontPage() );
223
224            // Front page does not exist?
225            if( m_page == null )
226            {
227                m_page = new WikiPage( m_engine, m_engine.getFrontPage() );
228            }
229        }
230
231        m_realPage = m_page;
232
233        // Special case: retarget any empty 'view' PageCommands to the front page
234        if ( PageCommand.VIEW.equals( command ) && command.getTarget() == null )
235        {
236            m_command = command.targetedCommand( m_page );
237        }
238
239        // Debugging...
240        if( log.isDebugEnabled() )
241        {
242            HttpSession session = ( request == null ) ? null : request.getSession( false );
243            String sid = ( session == null ) ? "(null)" : session.getId();
244            log.debug( "Creating WikiContext for session ID=" + sid + "; target=" + getName() );
245        }
246
247        // Figure out what template to use
248        setDefaultTemplate( request );
249    }
250
251    /**
252     * Creates a new WikiContext for the given WikiEngine, WikiPage and
253     * HttpServletRequest. This method simply looks up the appropriate Command
254     * using {@link #findCommand(WikiEngine, HttpServletRequest, WikiPage)} and
255     * delegates to
256     * {@link #WikiContext(WikiEngine, HttpServletRequest, Command)}.
257     * @param engine The WikiEngine that is handling the request
258     * @param request The HttpServletRequest that should be associated with this
259     *            context. This parameter may be <code>null</code>.
260     * @param page The WikiPage. If you want to create a WikiContext for an
261     *            older version of a page, you must supply this parameter
262     */
263    public WikiContext(WikiEngine engine, HttpServletRequest request, WikiPage page)
264    {
265        this( engine, request, findCommand( engine, request, page ) );
266    }
267
268    /**
269     * {@inheritDoc}
270     * @see org.apache.wiki.ui.Command#getContentTemplate()
271     */
272    @Override
273    public String getContentTemplate()
274    {
275        return m_command.getContentTemplate();
276    }
277
278    /**
279     * {@inheritDoc}
280     * @see org.apache.wiki.ui.Command#getJSP()
281     */
282    @Override
283    public String getJSP()
284    {
285        return m_command.getContentTemplate();
286    }
287
288    /**
289     *  Sets a reference to the real page whose content is currently being
290     *  rendered.
291     *  <p>
292     *  Sometimes you may want to render the page using some other page's context.
293     *  In those cases, it is highly recommended that you set the setRealPage()
294     *  to point at the real page you are rendering.  Please see InsertPageTag
295     *  for an example.
296     *  <p>
297     *  Also, if your plugin e.g. does some variable setting, be aware that if it
298     *  is embedded in the LeftMenu or some other page added with InsertPageTag,
299     *  you should consider what you want to do - do you wish to really reference
300     *  the "master" page or the included page.
301     *
302     *  @param page  The real page which is being rendered.
303     *  @return The previous real page
304     *  @since 2.3.14
305     *  @see org.apache.wiki.tags.InsertPageTag
306     */
307    public WikiPage setRealPage( WikiPage page )
308    {
309        WikiPage old = m_realPage;
310        m_realPage = page;
311        updateCommand( m_command.getRequestContext() );
312        return old;
313    }
314
315    /**
316     *  Gets a reference to the real page whose content is currently being rendered.
317     *  If your plugin e.g. does some variable setting, be aware that if it
318     *  is embedded in the LeftMenu or some other page added with InsertPageTag,
319     *  you should consider what you want to do - do you wish to really reference
320     *  the "master" page or the included page.
321     *  <p>
322     *  For example, in the default template, there is a page called "LeftMenu".
323     *  Whenever you access a page, e.g. "Main", the master page will be Main, and
324     *  that's what the getPage() will return - regardless of whether your plugin
325     *  resides on the LeftMenu or on the Main page.  However, getRealPage()
326     *  will return "LeftMenu".
327     *
328     *  @return A reference to the real page.
329     *  @see org.apache.wiki.tags.InsertPageTag
330     *  @see org.apache.wiki.parser.JSPWikiMarkupParser
331     */
332    public WikiPage getRealPage()
333    {
334        return m_realPage;
335    }
336
337    /**
338     *  Figure out to which page we are really going to.  Considers
339     *  special page names from the jspwiki.properties, and possible aliases.
340     *  This method forwards requests to
341     *  {@link org.apache.wiki.ui.CommandResolver#getSpecialPageReference(String)}.
342     *  @return A complete URL to the new page to redirect to
343     *  @since 2.2
344     */
345
346    public String getRedirectURL()
347    {
348        String pagename = m_page.getName();
349        String redirURL = m_engine.getCommandResolver().getSpecialPageReference( pagename );
350
351        if( redirURL == null )
352        {
353            String alias = (String)m_page.getAttribute( WikiPage.ALIAS );
354
355            if( alias != null )
356            {
357                redirURL = getViewURL( alias );
358            }
359            else
360            {
361                redirURL = (String)m_page.getAttribute( WikiPage.REDIRECT );
362            }
363        }
364
365        return redirURL;
366    }
367
368    /**
369     *  Returns the handling engine.
370     *
371     *  @return The wikiengine owning this context.
372     */
373    public WikiEngine getEngine()
374    {
375        return m_engine;
376    }
377
378    /**
379     *  Returns the page that is being handled.
380     *
381     *  @return the page which was fetched.
382     */
383    public WikiPage getPage()
384    {
385        return m_page;
386    }
387
388    /**
389     *  Sets the page that is being handled.
390     *
391     *  @param page The wikipage
392     *  @since 2.1.37.
393     */
394    public void setPage( WikiPage page )
395    {
396        m_page = page;
397        updateCommand( m_command.getRequestContext() );
398    }
399
400    /**
401     *  Returns the request context.
402     *  @return The name of the request context (e.g. VIEW).
403     */
404    @Override
405    public String getRequestContext()
406    {
407        return m_command.getRequestContext();
408    }
409
410    /**
411     *  Sets the request context.  See above for the different
412     *  request contexts (VIEW, EDIT, etc.)
413     *
414     *  @param arg The request context (one of the predefined contexts.)
415     */
416    public void setRequestContext( String arg )
417    {
418        updateCommand( arg );
419    }
420
421    /**
422     * {@inheritDoc}
423     * @see org.apache.wiki.ui.Command#getTarget()
424     */
425    @Override
426    public Object getTarget()
427    {
428        return m_command.getTarget();
429    }
430
431    /**
432     * {@inheritDoc}
433     * @see org.apache.wiki.ui.Command#getURLPattern()
434     */
435    @Override
436    public String getURLPattern()
437    {
438        return m_command.getURLPattern();
439    }
440
441    /**
442     *  Gets a previously set variable.
443     *
444     *  @param key The variable name.
445     *  @return The variable contents.
446     */
447    public Object getVariable( String key )
448    {
449        return m_variableMap.get( key );
450    }
451
452    /**
453     *  Sets a variable.  The variable is valid while the WikiContext is valid,
454     *  i.e. while page processing continues.  The variable data is discarded
455     *  once the page processing is finished.
456     *
457     *  @param key The variable name.
458     *  @param data The variable value.
459     */
460    public void setVariable( String key, Object data )
461    {
462        m_variableMap.put( key, data );
463        updateCommand( m_command.getRequestContext() );
464    }
465
466    /**
467     * This is just a simple helper method which will first check the context
468     * if there is already an override in place, and if there is not,
469     * it will then check the given properties.
470     *
471     * @param key      What key are we searching for?
472     * @param defValue Default value for the boolean
473     * @return  {@code true} or {@code false}.
474     */
475    public boolean getBooleanWikiProperty( final String key, final boolean defValue ) {
476        Object bool = getVariable( key );
477        if( bool != null ) {
478            return TextUtil.isPositive( (String) bool );
479        }
480
481        return TextUtil.getBooleanProperty( getEngine().getWikiProperties(), key, defValue );
482    }
483
484    /**
485     *  This method will safely return any HTTP parameters that
486     *  might have been defined.  You should use this method instead
487     *  of peeking directly into the result of getHttpRequest(), since
488     *  this method is smart enough to do all of the right things,
489     *  figure out UTF-8 encoded parameters, etc.
490     *
491     *  @since 2.0.13.
492     *  @param paramName Parameter name to look for.
493     *  @return HTTP parameter, or null, if no such parameter existed.
494     */
495    public String getHttpParameter( String paramName )
496    {
497        String result = null;
498
499        if( m_request != null )
500        {
501            result = m_request.getParameter( paramName );
502        }
503
504        return result;
505    }
506
507    /**
508     *  If the request did originate from a HTTP request,
509     *  then the HTTP request can be fetched here.  However, it the request
510     *  did NOT originate from a HTTP request, then this method will
511     *  return null, and YOU SHOULD CHECK FOR IT!
512     *
513     *  @return Null, if no HTTP request was done.
514     *  @since 2.0.13.
515     */
516    public HttpServletRequest getHttpRequest()
517    {
518        return m_request;
519    }
520
521    /**
522     *  Sets the template to be used for this request.
523     *
524     *  @param dir The template name
525     *  @since 2.1.15.
526     */
527    public void setTemplate( String dir )
528    {
529        m_template = dir;
530    }
531
532    /**
533     * Returns the target of this wiki context: a page, group name or JSP. If
534     * the associated Command is a PageCommand, this method returns the page's
535     * name. Otherwise, this method delegates to the associated Command's
536     * {@link org.apache.wiki.ui.Command#getName()} method. Calling classes
537     * can rely on the results of this method for looking up canonically-correct
538     * page or group names. Because it does not automatically assume that the
539     * wiki context is a PageCommand, calling this method is inherently safer
540     * than calling <code>getPage().getName()</code>.
541     * @return the name of the target of this wiki context
542     * @see org.apache.wiki.ui.PageCommand#getName()
543     * @see org.apache.wiki.ui.GroupCommand#getName()
544     */
545    @Override
546    public String getName()
547    {
548        if ( m_command instanceof PageCommand )
549        {
550            return m_page != null ? m_page.getName() : "<no page>";
551        }
552        return m_command.getName();
553    }
554
555    /**
556     *  Gets the template that is to be used throughout this request.
557     *  @since 2.1.15.
558     *  @return template name
559     */
560    public String getTemplate()
561    {
562        return m_template;
563    }
564
565    /**
566     *  Convenience method that gets the current user. Delegates the
567     *  lookup to the WikiSession associated with this WikiContect.
568     *  May return null, in case the current
569     *  user has not yet been determined; or this is an internal system.
570     *  If the WikiSession has not been set, <em>always</em> returns null.
571     *
572     *  @return The current user; or maybe null in case of internal calls.
573     */
574    public Principal getCurrentUser()
575    {
576        if (m_session == null)
577        {
578            // This shouldn't happen, really...
579            return WikiPrincipal.GUEST;
580        }
581        return m_session.getUserPrincipal();
582    }
583
584    /**
585     *  A shortcut to generate a VIEW url.
586     *
587     *  @param page The page to which to link.
588     *  @return An URL to the page.  This honours the current absolute/relative setting.
589     */
590    public String getViewURL( String page )
591    {
592        return getURL( VIEW, page, null );
593    }
594
595    /**
596     *  Creates an URL for the given request context.
597     *
598     *  @param context e.g. WikiContext.EDIT
599     *  @param page The page to which to link
600     *  @return An URL to the page, honours the absolute/relative setting in jspwiki.properties
601     */
602    public String getURL( final String context, final String page ) {
603        return getURL( context, page, null );
604    }
605
606    /**
607     *  Returns an URL from a page. It this WikiContext instance was constructed
608     *  with an actual HttpServletRequest, we will attempt to construct the
609     *  URL using HttpUtil, which preserves the HTTPS portion if it was used.
610     *
611     *  @param context The request context (e.g. WikiContext.UPLOAD)
612     *  @param page    The page to which to link
613     *  @param params  A list of parameters, separated with "&amp;"
614     *
615     *  @return An URL to the given context and page.
616     */
617    public String getURL( final String context, final String page, final String params ) {
618        final boolean absolute = "absolute".equals(m_engine.getVariable( this, WikiEngine.PROP_REFSTYLE ));
619
620        // FIXME: is rather slow
621        return m_engine.getURL( context, page, params, absolute );
622    }
623
624    /**
625     * Returns the Command associated with this WikiContext.
626     * @return the command
627     */
628    public Command getCommand()
629    {
630        return m_command;
631    }
632
633    /**
634     *  Returns a shallow clone of the WikiContext.
635     *
636     *  @since 2.1.37.
637     *  @return A shallow clone of the WikiContext
638     */
639    @Override
640    public Object clone() {
641        try {
642            // super.clone() must always be called to make sure that inherited objects
643            // get the right type
644            WikiContext copy = (WikiContext)super.clone();
645
646            copy.m_engine = m_engine;
647            copy.m_command = m_command;
648
649            copy.m_template    = m_template;
650            copy.m_variableMap = m_variableMap;
651            copy.m_request     = m_request;
652            copy.m_session     = m_session;
653            copy.m_page        = m_page;
654            copy.m_realPage    = m_realPage;
655            return copy;
656        } catch( CloneNotSupportedException e ){} // Never happens
657
658        return null;
659    }
660
661    /**
662     *  Creates a deep clone of the WikiContext.  This is useful when you want
663     *  to be sure that you don't accidentally mess with page attributes, etc.
664     *
665     *  @since  2.8.0
666     *  @return A deep clone of the WikiContext.
667     */
668    @SuppressWarnings("unchecked")
669    public WikiContext deepClone()
670    {
671        try
672        {
673            // super.clone() must always be called to make sure that inherited objects
674            // get the right type
675            WikiContext copy = (WikiContext)super.clone();
676
677            //  No need to deep clone these
678            copy.m_engine  = m_engine;
679            copy.m_command = m_command; // Static structure
680
681            copy.m_template    = m_template;
682            copy.m_variableMap = (HashMap<String,Object>)m_variableMap.clone();
683            copy.m_request     = m_request;
684            copy.m_session     = m_session;
685            copy.m_page        = (WikiPage)m_page.clone();
686            copy.m_realPage    = (WikiPage)m_realPage.clone();
687            return copy;
688        }
689        catch( CloneNotSupportedException e ){} // Never happens
690
691        return null;
692    }
693
694    /**
695     *  Returns the WikiSession associated with the context.
696     *  This method is guaranteed to always return a valid WikiSession.
697     *  If this context was constructed without an associated
698     *  HttpServletRequest, it will return {@link WikiSession#guestSession(WikiEngine)}.
699     *
700     *  @return The WikiSession associate with this context.
701     */
702    public WikiSession getWikiSession()
703    {
704        return m_session;
705    }
706
707    /**
708     *  This method can be used to find the WikiContext programmatically
709     *  from a JSP PageContext. We check the request context.
710     *  The wiki context, if it exists,
711     *  is looked up using the key
712     *  {@link org.apache.wiki.tags.WikiTagBase#ATTR_CONTEXT}.
713     *
714     *  @since 2.4
715     *  @param pageContext the JSP page context
716     *  @return Current WikiContext, or null, of no context exists.
717     */
718    public static WikiContext findContext( PageContext pageContext )
719    {
720        HttpServletRequest request = (HttpServletRequest) pageContext.getRequest();
721        WikiContext context = (WikiContext)request.getAttribute( WikiTagBase.ATTR_CONTEXT );
722        return context;
723    }
724
725    /**
726     * Returns the permission required to successfully execute this context.
727     * For example, the a wiki context of VIEW for a certain page means that
728     * the PagePermission "view" is required for the page. In some cases, no
729     * particular permission is required, in which case a dummy permission will
730     * be returned ({@link java.util.PropertyPermission}<code> "os.name",
731     * "read"</code>). This method is guaranteed to always return a valid,
732     * non-null permission.
733     * @return the permission
734     * @since 2.4
735     */
736    @Override
737    public Permission requiredPermission()
738    {
739        // This is a filthy rotten hack -- absolutely putrid
740        if ( WikiCommand.INSTALL.equals( m_command ) )
741        {
742            // See if admin users exists
743            try
744            {
745                UserManager userMgr = m_engine.getUserManager();
746                UserDatabase userDb = userMgr.getUserDatabase();
747                userDb.findByLoginName( Installer.ADMIN_ID );
748            }
749            catch ( NoSuchPrincipalException e )
750            {
751                return DUMMY_PERMISSION;
752            }
753            return new AllPermission( m_engine.getApplicationName() );
754        }
755
756        // TODO: we should really break the contract so that this
757        // method returns null, but until then we will use this hack
758        if ( m_command.requiredPermission() == null )
759        {
760            return DUMMY_PERMISSION;
761        }
762
763        return m_command.requiredPermission();
764    }
765
766    /**
767     * Associates a target with the current Command and returns
768     * the new targeted Command. If the Command associated with this
769     * WikiContext is already "targeted", it is returned instead.
770     * @see org.apache.wiki.ui.Command#targetedCommand(java.lang.Object)
771     *
772     * {@inheritDoc}
773     */
774    @Override
775    public Command targetedCommand( Object target )
776    {
777        if ( m_command.getTarget() == null )
778        {
779            return m_command.targetedCommand( target );
780        }
781        return m_command;
782    }
783
784    /**
785     *  Returns true, if the current user has administrative permissions (i.e. the omnipotent
786     *  AllPermission).
787     *
788     *  @since 2.4.46
789     *  @return true, if the user has all permissions.
790     */
791    public boolean hasAdminPermissions() {
792        return m_engine.getAuthorizationManager().checkPermission( getWikiSession(), new AllPermission(m_engine.getApplicationName()) );
793    }
794
795    /**
796     * Figures out which template a new WikiContext should be using.
797     * @param request the HTTP request
798     */
799    protected void setDefaultTemplate( HttpServletRequest request )
800    {
801        String defaultTemplate = m_engine.getTemplateDir();
802
803        //  Figure out which template we should be using for this page.
804        String template = null;
805        if ( request != null )
806        {
807            template = request.getParameter( "skin" );
808
809            if( template != null ) template = template.replaceAll("\\p{Punct}", "");
810        }
811
812        // If request doesn't supply the value, extract from wiki page
813        if( template == null )
814        {
815            WikiPage page = getPage();
816            if ( page != null )
817            {
818                template = (String)page.getAttribute( WikiEngine.PROP_TEMPLATEDIR );
819            }
820
821        }
822
823        // If something over-wrote the default, set the new value.
824        if ( template != null )
825        {
826            setTemplate( template );
827        }
828        else
829        {
830            setTemplate( defaultTemplate );
831        }
832    }
833
834    /**
835     * Looks up and returns a PageCommand based on a supplied WikiPage and HTTP
836     * request. First, the appropriate Command is obtained by examining the HTTP
837     * request; the default is {@link PageCommand#VIEW}. If the Command is a
838     * PageCommand (and it should be, in most cases), a targeted Command is
839     * created using the (non-<code>null</code>) WikiPage as target.
840     * @param engine the wiki engine
841     * @param request the HTTP request
842     * @param page the wiki page
843     * @return the correct command
844     */
845    protected static Command findCommand( WikiEngine engine, HttpServletRequest request, WikiPage page )
846    {
847        String defaultContext = PageCommand.VIEW.getRequestContext();
848        Command command = engine.getCommandResolver().findCommand( request, defaultContext );
849        if ( command instanceof PageCommand && page != null )
850        {
851            command = command.targetedCommand( page );
852        }
853        return command;
854    }
855
856    /**
857     * Protected method that updates the internally cached Command.
858     * Will always be called when the page name, request context, or variable
859     * changes.
860     * @param requestContext the desired request context
861     * @since 2.4
862     */
863    protected void updateCommand( String requestContext )
864    {
865        if ( requestContext == null )
866        {
867            m_command = PageCommand.NONE;
868        }
869        else
870        {
871            CommandResolver resolver = m_engine.getCommandResolver();
872            m_command = resolver.findCommand( m_request, requestContext );
873        }
874
875        if ( m_command instanceof PageCommand && m_page != null )
876        {
877            m_command = m_command.targetedCommand( m_page );
878        }
879    }
880
881}