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.tags; 020 021import org.apache.logging.log4j.LogManager; 022import org.apache.logging.log4j.Logger; 023import org.apache.wiki.api.core.Attachment; 024import org.apache.wiki.api.core.ContextEnum; 025import org.apache.wiki.api.core.Engine; 026import org.apache.wiki.api.core.Page; 027import org.apache.wiki.api.exceptions.ProviderException; 028import org.apache.wiki.api.providers.WikiProvider; 029import org.apache.wiki.attachment.AttachmentManager; 030import org.apache.wiki.pages.PageManager; 031import org.apache.wiki.parser.LinkParsingOperations; 032import org.apache.wiki.parser.MarkupParser; 033import org.apache.wiki.util.TextUtil; 034 035import javax.servlet.jsp.JspWriter; 036import javax.servlet.jsp.tagext.BodyContent; 037import javax.servlet.jsp.tagext.BodyTag; 038import java.util.HashMap; 039import java.util.Iterator; 040import java.util.Map; 041 042/** 043 * Provides a generic link tag for all kinds of linking purposes. 044 * <p> 045 * If parameter <i>jsp</i> is defined, constructs a URL pointing to the specified JSP page, under the baseURL known by the Engine. 046 * Any ParamTag name-value pairs contained in the body are added to this URL to provide support for arbitrary JSP calls. 047 * <p> 048 * @since 2.3.50 049 */ 050public class LinkTag extends WikiLinkTag implements ParamHandler, BodyTag { 051 052 static final long serialVersionUID = 0L; 053 private static final Logger log = LogManager.getLogger( LinkTag.class ); 054 055 private String m_version; 056 private String m_cssClass; 057 private String m_style; 058 private String m_title; 059 private String m_target; 060 private String m_compareToVersion; 061 private String m_rel; 062 private String m_jsp; 063 private String m_ref; 064 private String m_context = ContextEnum.PAGE_VIEW.getRequestContext(); 065 private String m_accesskey; 066 private String m_tabindex; 067 private String m_templatefile; 068 069 private Map<String, String> m_containedParams; 070 071 private BodyContent m_bodyContent; 072 073 @Override 074 public void initTag() { 075 super.initTag(); 076 m_version = m_cssClass = m_style = m_title = m_target = m_compareToVersion = m_rel = m_jsp = m_ref = m_accesskey = m_templatefile = null; 077 m_context = ContextEnum.PAGE_VIEW.getRequestContext(); 078 m_containedParams = new HashMap<>(); 079 } 080 081 public void setTemplatefile( final String key ) 082 { 083 m_templatefile = key; 084 } 085 086 public void setAccessKey( final String key ) 087 { 088 m_accesskey = key; 089 } 090 091 public String getVersion() 092 { 093 return m_version; 094 } 095 096 public void setVersion( final String arg ) 097 { 098 m_version = arg; 099 } 100 101 public void setCssClass( final String arg ) 102 { 103 m_cssClass = arg; 104 } 105 106 public void setStyle( final String style ) 107 { 108 m_style = style; 109 } 110 111 public void setTitle( final String title ) 112 { 113 m_title = title; 114 } 115 116 public void setTarget( final String target ) 117 { 118 m_target = target; 119 } 120 121 public void setTabindex( final String tabindex ) 122 { 123 m_tabindex = tabindex; 124 } 125 126 public void setCompareToVersion( final String ver ) 127 { 128 m_compareToVersion = ver; 129 } 130 131 public void setRel( final String rel ) 132 { 133 m_rel = rel; 134 } 135 136 public void setRef( final String ref ) 137 { 138 m_ref = ref; 139 } 140 141 public void setJsp( final String jsp ) 142 { 143 m_jsp = jsp; 144 } 145 146 public void setContext( final String context ) 147 { 148 m_context = context; 149 } 150 151 /** 152 * Support for ParamTag supplied parameters in body. 153 */ 154 @Override 155 public void setContainedParameter( final String name, final String value ) { 156 if( name != null ) { 157 if( m_containedParams == null ) { 158 m_containedParams = new HashMap<>(); 159 } 160 m_containedParams.put( name, value ); 161 } 162 } 163 164 165 /** 166 * This method figures out what kind of an URL should be output. It mirrors heavily on JSPWikiMarkupParser.handleHyperlinks(); 167 * 168 * @return the URL 169 * @throws ProviderException 170 */ 171 private String figureOutURL() throws ProviderException { 172 String url = null; 173 final Engine engine = m_wikiContext.getEngine(); 174 175 if( m_pageName == null ) { 176 final Page page = m_wikiContext.getPage(); 177 if( page != null ) { 178 m_pageName = page.getName(); 179 } 180 } 181 182 if( m_templatefile != null ) { 183 final String params = addParamsForRecipient( null, m_containedParams ); 184 final String template = engine.getTemplateDir(); 185 url = engine.getURL( ContextEnum.PAGE_NONE.getRequestContext(), "templates/"+template+"/"+m_templatefile, params ); 186 } else if( m_jsp != null ) { 187 final String params = addParamsForRecipient( null, m_containedParams ); 188 //url = m_wikiContext.getURL( ContextEnum.PAGE_NONE.getRequestContext(), m_jsp, params ); 189 url = engine.getURL( ContextEnum.PAGE_NONE.getRequestContext(), m_jsp, params ); 190 } else if( m_ref != null ) { 191 final int interwikipoint; 192 if( new LinkParsingOperations( m_wikiContext ).isExternalLink(m_ref) ) { 193 url = m_ref; 194 } else if( ( interwikipoint = m_ref.indexOf( ":" ) ) != -1 ) { 195 final String extWiki = m_ref.substring( 0, interwikipoint ); 196 final String wikiPage = m_ref.substring( interwikipoint+1 ); 197 198 url = engine.getInterWikiURL( extWiki ); 199 if( url != null ) { 200 url = TextUtil.replaceString( url, "%s", wikiPage ); 201 } 202 } else if( m_ref.startsWith("#") ) { 203 // Local link 204 } else if( TextUtil.isNumber(m_ref) ) { 205 // Reference 206 } else { 207 final int hashMark; 208 209 final String parms = (m_version != null) ? "version="+getVersion() : null; 210 211 // Internal wiki link, but is it an attachment link? 212 final Page p = engine.getManager( PageManager.class ).getPage( m_pageName ); 213 if( p instanceof Attachment ) { 214 url = m_wikiContext.getURL( ContextEnum.PAGE_ATTACH.getRequestContext(), m_pageName ); 215 } else if( (hashMark = m_ref.indexOf('#')) != -1 ) { 216 // It's an internal Wiki link, but to a named section 217 218 final String namedSection = m_ref.substring( hashMark+1 ); 219 String reallink = m_ref.substring( 0, hashMark ); 220 reallink = MarkupParser.cleanLink( reallink ); 221 222 String matchedLink; 223 String sectref = ""; 224 if( ( matchedLink = engine.getFinalPageName( reallink ) ) != null ) { 225 sectref = "section-" + engine.encodeName( matchedLink ) + "-" + namedSection; 226 sectref = "#" + sectref.replace( '%', '_' ); 227 } else { 228 matchedLink = reallink; 229 } 230 231 url = makeBasicURL( m_context, matchedLink, parms ) + sectref; 232 } else { 233 final String reallink = MarkupParser.cleanLink( m_ref ); 234 url = makeBasicURL( m_context, reallink, parms ); 235 } 236 } 237 } else if( m_pageName != null && !m_pageName.isEmpty() ) { 238 final Page p = engine.getManager( PageManager.class ).getPage( m_pageName ); 239 240 String parms = (m_version != null) ? "version="+getVersion() : null; 241 242 parms = addParamsForRecipient( parms, m_containedParams ); 243 244 if( p instanceof Attachment ) { 245 String ctx = m_context; 246 // Switch context appropriately when attempting to view an 247 // attachment, but don't override the context setting otherwise 248 if( m_context == null || m_context.equals( ContextEnum.PAGE_VIEW.getRequestContext() ) ) { 249 ctx = ContextEnum.PAGE_ATTACH.getRequestContext(); 250 } 251 url = engine.getURL( ctx, m_pageName, parms ); 252 //url = m_wikiContext.getURL( ctx, m_pageName, parms ); 253 } else { 254 url = makeBasicURL( m_context, m_pageName, parms ); 255 } 256 } else { 257 final String page = engine.getFrontPage(); 258 url = makeBasicURL( m_context, page, null ); 259 } 260 261 return url; 262 } 263 264 private String addParamsForRecipient( final String addTo, final Map< String, String > params ) { 265 if( params == null || params.size() == 0 ) { 266 return addTo; 267 } 268 final StringBuilder buf = new StringBuilder(); 269 final Iterator< Map.Entry< String, String > > it = params.entrySet().iterator(); 270 while( it.hasNext() ) { 271 final Map.Entry< String, String > e = it.next(); 272 final String n = e.getKey(); 273 final String v = e.getValue(); 274 buf.append( n ); 275 buf.append( "=" ); 276 buf.append( v ); 277 if( it.hasNext() ) { 278 buf.append( "&" ); 279 } 280 } 281 if( addTo == null ) { 282 return buf.toString(); 283 } 284 if( !addTo.endsWith( "&" ) ) { 285 return addTo + "&" + buf; 286 } 287 return addTo + buf; 288 } 289 290 private String makeBasicURL( final String context, final String page, String parms ) { 291 final Engine engine = m_wikiContext.getEngine(); 292 293 if( context.equals( ContextEnum.PAGE_DIFF.getRequestContext() ) ) { 294 int r1; 295 int r2; 296 297 if( DiffLinkTag.VER_LATEST.equals( getVersion() ) ) { 298 final Page latest = engine.getManager( PageManager.class ).getPage( page, WikiProvider.LATEST_VERSION ); 299 300 r1 = latest.getVersion(); 301 } else if( DiffLinkTag.VER_PREVIOUS.equals(getVersion()) ) { 302 r1 = m_wikiContext.getPage().getVersion() - 1; 303 r1 = Math.max( r1, 1 ); 304 } else if( DiffLinkTag.VER_CURRENT.equals(getVersion()) ) { 305 r1 = m_wikiContext.getPage().getVersion(); 306 } else { 307 r1 = Integer.parseInt( getVersion() ); 308 } 309 310 if( DiffLinkTag.VER_LATEST.equals(m_compareToVersion) ) { 311 final Page latest = engine.getManager( PageManager.class ).getPage( page, WikiProvider.LATEST_VERSION ); 312 313 r2 = latest.getVersion(); 314 } else if( DiffLinkTag.VER_PREVIOUS.equals(m_compareToVersion) ) { 315 r2 = m_wikiContext.getPage().getVersion() - 1; 316 r2 = Math.max( r2, 1 ); 317 } else if( DiffLinkTag.VER_CURRENT.equals(m_compareToVersion) ) { 318 r2 = m_wikiContext.getPage().getVersion(); 319 } else { 320 r2 = Integer.parseInt( m_compareToVersion ); 321 } 322 323 parms = "r1="+r1+"&r2="+r2; 324 } 325 326 return engine.getURL( m_context, m_pageName, parms ); 327 } 328 329 @Override 330 public int doWikiStartTag() throws Exception { 331 return EVAL_BODY_BUFFERED; 332 } 333 334 @Override 335 public int doEndTag() { 336 try { 337 final Engine engine = m_wikiContext.getEngine(); 338 final JspWriter out = pageContext.getOut(); 339 final String url = figureOutURL(); 340 341 final StringBuilder sb = new StringBuilder( 20 ); 342 343 sb.append( (m_cssClass != null) ? "class=\""+m_cssClass+"\" " : "" ); 344 sb.append( (m_style != null) ? "style=\""+m_style+"\" " : "" ); 345 sb.append( (m_target != null ) ? "target=\""+m_target+"\" " : "" ); 346 sb.append( (m_title != null ) ? "title=\""+m_title+"\" " : "" ); 347 sb.append( (m_rel != null ) ? "rel=\""+m_rel+"\" " : "" ); 348 sb.append( (m_accesskey != null) ? "accesskey=\""+m_accesskey+"\" " : "" ); 349 sb.append( (m_tabindex != null) ? "tabindex=\""+m_tabindex+"\" " : "" ); 350 351 if( engine.getManager( PageManager.class ).getPage( m_pageName ) instanceof Attachment ) { 352 sb.append( engine.getManager( AttachmentManager.class ).forceDownload( m_pageName ) ? "download " : "" ); 353 } 354 355 switch( m_format ) { 356 case URL: 357 out.print( url ); 358 break; 359 default: 360 case ANCHOR: 361 out.print("<a "+ sb +" href=\""+url+"\">"); 362 break; 363 } 364 365 // Add any explicit body content. This is not the intended use of LinkTag, but happens to be the way it has worked previously. 366 if( m_bodyContent != null ) { 367 final String linktext = m_bodyContent.getString().trim(); 368 out.write( linktext ); 369 } 370 371 // Finish off by closing opened anchor 372 if( m_format == ANCHOR ) out.print("</a>"); 373 } catch( final Exception e ) { 374 // Yes, we want to catch all exceptions here, including RuntimeExceptions 375 log.error( "Tag failed", e ); 376 } 377 378 return EVAL_PAGE; 379 } 380 381 @Override 382 public void setBodyContent( final BodyContent bc ) 383 { 384 m_bodyContent = bc; 385 } 386 387 @Override 388 public void doInitBody() { 389 } 390 391}