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
020package org.apache.wiki.plugin;
021
022import org.apache.commons.lang3.StringUtils;
023import org.apache.logging.log4j.LogManager;
024import org.apache.logging.log4j.Logger;
025import org.apache.wiki.api.core.Context;
026import org.apache.wiki.api.core.ContextEnum;
027import org.apache.wiki.api.exceptions.PluginException;
028import org.apache.wiki.api.exceptions.ProviderException;
029import org.apache.wiki.api.plugin.Plugin;
030import org.apache.wiki.pages.PageManager;
031import org.apache.wiki.references.ReferenceManager;
032import org.jdom2.Element;
033import org.jdom2.Namespace;
034import org.jdom2.output.Format;
035import org.jdom2.output.XMLOutputter;
036
037import java.util.List;
038import java.util.Locale;
039import java.util.Map;
040import java.util.ResourceBundle;
041import java.util.Set;
042import java.util.regex.Pattern;
043import java.util.stream.Collectors;
044
045/**
046 *  A Plugin that creates an index of pages according to a certain pattern.
047 *  <br />
048 *  The default is to include all pages.
049 *  <p>
050 *  This is a rewrite of the earlier JSPWiki IndexPlugin using JDOM2.
051 8  </p>
052 *  <p>
053 *  Parameters (from AbstractReferralPlugin):
054 *  </p>
055 *  <ul>
056 *    <li><b>include</b> - A regexp pattern for marking which pages should be included.</li>
057 *    <li><b>exclude</b> - A regexp pattern for marking which pages should be excluded.</li>
058 *  </ul>
059 *  
060 * @author Ichiro Furusato
061 */
062public class IndexPlugin extends AbstractReferralPlugin implements Plugin {
063
064    private static final Logger LOG = LogManager.getLogger(IndexPlugin.class);
065
066    private final Namespace xmlns_XHTML = Namespace.getNamespace("http://www.w3.org/1999/xhtml");
067   
068    @Override
069    public String getDisplayName(Locale locale) {
070        final ResourceBundle rb = ResourceBundle.getBundle(PluginManager.PLUGIN_I18N_RESOURCE, locale);
071        return rb.getString(this.getClass().getSimpleName());
072    }
073    
074    /**
075     * {@inheritDoc}
076     */
077    @Override
078    public String execute( final Context context, final Map<String,String> params ) throws PluginException {
079        final String include = params.get(PARAM_INCLUDE);
080        final String exclude = params.get(PARAM_EXCLUDE);
081        
082        final Element masterDiv = getElement("div","index");
083        final Element indexDiv = getElement("div","header");
084        masterDiv.addContent(indexDiv);
085        try {
086            final List<String> pages = listPages(context,include,exclude);
087            context.getEngine().getManager( PageManager.class ).getPageSorter().sort(pages);
088            char initialChar = ' ';
089            Element currentDiv = new Element("div",xmlns_XHTML);            
090            for( final String name : pages ) {
091                if( StringUtils.isNotBlank( name ) &&  name.charAt(0) != initialChar ) {
092                    if ( initialChar != ' ' ) {
093                        indexDiv.addContent(" - ");
094                    }                    
095                    initialChar = name.charAt(0);
096                    masterDiv.addContent(makeHeader(String.valueOf(initialChar)));
097                    currentDiv = getElement("div","body");
098                    masterDiv.addContent(currentDiv);
099                    indexDiv.addContent(getLink("#"+initialChar,String.valueOf(initialChar)));
100                } else {
101                    currentDiv.addContent(", ");
102                }
103                currentDiv.addContent( getLink( context.getURL( ContextEnum.PAGE_VIEW.getRequestContext(), name ), name ) );
104            }
105            
106        } catch( final ProviderException e ) {
107            LOG.warn("could not load page index",e);
108            throw new PluginException( e.getMessage() );
109        }
110        // serialize to raw format string (no changes to whitespace)
111        final XMLOutputter out = new XMLOutputter(Format.getRawFormat());
112        return out.outputString(masterDiv);
113    }
114
115    private Element getLink( final String href, final String content ) {
116        final Element a = new Element( "a", xmlns_XHTML );
117        a.setAttribute( "href", href );
118        a.addContent( content );
119        return a;
120    }
121
122    private Element makeHeader( final String initialChar ) {
123        final Element span = getElement( "span", "section" );
124        final Element a = new Element( "a", xmlns_XHTML );
125        a.setAttribute( "id", initialChar );
126        a.addContent( initialChar );
127        span.addContent( a );
128        return span;
129    }
130
131    private Element getElement( final String gi, final String classValue ) {
132        final Element elt = new Element( gi, xmlns_XHTML );
133        elt.setAttribute( "class", classValue );
134        return elt;
135    }
136
137    /**
138     *  Grabs a list of all pages and filters them according to the include/exclude patterns.
139     *  
140     * @param context
141     * @param include
142     * @param exclude
143     * @return A list containing page names which matched the filters.
144     * @throws ProviderException
145     */
146    private List<String> listPages( final Context context, final String include, final String exclude ) throws ProviderException {
147        final Pattern includePtrn = include != null ? Pattern.compile( include ) : Pattern.compile(".*");
148        final Pattern excludePtrn = exclude != null ? Pattern.compile( exclude ) : Pattern.compile("\\p{Cntrl}"); // there are no control characters in page names
149        final List< String > result;
150        final Set< String > pages = context.getEngine().getManager( ReferenceManager.class ).findCreated();
151        result = pages.stream().filter(pageName -> !excludePtrn.matcher(pageName).matches()).filter(pageName -> includePtrn.matcher(pageName).matches()).collect(Collectors.toList());
152        return result;
153    }
154
155}