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