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