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