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;
        }
    }
}

Reply via email to