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