Hi!
I understand I am better to propose contributions here rather than on
the users list!
So I move my mods from 2.4 to 2.6.1. I would like to underline how easy
and efficient "WinMerge", open source "compare" program, is for this
kind of tasks.
New attribute for ReferringPagesPlugin:
title='Title which appears ONLY if there are referring pages'
New attribute for RecentPagesPlugin:
referring='Page Name of the page that modified pages must refer'
Very very useful addition to list modified pages signed by a given
person using [PersonName] or categories like [TODO].
New possibilities for before='string inserted before the link' and
after='string inserted after'
(those possibilities are available for all plugins derived from
AbstractReferralPlugin):
1) %p returns the referring page name surrounded with quotes
2) %n is replaced by two end of line: \n\n
3) the resulting after/before string is __repetitively re-interpreted
for Wiki Markup__.
So, if you want to list the second section of all referring pages, you
can write:
[{ReferringPagesPlugin before='!!' after='%n[{InsertPage section=2
page=%p}]%n----%n'}]
This will generate:
!![Referring Page Name 1]
... content of section 2 of the Referring page 1
!![Referring Page Name 2]
... content of section 2 of the Referring page 2
.....
Note: A "section" is a part of a page between horizontal lines (----).
This is very useful if you put (for instance) an abstract of each page
always in the same section:
you can then list those abstracts near the links to each page...
Look at:
http://www.destin.be/CAFE/Wiki.jsp?page=Case%20111-En%20bien.%20On%20pense%20du%20bien%20du%20texte
(or to any "Cases", starting from
http://www.destin.be/CAFE )
This version also contains the support of patch 100 (in a different way):
1) max=0 to list NO pages (but interprets extra=...)
2) %d within extra="%d pages are not listed above"
3) %m latest modification of the pages
Attached source code is for 2.6.1
I would like to emphazize two points:
1) The source code seems to be moving toward accepting sub-parameters
within plugins parameters in the form {0}, {1}, etc. It seems to make
problem with the parser which sees "}" as the end of the plugin call.
Am I right?
2) %letter could be a nice way to indicate sub-parameter (if the
community agree) BUT it would have to be normalized for all core plugins
to ease learning by users.
Hope this helps!
Have a nice day!
Christophe
/*
JSPWiki - a JSP-based WikiWiki clone.
Copyright (C) 2001 Janne Jalkanen ([EMAIL PROTECTED])
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.ecyrd.jspwiki.plugin;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;
import org.apache.ecs.xhtml.*;
import org.apache.log4j.Logger;
import com.ecyrd.jspwiki.TextUtil;
import com.ecyrd.jspwiki.WikiContext;
import com.ecyrd.jspwiki.WikiEngine;
import com.ecyrd.jspwiki.WikiPage;
import com.ecyrd.jspwiki.attachment.Attachment;
import java.security.Principal;
import com.ecyrd.jspwiki.ReferenceManager;
import com.ecyrd.jspwiki.parser.MarkupParser;
/**
* Returns the Recent Changes.
*
* Parameters: since=number of days,
* format=(compact|full)
*
* @author Janne Jalkanen
*/
public class RecentChangesPlugin
implements WikiPlugin
{
private static final String PARAM_FORMAT = "format";
private static final String PARAM_TIME_FORMAT = "timeFormat";
private static final String PARAM_DATE_FORMAT = "dateFormat";
private static final String PARAM_REFERRING = "referring";
/** How many days we show by default. */
private static final int DEFAULT_DAYS = 100*365;
private static Logger log = Logger.getLogger( RecentChangesPlugin.class );
private boolean isSameDay( Date a, Date b )
{
Calendar aa = Calendar.getInstance(); aa.setTime(a);
Calendar bb = Calendar.getInstance(); bb.setTime(b);
return aa.get( Calendar.YEAR ) == bb.get( Calendar.YEAR ) &&
aa.get( Calendar.DAY_OF_YEAR ) == bb.get( Calendar.DAY_OF_YEAR
);
}
/**
* [EMAIL PROTECTED]
*/
public String execute( WikiContext context, Map params )
throws PluginException
{
int since = TextUtil.parseIntParameter( (String)
params.get("since"),
DEFAULT_DAYS );
int spacing = 4;
boolean showAuthor = true;
boolean showChangenote = true;
int tablewidth = 4;
WikiEngine engine = context.getEngine();
//
// Which format we want to see?
//
if( "compact".equals( params.get(PARAM_FORMAT) ) )
{
spacing = 0;
showAuthor = false;
showChangenote = false;
tablewidth = 2;
}
ReferenceManager mgr = engine.getReferenceManager();
Collection filterRef = null;
Object ptr = params.get(PARAM_REFERRING);
if (ptr != null) {
String pageName = (String) ptr;
if ("".equals(filterRef)||"{$username}".equals(pageName)) {
Principal currUser = context.getCurrentUser();
if (currUser != null) pageName = currUser.getName();
else pageName = null;
}
if (pageName != null) {
pageName = MarkupParser.cleanLink( pageName );
filterRef = mgr.findReferrers( pageName );
log.debug("Pages referring to "+pageName+" = "+filterRef);
}
}
Calendar sincedate = new GregorianCalendar();
sincedate.add( Calendar.DAY_OF_MONTH, -since );
log.debug("Calculating recent changes from "+sincedate.getTime());
// FIXME: Should really have a since date on the getRecentChanges
// method.
Collection changes = engine.getRecentChanges();
if( changes != null )
{
Date olddate = new Date(0);
DateFormat fmt = getDateFormat(params);
DateFormat tfmt = getTimeFormat(params);
table rt = new table();
rt.setCellPadding(spacing).setClass("recentchanges");
for( Iterator i = changes.iterator(); i.hasNext(); )
{
WikiPage pageref = (WikiPage) i.next();
Date lastmod = pageref.getLastModified();
if( lastmod.before( sincedate.getTime() ) )
{
break;
}
boolean accepted = true;
if (filterRef != null) accepted =
filterRef.contains(pageref.getName());
if (accepted) {
if( !isSameDay( lastmod, olddate ) )
{
tr row = new tr();
td col = new td();
col.setColSpan(tablewidth).setClass("date");
col.addElement( new b().addElement(fmt.format(lastmod)) );
rt.addElement(row);
row.addElement(col);
olddate = lastmod;
}
String link = context.getURL( pageref instanceof Attachment ?
WikiContext.ATTACH : WikiContext.VIEW,
pageref.getName() ) ;
a linkel = new a(link,engine.beautifyTitle(pageref.getName()));
tr row = new tr();
td col = new td().setWidth("30%").addElement(linkel);
//
// Add the direct link to the attachment info.
//
if( pageref instanceof Attachment )
{
linkel = new
a().setHref(context.getURL(WikiContext.INFO,pageref.getName()));
linkel.addElement( new
img().setSrc(context.getURL(WikiContext.NONE, "images/attachment_small.png")));
col.addElement( linkel );
}
row.addElement(col);
rt.addElement(row);
if( pageref instanceof Attachment )
{
row.addElement( new
td(tfmt.format(lastmod)).setClass("lastchange") );
}
else
{
td infocol = (td) new td().setClass("lastchange");
infocol.addElement( new a(context.getURL(WikiContext.DIFF,
pageref.getName(), "r1=-1"),tfmt.format(lastmod)) );
row.addElement(infocol);
}
//
// Display author information.
//
if( showAuthor )
{
String author = pageref.getAuthor();
td authorinfo = new td();
authorinfo.setClass("author");
if( author != null )
{
if( engine.pageExists(author) )
{
authorinfo.addElement( new
a(context.getURL(WikiContext.VIEW, author),author) );
}
else
{
authorinfo.addElement(author);
}
}
else
{
authorinfo.addElement("unknown");
}
row.addElement( authorinfo );
}
// Change note
if( showChangenote )
{
String changenote =
(String)pageref.getAttribute(WikiPage.CHANGENOTE);
row.addElement( new td(changenote != null ?
TextUtil.replaceEntities(changenote) : "").setClass("changenote") );
}
// Revert note
/*
if( context.hasAdminPermissions() )
{
row.addElement( new td("Revert") );
}
*/
}
}
rt.setPrettyPrint(true);
return rt.toString();
}
return "";
}
// TODO: Ideally the default behavior should be to return the default
format for the default
// locale, but that is at odds with the 1st version of this plugin. We seek
to preserve the
// behaviour of that first version, so to get the default format, the user
must explicitly do
// something like: dateFormat='' timeformat='' which is a odd, but probably
okay.
private DateFormat getTimeFormat(Map params)
{
String formatString = get(params, "HH:mm:ss", PARAM_TIME_FORMAT);
if ("".equals(formatString.trim()))
return SimpleDateFormat.getTimeInstance();
return new SimpleDateFormat(formatString);
}
private DateFormat getDateFormat(Map params)
{
String formatString = get(params, "dd.MM.yyyy", PARAM_DATE_FORMAT);
if ("".equals(formatString.trim()))
return SimpleDateFormat.getDateInstance();
return new SimpleDateFormat(formatString);
}
private String get(Map params, String defaultValue, String paramName)
{
String value = (String) params.get(paramName);
return null == value ? defaultValue : value;
}
}
/*
JSPWiki - a JSP-based WikiWiki clone.
Copyright (C) 2001-2002 Janne Jalkanen ([EMAIL PROTECTED])
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.ecyrd.jspwiki.plugin;
import org.apache.log4j.Logger;
import com.ecyrd.jspwiki.*;
import java.text.MessageFormat;
import java.util.*;
/**
* Displays the pages referring to the current page.
*
* Parameters: <BR>
* max: How many items to show.<BR>
* extras: How to announce extras.<BR>
* From AbstractReferralPlugin:<BR>
* separator: How to separate generated links; default is a wikitext line
break,
* producing a vertical list.<BR>
* maxwidth: maximum width, in chars, of generated links.
*
* @author Janne Jalkanen
*/
public class ReferringPagesPlugin
extends AbstractReferralPlugin
{
private static Logger log = Logger.getLogger( ReferringPagesPlugin.class );
public static final String PARAM_MAX = "max";
public static final String PARAM_EXTRAS = "extras";
public static final String PARAM_PAGE = "page";
public static final String PARAM_TITLE = "title";
public String execute( WikiContext context, Map params )
throws PluginException
{
ReferenceManager refmgr = context.getEngine().getReferenceManager();
String pageName = (String)params.get( PARAM_PAGE );
ResourceBundle rb =
context.getBundle(WikiPlugin.CORE_PLUGINS_RESOURCEBUNDLE);
if( pageName == null )
{
pageName = context.getPage().getName();
}
WikiPage page = context.getEngine().getPage( pageName );
if( page != null )
{
Collection links = refmgr.findReferrers( page.getName() );
String wikitext = "";
super.initialize( context, params );
int items = TextUtil.parseIntParameter( (String)params.get(
PARAM_MAX ), ALL_ITEMS );
String extras = (String)params.get( PARAM_EXTRAS );
if( extras == null )
{
extras = rb.getString("referringpagesplugin.more");
}
String aTitle = (String)params.get( PARAM_TITLE );
if( aTitle == null ) aTitle = "";
wikitext = "";
log.debug( "Fetching referring pages for "+page.getName()+
" with a max of "+items);
if( links != null && links.size() > 0 ) // Existing links?
{
if (extras.indexOf("%m") >= 0) m_lastModified = true;
links = filterCollection( links );
if ( links != null && links.size() > 0 ) { // Still existing
links after filtering
if (aTitle.length() > 0) { // Title specified
wikitext = aTitle;
wikitext = TextUtil.replaceString( wikitext, "%d",
""+(links.size()-items) );
wikitext = TextUtil.replaceString( wikitext, "%m",
""+(df.format(m_dateLastModified)) );
wikitext = TextUtil.replaceString( wikitext, "%n","\n\n" );
Object[] args = { "" + ( links.size() - items),
context.getURL( WikiContext.INFO,
page.getName() ) };
wikitext = MessageFormat.format(wikitext, args);
}
wikitext += wikitizeCollection( links, m_separator, items );
// if( items < links.size() && items > 0 )
if( (extras.length() > 0) // "more" may have to be printed
&& (items < links.size()) // Less links printed than
existing links
&& (items >= 0) ) // Maximum number of items is not
infinite
{
extras = TextUtil.replaceString( extras, "%d",
""+(links.size()-items) );
extras = TextUtil.replaceString( extras, "%m",
""+(df.format(m_dateLastModified)) );
extras = TextUtil.replaceString( extras, "%n","\n\n" );
Object[] args = { "" + ( links.size() - items),
context.getURL( WikiContext.INFO,
page.getName() ) };
extras = MessageFormat.format(extras, args);
wikitext += extras;
}
else extras = ""; // no "more" printed
}
else extras = ""; // no "more" printed
}
else extras = ""; // no "more" printed
//
// If nothing was left after filtering or during search
//
if( (extras.length() == 0) // No "more" printed
&& (aTitle.length() == 0) // No title specified so no title
automatically hidden!
&& (links == null || links.size() == 0) )
{
wikitext += rb.getString("referringpagesplugin.nobody");
}
return makeHTML( context, wikitext );
}
return "";
}
}
/*
JSPWiki - a JSP-based WikiWiki clone.
Copyright (C) 2001-2005 Janne Jalkanen ([EMAIL PROTECTED])
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.ecyrd.jspwiki.plugin;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.apache.oro.text.GlobCompiler;
import org.apache.oro.text.regex.*;
import com.ecyrd.jspwiki.*;
import com.ecyrd.jspwiki.parser.MarkupParser;
import com.ecyrd.jspwiki.parser.WikiDocument;
import com.ecyrd.jspwiki.render.RenderingManager;
import java.util.Date;
import java.text.SimpleDateFormat;
/**
* This is a base class for all plugins using referral things.
*
* <p>Parameters:<br>
* maxwidth: maximum width of generated links<br>
* separator: separator between generated links (wikitext)<br>
* after: output after the link
* before: output before the link
* @author Janne Jalkanen
*/
public abstract class AbstractReferralPlugin
implements WikiPlugin
{
private static Logger log = Logger.getLogger( AbstractReferralPlugin.class
);
public static final int ALL_ITEMS = -1;
public static final String PARAM_MAXWIDTH = "maxwidth";
public static final String PARAM_SEPARATOR = "separator";
public static final String PARAM_AFTER = "after";
public static final String PARAM_BEFORE = "before";
public static final String PARAM_EXCLUDE = "exclude";
public static final String PARAM_INCLUDE = "include";
protected int m_maxwidth = Integer.MAX_VALUE;
protected String m_before = ""; // null not blank
protected String m_separator = ""; // null not blank
protected String m_after = "\\\\";
protected Pattern[] m_exclude;
protected Pattern[] m_include;
protected boolean m_lastModified=false;
// the last modified date of the page that has been last modified:
protected Date m_dateLastModified = new Date(0);
protected SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd
HH:mm:ss");
protected WikiEngine m_engine;
/**
* Used to initialize some things. All plugins must call this first.
*
* @since 1.6.4
*/
// FIXME: The compiled pattern strings should really be cached somehow.
public void initialize( WikiContext context, Map params )
throws PluginException
{
m_engine = context.getEngine();
m_maxwidth = TextUtil.parseIntParameter( (String)params.get(
PARAM_MAXWIDTH ), Integer.MAX_VALUE );
if( m_maxwidth < 0 ) m_maxwidth = 0;
String s = (String) params.get( PARAM_SEPARATOR );
if( s != null )
{
m_separator = s;
// pre-2.1.145 there was a separator at the end of the list
// if they set the parameters, we use the new format of
// before Item1 after separator before Item2 after separator before
Item3 after
m_after = "";
}
s = (String) params.get( PARAM_BEFORE );
if( s != null )
{
m_before = s;
}
s = (String) params.get( PARAM_AFTER );
if( s != null )
{
m_after = s;
}
s = (String) params.get( PARAM_EXCLUDE );
if( s != null )
{
try
{
PatternCompiler pc = new GlobCompiler();
String[] ptrns = StringUtils.split( s, "," );
m_exclude = new Pattern[ptrns.length];
for( int i = 0; i < ptrns.length; i++ )
{
m_exclude[i] = pc.compile( ptrns[i] );
}
}
catch( MalformedPatternException e )
{
throw new PluginException("Exclude-parameter has a malformed
pattern: "+e.getMessage());
}
}
// TODO: Cut-n-paste, refactor
s = (String) params.get( PARAM_INCLUDE );
if( s != null )
{
try
{
PatternCompiler pc = new GlobCompiler();
String[] ptrns = StringUtils.split( s, "," );
m_include = new Pattern[ptrns.length];
for( int i = 0; i < ptrns.length; i++ )
{
m_include[i] = pc.compile( ptrns[i] );
}
}
catch( MalformedPatternException e )
{
throw new PluginException("Include-parameter has a malformed
pattern: "+e.getMessage());
}
}
// log.debug( "Requested maximum width is "+m_maxwidth );
}
protected Collection filterCollection( Collection c )
{
ArrayList result = new ArrayList();
PatternMatcher pm = new Perl5Matcher();
for( Iterator i = c.iterator(); i.hasNext(); )
{
String pageName = (String) i.next();
//
// If include parameter exists, then by default we include only
those
// pages in it (excluding the ones in the exclude pattern list).
//
// include='*' means the same as no include.
//
boolean includeThis = m_include == null;
if( m_include != null )
{
for( int j = 0; j < m_include.length; j++ )
{
if( pm.matches( pageName, m_include[j] ) )
{
includeThis = true;
break;
}
}
}
if( m_exclude != null )
{
for( int j = 0; j < m_exclude.length; j++ )
{
if( pm.matches( pageName, m_exclude[j] ) )
{
includeThis = false;
break; // The inner loop, continue on the next item
}
}
}
if( includeThis )
{
if (result.add( pageName )) {
// if we want to show the last modified date of the most
recently change page, we keep a "high watermark" here:
if (m_lastModified) {
WikiPage page = m_engine.getPage(pageName);
if (page!= null)
{
Date lastModPage = page.getLastModified();
if (lastModPage.after(m_dateLastModified)){
m_dateLastModified=lastModPage;
}
}
}
}
}
}
return result;
}
/**
* Makes WikiText from a Collection.
*
* @param links Collection to make into WikiText.
* @param separator Separator string to use.
* @param numItems How many items to show.
*/
protected String wikitizeCollection( Collection links, String separator,
int numItems )
{
if( links == null || links.isEmpty() )
return "";
StringBuffer output = new StringBuffer();
Iterator it = links.iterator();
int count = 0;
//
// The output will be B Item[1] A S B Item[2] A S B Item[3] A
//
while( it.hasNext() && ( (count < numItems) || ( numItems == ALL_ITEMS
) ) )
{
String value = (String)it.next();
output.append( ( count > 0 ? m_separator : "")
+ TextUtil.replaceString(TextUtil.replaceString(
m_before, "%n","\n\n" ), "%p", "'"+value+"'" )
// Make a Wiki markup link. See TranslatorReader.
+ "[" + m_engine.beautifyTitle(value) + "|" + value
+ "]"
+ TextUtil.replaceString(TextUtil.replaceString(
m_after, "%n","\n\n" ), "%p", "'"+value+"'") );
count++;
}
return output.toString();
}
/**
* Makes HTML with common parameters.
*
* @since 1.6.4
*/
protected String makeHTML( WikiContext context, String wikitext )
{
String result = "";
RenderingManager mgr = m_engine.getRenderingManager();
try
{
MarkupParser parser = mgr.getParser(context, wikitext);
parser.addLinkTransmutator( new CutMutator(m_maxwidth) );
parser.enableImageInlining( false );
WikiDocument doc = parser.parse();
result = mgr.getHTML( context, doc );
}
catch( IOException e )
{
log.error("Failed to convert page data to HTML", e);
}
return result;
}
/**
* A simple class that just cuts a String to a maximum
* length, adding three dots after the cutpoint.
*/
private static class CutMutator implements StringTransmutator
{
private int m_length;
public CutMutator( int length )
{
m_length = length;
}
public String mutate( WikiContext context, String text )
{
if( text.length() > m_length )
{
return text.substring( 0, m_length ) + "...";
}
return text;
}
}
}