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