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