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