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.plugin;
020
021 import java.util.*;
022
023 import org.apache.log4j.Logger;
024 import org.apache.oro.text.regex.*;
025 import org.apache.wiki.ReferenceManager;
026 import org.apache.wiki.WikiContext;
027 import org.apache.wiki.WikiEngine;
028 import org.apache.wiki.WikiPage;
029 import org.apache.wiki.api.exceptions.PluginException;
030 import org.apache.wiki.api.plugin.WikiPlugin;
031 import org.apache.wiki.util.TextUtil;
032
033
034 /**
035 * Displays the pages referring to the current page.
036 *
037 * <p>Parameters</p>
038 * <ul>
039 * <li><b>name</b> - Name of the root page. Default name of calling page
040 * <li><b>type</b> - local|externalattachment
041 * <li><b>depth</b> - How many levels of pages to be parsed.
042 * <li><b>include</b> - Include only these pages. (eg. include='UC.*|BP.*' )
043 * <li><b>exclude</b> - Exclude with this pattern. (eg. exclude='LeftMenu' )
044 * <li><b>format</b> - full|compact, FULL now expands all levels correctly
045 * </ul>
046 *
047 */
048 public class ReferredPagesPlugin implements WikiPlugin
049 {
050 private static Logger log = Logger.getLogger( ReferredPagesPlugin.class );
051 private WikiEngine m_engine;
052 private int m_depth;
053 private HashSet<String> m_exists = new HashSet<String>();
054 private StringBuffer m_result = new StringBuffer(1024);
055 private PatternMatcher m_matcher = new Perl5Matcher();
056 private Pattern m_includePattern;
057 private Pattern m_excludePattern;
058 private boolean m_formatCompact = true;
059 private boolean m_formatSort = false;
060
061 /** The parameter name for the root page to start from. Value is <tt>{@value}</tt>. */
062 public static final String PARAM_ROOT = "page";
063
064 /** The parameter name for the depth. Value is <tt>{@value}</tt>. */
065 public static final String PARAM_DEPTH = "depth";
066
067 /** The parameter name for the type of the references. Value is <tt>{@value}</tt>. */
068 public static final String PARAM_TYPE = "type";
069
070 /** The parameter name for the included pages. Value is <tt>{@value}</tt>. */
071 public static final String PARAM_INCLUDE = "include";
072
073 /** The parameter name for the excluded pages. Value is <tt>{@value}</tt>. */
074 public static final String PARAM_EXCLUDE = "exclude";
075
076 /** The parameter name for the format. Value is <tt>{@value}</tt>. */
077 public static final String PARAM_FORMAT = "format";
078
079 /** The minimum depth. Value is <tt>{@value}</tt>. */
080 public static final int MIN_DEPTH = 1;
081
082 /** The maximum depth. Value is <tt>{@value}</tt>. */
083 public static final int MAX_DEPTH = 8;
084
085 /**
086 * {@inheritDoc}
087 */
088 public String execute( WikiContext context, Map<String, String> params )
089 throws PluginException
090 {
091 m_engine = context.getEngine();
092
093 WikiPage page = context.getPage();
094 if( page == null ) return "";
095
096 // parse parameters
097 String rootname = params.get( PARAM_ROOT );
098 if( rootname == null ) rootname = page.getName() ;
099
100 String format = params.get( PARAM_FORMAT );
101 if( format == null) format = "";
102 if( format.indexOf( "full" ) >=0 ) m_formatCompact = false ;
103 if( format.indexOf( "sort" ) >=0 ) m_formatSort = true ;
104
105 m_depth = TextUtil.parseIntParameter( params.get( PARAM_DEPTH ), MIN_DEPTH );
106 if( m_depth > MAX_DEPTH ) m_depth = MAX_DEPTH;
107
108 String includePattern = params.get(PARAM_INCLUDE);
109 if( includePattern == null ) includePattern = ".*";
110
111 String excludePattern = params.get(PARAM_EXCLUDE);
112 if( excludePattern == null ) excludePattern = "^$";
113
114 log.debug( "Fetching referred pages for "+ rootname +
115 " with a depth of "+ m_depth +
116 " with include pattern of "+ includePattern +
117 " with exclude pattern of "+ excludePattern );
118
119 //
120 // do the actual work
121 //
122 String href = context.getViewURL(rootname);
123 String title = "ReferredPagesPlugin: depth["+m_depth+
124 "] include["+includePattern+"] exclude["+excludePattern+
125 "] format["+(m_formatCompact ? "compact" : "full") +
126 (m_formatSort ? " sort" : "") + "]";
127
128 m_result.append("<div class=\"ReferredPagesPlugin\">\n");
129 m_result.append("<a class=\"wikipage\" href=\""+ href +
130 "\" title=\"" + title +
131 "\">" + rootname + "</a>\n");
132 m_exists.add(rootname);
133
134 // pre compile all needed patterns
135 // glob compiler : * is 0..n instance of any char -- more convenient as input
136 // perl5 compiler : .* is 0..n instances of any char -- more powerful
137 //PatternCompiler g_compiler = new GlobCompiler();
138 PatternCompiler compiler = new Perl5Compiler();
139
140 try
141 {
142 m_includePattern = compiler.compile(includePattern);
143
144 m_excludePattern = compiler.compile(excludePattern);
145 }
146 catch( MalformedPatternException e )
147 {
148 if (m_includePattern == null )
149 {
150 throw new PluginException("Illegal include pattern detected.");
151 }
152 else if (m_excludePattern == null )
153 {
154 throw new PluginException("Illegal exclude pattern detected.");
155 }
156 else
157 {
158 throw new PluginException("Illegal internal pattern detected.");
159 }
160 }
161
162 // go get all referred links
163 getReferredPages(context,rootname, 0);
164
165 // close and finish
166 m_result.append ("</div>\n" ) ;
167
168 return m_result.toString() ;
169 }
170
171
172 /**
173 * Retrieves a list of all referred pages. Is called recursively
174 * depending on the depth parameter
175 */
176 @SuppressWarnings("unchecked")
177 private void getReferredPages( WikiContext context, String pagename, int depth )
178 {
179 if( depth >= m_depth ) return; // end of recursion
180 if( pagename == null ) return;
181 if( !m_engine.pageExists(pagename) ) return;
182
183 ReferenceManager mgr = m_engine.getReferenceManager();
184
185 Collection<String> allPages = mgr.findRefersTo( pagename );
186
187 handleLinks( context, allPages, ++depth, pagename );
188 }
189
190 private void handleLinks(WikiContext context,Collection<String> links, int depth, String pagename)
191 {
192 boolean isUL = false;
193 HashSet<String> localLinkSet = new HashSet<String>(); // needed to skip multiple
194 // links to the same page
195 localLinkSet.add(pagename);
196
197 ArrayList<String> allLinks = new ArrayList<String>();
198
199 if( links != null )
200 allLinks.addAll( links );
201
202 if( m_formatSort ) context.getEngine().getPageSorter().sort( allLinks );
203
204 for( Iterator i = allLinks.iterator(); i.hasNext(); )
205 {
206 String link = (String) i.next() ;
207
208 if( localLinkSet.contains( link ) ) continue; // skip multiple
209 // links to the same
210 // page
211 localLinkSet.add( link );
212
213 if( !m_engine.pageExists( link ) ) continue; // hide links to non
214 // existing pages
215
216 if( m_matcher.matches( link , m_excludePattern ) ) continue;
217 if( !m_matcher.matches( link , m_includePattern ) ) continue;
218
219 if( m_exists.contains( link ) )
220 {
221 if( !m_formatCompact )
222 {
223 if( !isUL )
224 {
225 isUL = true; m_result.append("<ul>\n");
226 }
227
228 m_result.append("<li> " + link + " </li>\n");
229
230 getReferredPages( context, link, depth ); // added recursive
231 // call - on general
232 // request
233 }
234 }
235 else
236 {
237 if( !isUL )
238 {
239 isUL = true; m_result.append("<ul>\n");
240 }
241
242 String href = context.getURL(WikiContext.VIEW,link);
243 m_result.append("<li><a class=\"wikipage\" href=\""+ href +"\">"+link+"</a></li>\n" );
244
245 m_exists.add( link );
246
247 getReferredPages( context, link, depth );
248 }
249 }
250
251 if( isUL ) m_result.append("</ul>\n");
252 }
253 }
254