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.ContextEnum; 023import org.apache.wiki.pages.PageManager; 024import org.apache.wiki.util.TextUtil; 025 026import javax.servlet.http.HttpSession; 027import javax.servlet.jsp.JspWriter; 028import java.io.IOException; 029import java.io.Serializable; 030import java.util.LinkedList; 031 032/** 033 * Implement a "breadcrumb" (most recently visited) trail. This tag can be added to any view jsp page. 034 * Separate breadcrumb trails are not tracked across multiple browser windows.<br> 035 * The optional attributes are: 036 * <p> 037 * <b>maxpages</b>, the number of pages to store, 10 by default<br> 038 * <b>separator</b>, the separator string to use between pages, " | " by default<br> 039 * </p> 040 * 041 * <p> 042 * This class is implemented by storing a breadcrumb trail, which is a 043 * fixed size queue, into a session variable "breadCrumbTrail". 044 * This queue is displayed as a series of links separated by a separator 045 * character. 046 * </p> 047 */ 048public class BreadcrumbsTag extends WikiTagBase 049{ 050 private static final long serialVersionUID = 0L; 051 052 private static final Logger log = Logger.getLogger(BreadcrumbsTag.class); 053 /** The name of the session attribute representing the breadcrumbtrail */ 054 public static final String BREADCRUMBTRAIL_KEY = "breadCrumbTrail"; 055 private int m_maxQueueSize = 11; 056 private String m_separator = ", "; 057 058 /** 059 * {@inheritDoc} 060 */ 061 @Override 062 public void initTag() 063 { 064 super.initTag(); 065 m_maxQueueSize = 11; 066 m_separator = ", "; 067 } 068 069 /** 070 * Returns the maxpages. This may differ from what was set by setMaxpages(). 071 * 072 * @return The current size of the pages. 073 */ 074 public int getMaxpages() 075 { 076 return m_maxQueueSize; 077 } 078 079 /** 080 * Sets how many pages to show. 081 * 082 * @param maxpages The amount. 083 */ 084 public void setMaxpages( final int maxpages) 085 { 086 m_maxQueueSize = maxpages + 1; 087 } 088 089 /** 090 * Get the separator string. 091 * 092 * @return The string set in setSeparator() 093 */ 094 public String getSeparator() 095 { 096 return m_separator; 097 } 098 099 /** 100 * Set the separator string. 101 * 102 * @param separator A string which separates the page names. 103 */ 104 public void setSeparator( final String separator) 105 { 106 m_separator = TextUtil.replaceEntities( separator ); 107 } 108 109 /** 110 * {@inheritDoc} 111 */ 112 @Override 113 public int doWikiStartTag() throws IOException { 114 final HttpSession session = pageContext.getSession(); 115 FixedQueue trail = (FixedQueue) session.getAttribute(BREADCRUMBTRAIL_KEY); 116 final String page = m_wikiContext.getPage().getName(); 117 118 if( trail == null ) { 119 trail = new FixedQueue(m_maxQueueSize); 120 } else { 121 // check if page still exists (could be deleted/renamed by another user) 122 for (int i = 0;i<trail.size();i++) { 123 if( !m_wikiContext.getEngine().getManager( PageManager.class ).wikiPageExists( trail.get( i ) ) ) { 124 trail.remove(i); 125 } 126 } 127 } 128 129 if( m_wikiContext.getRequestContext().equals( ContextEnum.PAGE_VIEW.getRequestContext() ) ) { 130 if( m_wikiContext.getEngine().getManager( PageManager.class ).wikiPageExists( page ) ) { 131 if( trail.isEmpty() ) { 132 trail.pushItem( page ); 133 } else { 134 // Don't add the page to the queue if the page was just refreshed 135 if( !trail.getLast().equals( page ) ) { 136 trail.pushItem( page ); 137 } 138 } 139 } else { 140 log.debug( "didn't add page because it doesn't exist: " + page ); 141 } 142 } 143 144 session.setAttribute(BREADCRUMBTRAIL_KEY, trail); 145 146 // 147 // Print out the breadcrumb trail 148 // 149 150 // FIXME: this code would be much simpler if we could just output the [pagename] and then use the 151 // wiki engine to output the appropriate wikilink 152 153 final JspWriter out = pageContext.getOut(); 154 final int queueSize = trail.size(); 155 final String linkclass = "wikipage"; 156 String curPage = null; 157 158 for( int i = 0; i < queueSize - 1; i++ ) { 159 curPage = trail.get(i); 160 161 //FIXME: I can't figure out how to detect the appropriate jsp page to put here, so I hard coded Wiki.jsp 162 //This breaks when you view an attachment metadata page 163 out.print( "<a class=\"" + linkclass + "\" href=\"" + m_wikiContext.getViewURL(curPage) + "\">" + 164 TextUtil.replaceEntities( curPage ) + 165 "</a>" ); 166 167 if( i < queueSize - 2 ) { 168 out.print(m_separator); 169 } 170 } 171 172 return SKIP_BODY; 173 } 174 175 /** 176 * Extends the LinkedList class to provide a fixed-size queue implementation 177 */ 178 public static class FixedQueue extends LinkedList< String > implements Serializable { 179 private int m_size; 180 private static final long serialVersionUID = 0L; 181 182 FixedQueue( final int size ) { 183 m_size = size; 184 } 185 186 String pushItem( final String o ) { 187 add( o ); 188 if( size() > m_size ) { 189 return removeFirst(); 190 } 191 192 return null; 193 } 194 195 /** 196 * @param pageName the page to be deleted from the breadcrumb 197 */ 198 public void removeItem( final String pageName ) { 199 for( int i = 0; i < size(); i++ ) { 200 final String page = get( i ); 201 if( page != null && page.equals( pageName ) ) { 202 remove( page ); 203 } 204 } 205 } 206 207 } 208} 209