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