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.wiki.WikiContext; 022 023import javax.servlet.http.HttpSession; 024import javax.servlet.jsp.JspWriter; 025 026import org.apache.log4j.Logger; 027 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(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