    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.plugin;
    020
    021import org.apache.wiki.api.core.Context;
    022import org.apache.wiki.api.core.ContextEnum;
    023import org.apache.wiki.api.core.Engine;
    024import org.apache.wiki.api.core.Page;
    025import org.apache.wiki.api.exceptions.PluginException;
    026import org.apache.wiki.api.exceptions.ProviderException;
    027import org.apache.wiki.api.plugin.Plugin;
    028import org.apache.wiki.auth.AuthorizationManager;
    029import org.apache.wiki.auth.permissions.PermissionFactory;
    030import org.apache.wiki.pages.PageManager;
    031import org.apache.wiki.preferences.Preferences;
    032import org.apache.wiki.render.RenderingManager;
    033import org.apache.wiki.util.HttpUtil;
    034import org.apache.wiki.util.TextUtil;
    035
    036import java.util.ArrayList;
    037import java.util.List;
    038import java.util.Map;
    039import java.util.Objects;
    040import java.util.ResourceBundle;
    041
    042
    043/**
    044 *  Inserts page contents.  Muchos thanks to Scott Hurlbert for the initial code.
    045 *
    046 *  <p>Parameters : </p>
    047 *  <ul>
    048 *  <li><b>page</b> - the name of the page to be inserted</li>
    049 *  <li><b>style</b> - the style to use</li>
    050 *  <li><b>maxlength</b> - the maximum length of the page to be inserted (page contents)</li>
    051 *  <li><b>class</b> - the class to use</li>
    052 *  <li><b>section</b> - the section of the page that has to be inserted (separated by "----"</li>
    053 *  <li><b>default</b> - the text to insert if the requested page does not exist</li>
    054 *  </ul>
    055 *
    056 *  @since 2.1.37
    057 */
    058public class InsertPage implements Plugin {
    059
    060    /** Parameter name for setting the page.  Value is <tt>{@value}</tt>. */
    061    public static final String PARAM_PAGENAME  = "page";
    062    /** Parameter name for setting the style.  Value is <tt>{@value}</tt>. */
    063    public static final String PARAM_STYLE     = "style";
    064    /** Parameter name for setting the maxlength.  Value is <tt>{@value}</tt>. */
    065    public static final String PARAM_MAXLENGTH = "maxlength";
    066    /** Parameter name for setting the class.  Value is <tt>{@value}</tt>. */
    067    public static final String PARAM_CLASS     = "class";
    068    /** Parameter name for setting the show option.  Value is <tt>{@value}</tt>. */
    069    public static final String PARAM_SHOW   = "show";
    070    /** Parameter name for setting the section.  Value is <tt>{@value}</tt>. */
    071    public static final String PARAM_SECTION   = "section";
    072    /** Parameter name for setting the default.  Value is <tt>{@value}</tt>. */
    073    public static final String PARAM_DEFAULT   = "default";
    074
    075    private static final String DEFAULT_STYLE = "";
    076
    077    private static final String ONCE_COOKIE = "JSPWiki.Once.";
    078
    079    /** This attribute is stashed in the WikiContext to make sure that we don't have circular references. */
    080    public static final String ATTR_RECURSE    = "org.apache.wiki.plugin.InsertPage.recurseCheck";
    081
    082    /**
    083     *  {@inheritDoc}
    084     */
    085    @Override @SuppressWarnings("unchecked")
    086    public String execute( final Context context, final Map<String, String> params ) throws PluginException {
    087        final Engine engine = context.getEngine();
    088
    089        final StringBuilder res = new StringBuilder();
    090
    091        final String clazz        = TextUtil.replaceEntities(params.get( PARAM_CLASS ));
    092        final String includedPage = TextUtil.replaceEntities(params.get( PARAM_PAGENAME ));
    093        String style              = TextUtil.replaceEntities(params.get( PARAM_STYLE ));
    094        final boolean showOnce    = "once".equals( params.get( PARAM_SHOW ) );
    095        final String defaultstr   = params.get( PARAM_DEFAULT );
    096        final int section         = TextUtil.parseIntParameter(params.get( PARAM_SECTION ), -1 );
    097        int maxlen                = TextUtil.parseIntParameter(params.get( PARAM_MAXLENGTH ), -1 );
    098
    099        final ResourceBundle rb = Preferences.getBundle( context, Plugin.CORE_PLUGINS_RESOURCEBUNDLE );
    100
    101        if( style == null ) {
    102            style = DEFAULT_STYLE;
    103        }
    104
    105        if( maxlen == -1 ) {
    106            maxlen = Integer.MAX_VALUE;
    107        }
    108
    109        if( includedPage != null ) {
    110            final Page page;
    111            try {
    112                final String pageName = engine.getFinalPageName( includedPage );
    113                page = engine.getManager(PageManager.class).getPage(Objects.requireNonNullElse(pageName, includedPage));
    114            } catch( final ProviderException e ) {
    115                res.append( "<span class=\"error\">Page could not be found by the page provider.</span>" );
    116                return res.toString();
    117            }
    118
    119            if( page != null ) {
    120                //  Check for recursivity
    121                List<String> previousIncludes = context.getVariable( ATTR_RECURSE );
    122
    123                if( previousIncludes != null ) {
    124                    if( previousIncludes.contains( page.getName() ) ) {
    125                        return "<span class=\"error\">Error: Circular reference - you can't include a page in itself!</span>";
    126                    }
    127                } else {
    128                    previousIncludes = new ArrayList<>();
    129                }
    130
    131                // Check for permissions
    132                final AuthorizationManager mgr = engine.getManager( AuthorizationManager.class );
    133
    134                if( !mgr.checkPermission( context.getWikiSession(), PermissionFactory.getPagePermission( page, "view") ) ) {
    135                    res.append("<span class=\"error\">You do not have permission to view this included page.</span>");
    136                    return res.toString();
    137                }
    138
    139                // Show Once
    140                // Check for page-cookie, only include page if cookie is not yet set
    141                String cookieName = "";
    142
    143                if( showOnce ) {
    144                    cookieName = ONCE_COOKIE + TextUtil.urlEncodeUTF8( page.getName() ).replaceAll( "\\+", "%20" );
    145
    146                    if( HttpUtil.retrieveCookieValue( context.getHttpRequest(), cookieName ) != null ) {
    147                        return "";  //silent exit
    148                    }
    149
    150                }
    151
    152                // move here, after premature exit points (permissions, page-cookie)
    153                previousIncludes.add( page.getName() );
    154                context.setVariable( ATTR_RECURSE, previousIncludes );
    155
    156                /**
    157                 *  We want inclusion to occur within the context of
    158                 *  its own page, because we need the links to be correct.
    159                 */
    160
    161                final Context includedContext = context.clone();
    162                includedContext.setPage( page );
    163
    164                String pageData = engine.getManager( PageManager.class ).getPureText( page );
    165                String moreLink = "";
    166
    167                if( section != -1 ) {
    168                    try {
    169                        pageData = TextUtil.getSection( pageData, section );
    170                    } catch( final IllegalArgumentException e ) {
    171                        throw new PluginException( e.getMessage() );
    172                    }
    173                }
    174
    175                if( pageData.length() > maxlen ) {
    176                    pageData = pageData.substring( 0, maxlen )+" ...";
    177                    moreLink = "<p><a href=\""+context.getURL( ContextEnum.PAGE_VIEW.getRequestContext(),includedPage)+"\">"+rb.getString("insertpage.more")+"</a></p>";
    178                }
    179
    180                res.append("<div class=\"inserted-page ");
    181                if( clazz != null ) res.append( clazz );
    182                if( !style.equals(DEFAULT_STYLE) ) res.append( "\" style=\"" ).append( style );
    183                if( showOnce ) res.append( "\" data-once=\"" ).append( cookieName );
    184                res.append("\" >");
    185
    186                res.append( engine.getManager( RenderingManager.class ).textToHTML( includedContext, pageData ) );
    187                res.append( moreLink );
    188
    189                res.append("</div>");
    190
    191                //
    192                //  Remove the name from the stack; we're now done with this.
    193                //
    194                previousIncludes.remove( page.getName() );
    195                context.setVariable( ATTR_RECURSE, previousIncludes );
    196            } else {
    197                if( defaultstr != null ) {
    198                    res.append( defaultstr );
    199                } else {
    200                    res.append( "There is no page called '" ).append( includedPage ).append( "'.  Would you like to " );
    201                    res.append( "<a href=\"" ).append( context.getURL( ContextEnum.PAGE_EDIT.getRequestContext(), includedPage ) ).append( "\">create it?</a>" );
    202                }
    203            }
    204        } else {
    205            res.append( "<span class=\"error\">" );
    206            res.append( "You have to define a page!" );
    207            res.append( "</span>" );
    208        }
    209        return res.toString();
    210    }
    211
    212}