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}