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