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