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