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}