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 org.apache.log4j.Logger;
022import org.apache.wiki.api.core.Context;
023import org.apache.wiki.api.core.Engine;
024import org.apache.wiki.api.core.Page;
025import org.apache.wiki.api.exceptions.PluginException;
026import org.apache.wiki.api.exceptions.RedirectException;
027import org.apache.wiki.api.exceptions.WikiException;
028import org.apache.wiki.api.plugin.Plugin;
029import org.apache.wiki.api.spi.Wiki;
030import org.apache.wiki.pages.PageManager;
031import org.apache.wiki.parser.MarkupParser;
032import org.apache.wiki.preferences.Preferences;
033
034import java.io.PrintWriter;
035import java.io.StringWriter;
036import java.security.Principal;
037import java.text.MessageFormat;
038import java.text.SimpleDateFormat;
039import java.util.Date;
040import java.util.Map;
041import java.util.Properties;
042import java.util.ResourceBundle;
043import java.util.StringTokenizer;
044
045/**
046 *  Provides a handler for bug reports.  Still under construction.
047 *
048 *  <p>Parameters : </p>
049 *  <ul>
050 *  <li><b>title</b> -  title of the bug, this is required.  If it is empty (as in "")  it is a signal to the handler to return quietly.</li>
051 *  <li><b>description</b> - description of the bug.</li>
052 *  <li><b>version</b> - version</li>
053 *  <li><b>map</b> - I have no idea </li>
054 *  <li><b>page</b> - The name of the page to be created for this bug report </li>
055 *  </ul>
056 *
057 */
058public class BugReportHandler implements Plugin {
059
060    private static final Logger log = Logger.getLogger( BugReportHandler.class );
061    private static final String DEFAULT_DATEFORMAT = "dd-MMM-yyyy HH:mm:ss zzz";
062
063    /** Parameter name for setting the title.  Value is <tt>{@value}</tt>. */
064    public static final String PARAM_TITLE          = "title";
065    /** Parameter name for setting the description.  Value is <tt>{@value}</tt>. */
066    public static final String PARAM_DESCRIPTION    = "description";
067    /** Parameter name for setting the version.  Value is <tt>{@value}</tt>. */
068    public static final String PARAM_VERSION        = "version";
069    /** Parameter name for setting the map.  Value is <tt>{@value}</tt>. */
070    public static final String PARAM_MAPPINGS       = "map";
071    /** Parameter name for setting the page.  Value is <tt>{@value}</tt>. */
072    public static final String PARAM_PAGE           = "page";
073
074    /**
075     *  {@inheritDoc}
076     */
077    @Override
078    public String execute( final Context context, final Map< String, String > params ) throws PluginException {
079        final String title = params.get( PARAM_TITLE );
080        String description = params.get( PARAM_DESCRIPTION );
081        String version = params.get( PARAM_VERSION );
082        String submitter = null;
083        final SimpleDateFormat format = new SimpleDateFormat( DEFAULT_DATEFORMAT );
084        final ResourceBundle rb = Preferences.getBundle( context, Plugin.CORE_PLUGINS_RESOURCEBUNDLE );
085        final Principal wup = context.getCurrentUser();
086
087        if( wup != null ) {
088            submitter = wup.getName();
089        }
090
091        if( title == null ) {
092            throw new PluginException(rb.getString("bugreporthandler.titlerequired"));
093        }
094        if( title.length() == 0 ) {
095            return "";
096        }
097
098        if( description == null ) {
099            description = "";
100        }
101        if( version == null ) {
102            version = "unknown";
103        }
104
105        final Properties mappings = parseMappings( params.get( PARAM_MAPPINGS ) );
106
107        //  Start things
108        try {
109            final StringWriter str = new StringWriter();
110            final PrintWriter out = new PrintWriter( str );
111            final Date d = new Date();
112
113            //  Outputting of basic data
114            out.println( "|" + mappings.getProperty(PARAM_TITLE,"Title" ) + "|" + title );
115            out.println( "|" + mappings.getProperty("date","Date" ) + "|" + format.format( d ) );
116            out.println( "|" + mappings.getProperty(PARAM_VERSION,"Version" ) + "|" + version );
117            if( submitter != null ) {
118                out.println("|"+mappings.getProperty("submitter","Submitter") + "|" + submitter );
119            }
120
121            //  Outputting the other parameters added to this.
122            for( final Map.Entry< String, String > entry : params.entrySet() ) {
123                if( !( entry.getKey().equals( PARAM_TITLE ) ||
124                       entry.getKey().equals( PARAM_DESCRIPTION ) ||
125                       entry.getKey().equals( PARAM_VERSION ) ||
126                       entry.getKey().equals( PARAM_MAPPINGS ) ||
127                       entry.getKey().equals( PARAM_PAGE ) ||
128                       entry.getKey().startsWith( "_" )
129                     ) ) {
130                    //  If no mapping has been defined, just ignore it.
131                    final String head = mappings.getProperty( entry.getKey(), entry.getKey() );
132                    if( head.length() > 0 ) {
133                        out.println( "|" + head + "|" + entry.getValue() );
134                    }
135                }
136            }
137
138            out.println();
139            out.println( description );
140            out.close();
141
142            //  Now create a new page for this bug report
143            final String pageName = findNextPage( context, title, params.get( PARAM_PAGE ) );
144            final Page newPage = Wiki.contents().page( context.getEngine(), pageName );
145            final Context newContext = context.clone();
146            newContext.setPage( newPage );
147            context.getEngine().getManager( PageManager.class ).saveText( newContext, str.toString() );
148
149            final MessageFormat formatter = new MessageFormat("");
150            formatter.applyPattern( rb.getString("bugreporthandler.new") );
151            final String[] args = { "<a href=\""+context.getViewURL(pageName)+"\">"+pageName+"</a>" };
152
153            return formatter.format( args );
154        } catch( final RedirectException e ) {
155            log.info("Saving not allowed, reason: '"+e.getMessage()+"', can't redirect to "+e.getRedirect());
156            throw new PluginException("Saving not allowed, reason: "+e.getMessage());
157        } catch( final WikiException e ) {
158            log.error( "Unable to save page!", e );
159            return rb.getString("bugreporthandler.unable" );
160        }
161    }
162
163    /**
164     *  Finds a free page name for adding the bug report.  Tries to construct a page, and if it's found, adds a number to it
165     *  and tries again.
166     */
167    private synchronized String findNextPage( final Context context, final String title, final String baseName ) {
168        final String basicPageName = ( ( baseName != null ) ? baseName : "Bug" ) + MarkupParser.cleanLink( title );
169        final Engine engine = context.getEngine();
170
171        String pageName = basicPageName;
172        long   lastbug  = 2;
173        while( engine.getManager( PageManager.class ).wikiPageExists( pageName ) ) {
174            pageName = basicPageName + lastbug++;
175        }
176
177        return pageName;
178    }
179
180    /**
181     *  Just parses a mappings list in the form of "a=b;b=c;c=d".
182     *  <p>
183     *  FIXME: Should probably be in TextUtil or somewhere.
184     */
185    private Properties parseMappings( final String mappings ) {
186        final Properties props = new Properties();
187        if( mappings == null ) {
188            return props;
189        }
190
191        final StringTokenizer tok = new StringTokenizer( mappings, ";" );
192        while( tok.hasMoreTokens() ) {
193            final String t = tok.nextToken();
194            final int colon = t.indexOf("=");
195            final String key;
196            final String value;
197
198            if( colon > 0 ) {
199                key = t.substring(0,colon);
200                value = t.substring(colon+1);
201            } else {
202                key = t;
203                value = "";
204            }
205
206            props.setProperty( key, value );
207        }
208        return props;
209    }
210
211}