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