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