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 org.apache.wiki.WikiContext;
022
023 import javax.servlet.http.HttpSession;
024 import javax.servlet.jsp.JspWriter;
025
026 import org.apache.log4j.Logger;
027
028 import java.io.IOException;
029 import java.io.Serializable;
030 import 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 */
048 public 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(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(String separator)
105 {
106 m_separator = separator;
107 }
108
109 /**
110 * {@inheritDoc}
111 */
112 @Override
113 public int doWikiStartTag() throws IOException
114 {
115 HttpSession session = pageContext.getSession();
116 FixedQueue trail = (FixedQueue) session.getAttribute(BREADCRUMBTRAIL_KEY);
117
118 String page = m_wikiContext.getPage().getName();
119
120 if( trail == null )
121 {
122 trail = new FixedQueue(m_maxQueueSize);
123 } else {
124 // check if page still exists (could be deleted/renamed by another user)
125 for (int i = 0;i<trail.size();i++) {
126 if (!m_wikiContext.getEngine().pageExists(trail.get(i))) {
127 trail.remove(i);
128 }
129 }
130 }
131
132 if (m_wikiContext.getRequestContext().equals(WikiContext.VIEW))
133 {
134 if (m_wikiContext.getEngine().pageExists(page))
135 {
136 if (trail.isEmpty())
137 {
138 trail.pushItem(page);
139 }
140 else
141 {
142 //
143 // Don't add the page to the queue if the page was just refreshed
144 //
145 if (!trail.getLast().equals(page))
146 {
147 trail.pushItem(page);
148 }
149 }
150 }
151 else
152 {
153 log.debug("didn't add page because it doesn't exist: " + page);
154 }
155 }
156
157 session.setAttribute(BREADCRUMBTRAIL_KEY, trail);
158
159 //
160 // Print out the breadcrumb trail
161 //
162
163 // FIXME: this code would be much simpler if we could just output the [pagename] and then use the
164 // wiki engine to output the appropriate wikilink
165
166 JspWriter out = pageContext.getOut();
167 int queueSize = trail.size();
168 String linkclass = "wikipage";
169 String curPage = null;
170
171 for( int i = 0; i < queueSize - 1; i++ )
172 {
173 curPage = trail.get(i);
174
175 //FIXME: I can't figure out how to detect the appropriate jsp page to put here, so I hard coded Wiki.jsp
176 //This breaks when you view an attachment metadata page
177 out.print("<a class=\"" + linkclass + "\" href=\"" + m_wikiContext.getViewURL(curPage)+ "\">" + curPage + "</a>");
178
179 if( i < queueSize - 2 )
180 {
181 out.print(m_separator);
182 }
183 }
184
185 return SKIP_BODY;
186 }
187
188 /**
189 * Extends the LinkedList class to provide a fixed-size queue implementation
190 */
191 public static class FixedQueue
192 extends LinkedList<String>
193 implements Serializable
194 {
195 private int m_size;
196 private static final long serialVersionUID = 0L;
197
198 FixedQueue(int size)
199 {
200 m_size = size;
201 }
202
203 String pushItem(String o)
204 {
205 add(o);
206 if( size() > m_size )
207 {
208 return removeFirst();
209 }
210
211 return null;
212 }
213
214 /**
215 * @param pageName
216 * the page to be deleted from the breadcrumb
217 */
218 public void removeItem(String pageName)
219 {
220 for (int i = 0; i < size(); i++)
221 {
222 String page = get(i);
223 if (page != null && page.equals(pageName))
224 {
225 remove(page);
226 }
227 }
228 }
229
230 }
231 }
232