blob: 8498e1c26bc33bd75bd324716feca07b36251bf5 [file] [log] [blame]
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
package org.apache.wiki.rss;
import org.apache.log4j.Logger;
import org.apache.wiki.WikiContext;
import org.apache.wiki.api.core.Attachment;
import org.apache.wiki.api.core.Context;
import org.apache.wiki.api.core.Engine;
import org.apache.wiki.api.core.Page;
import org.apache.wiki.api.core.Session;
import org.apache.wiki.api.providers.WikiProvider;
import org.apache.wiki.api.spi.Wiki;
import org.apache.wiki.auth.AuthorizationManager;
import org.apache.wiki.auth.permissions.PagePermission;
import org.apache.wiki.diff.DifferenceManager;
import org.apache.wiki.pages.PageManager;
import org.apache.wiki.pages.PageTimeComparator;
import org.apache.wiki.render.RenderingManager;
import org.apache.wiki.util.TextUtil;
import org.apache.wiki.variables.VariableManager;
import java.io.File;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.Set;
/**
* Default implementation for {@link RSSGenerator}.
*
* {@inheritDoc}
*/
// FIXME: Limit diff and page content size.
public class DefaultRSSGenerator implements RSSGenerator {
private static final Logger log = Logger.getLogger( DefaultRSSGenerator.class );
private Engine m_engine;
/** The RSS file to generate. */
private String m_rssFile;
private String m_channelDescription = "";
private String m_channelLanguage = "en-us";
private boolean m_enabled = true;
private static final int MAX_CHARACTERS = Integer.MAX_VALUE-1;
/**
* Builds the RSS generator for a given Engine.
*
* @param engine The Engine.
* @param properties The properties.
*/
public DefaultRSSGenerator( final Engine engine, final Properties properties ) {
m_engine = engine;
m_channelDescription = properties.getProperty( PROP_CHANNEL_DESCRIPTION, m_channelDescription );
m_channelLanguage = properties.getProperty( PROP_CHANNEL_LANGUAGE, m_channelLanguage );
m_rssFile = TextUtil.getStringProperty( properties, DefaultRSSGenerator.PROP_RSSFILE, "rss.rdf" );
}
/**
* {@inheritDoc}
*
* Start the RSS generator & generator thread
*/
@Override
public void initialize( final Engine engine, final Properties properties ) {
final File rssFile;
if( m_rssFile.startsWith( File.separator ) ) { // honor absolute pathnames
rssFile = new File( m_rssFile );
} else { // relative path names are anchored from the webapp root path
rssFile = new File( engine.getRootPath(), m_rssFile );
}
final int rssInterval = TextUtil.getIntegerProperty( properties, DefaultRSSGenerator.PROP_INTERVAL, 3600 );
final RSSThread rssThread = new RSSThread( engine, rssFile, rssInterval );
rssThread.start();
}
private String getAuthor( final Page page ) {
String author = page.getAuthor();
if( author == null ) {
author = "An unknown author";
}
return author;
}
private String getAttachmentDescription( final Attachment att ) {
final String author = getAuthor( att );
final StringBuilder sb = new StringBuilder();
if( att.getVersion() != 1 ) {
sb.append( author ).append( " uploaded a new version of this attachment on " ).append( att.getLastModified() );
} else {
sb.append( author ).append( " created this attachment on " ).append( att.getLastModified() );
}
sb.append( "<br /><hr /><br />" )
.append( "Parent page: <a href=\"" )
.append( m_engine.getURL( WikiContext.VIEW, att.getParentName(), null ) )
.append( "\">" ).append( att.getParentName() ).append( "</a><br />" )
.append( "Info page: <a href=\"" )
.append( m_engine.getURL( WikiContext.INFO, att.getName(), null ) )
.append( "\">" ).append( att.getName() ).append( "</a>" );
return sb.toString();
}
private String getPageDescription( final Page page ) {
final StringBuilder buf = new StringBuilder();
final String author = getAuthor( page );
final Context ctx = Wiki.context().create( m_engine, page );
if( page.getVersion() > 1 ) {
final String diff = m_engine.getManager( DifferenceManager.class ).getDiff( ctx,
page.getVersion() - 1, // FIXME: Will fail when non-contiguous versions
page.getVersion() );
buf.append( author ).append( " changed this page on " ).append( page.getLastModified() ).append( ":<br /><hr /><br />" );
buf.append( diff );
} else {
buf.append( author ).append( " created this page on " ).append( page.getLastModified() ).append( ":<br /><hr /><br />" );
buf.append( m_engine.getManager( RenderingManager.class ).getHTML( page.getName() ) );
}
return buf.toString();
}
private String getEntryDescription( final Page page ) {
final String res;
if( page instanceof Attachment ) {
res = getAttachmentDescription( (Attachment)page );
} else {
res = getPageDescription( page );
}
return res;
}
// FIXME: This should probably return something more intelligent
private String getEntryTitle( final Page page ) {
return page.getName() + ", version " + page.getVersion();
}
/** {@inheritDoc} */
@Override
public String generate() {
final Context context = Wiki.context().create( m_engine, Wiki.contents().page( m_engine, "__DUMMY" ) );
context.setRequestContext( WikiContext.RSS );
final Feed feed = new RSS10Feed( context );
return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + generateFullWikiRSS( context, feed );
}
/** {@inheritDoc} */
@Override
public String generateFeed( final Context wikiContext, final List< Page > changed, final String mode, final String type ) throws IllegalArgumentException {
final Feed feed;
final String res;
if( ATOM.equals(type) ) {
feed = new AtomFeed( wikiContext );
} else if( RSS20.equals( type ) ) {
feed = new RSS20Feed( wikiContext );
} else {
feed = new RSS10Feed( wikiContext );
}
feed.setMode( mode );
if( MODE_BLOG.equals( mode ) ) {
res = generateBlogRSS( wikiContext, changed, feed );
} else if( MODE_FULL.equals(mode) ) {
res = generateFullWikiRSS( wikiContext, feed );
} else if( MODE_WIKI.equals(mode) ) {
res = generateWikiPageRSS( wikiContext, changed, feed );
} else {
throw new IllegalArgumentException( "Invalid value for feed mode: "+mode );
}
return res;
}
/** {@inheritDoc} */
@Override
public boolean isEnabled() {
return m_enabled;
}
/** {@inheritDoc} */
@Override
public synchronized void setEnabled( final boolean enabled ) {
m_enabled = enabled;
}
/** {@inheritDoc} */
@Override
public String getRssFile() {
return m_rssFile;
}
/** {@inheritDoc} */
@Override
public String generateFullWikiRSS( final Context wikiContext, final Feed feed ) {
feed.setChannelTitle( m_engine.getApplicationName() );
feed.setFeedURL( m_engine.getBaseURL() );
feed.setChannelLanguage( m_channelLanguage );
feed.setChannelDescription( m_channelDescription );
final Set< Page > changed = m_engine.getManager( PageManager.class ).getRecentChanges();
final Session session = Wiki.session().guest( m_engine );
int items = 0;
for( final Iterator< Page > i = changed.iterator(); i.hasNext() && items < 15; items++ ) {
final Page page = i.next();
// Check if the anonymous user has view access to this page.
if( !m_engine.getManager( AuthorizationManager.class ).checkPermission(session, new PagePermission(page,PagePermission.VIEW_ACTION) ) ) {
// No permission, skip to the next one.
continue;
}
final String url;
if( page instanceof Attachment ) {
url = m_engine.getURL( WikiContext.ATTACH, page.getName(),null );
} else {
url = m_engine.getURL( WikiContext.VIEW, page.getName(), null );
}
final Entry e = new Entry();
e.setPage( page );
e.setURL( url );
e.setTitle( page.getName() );
e.setContent( getEntryDescription(page) );
e.setAuthor( getAuthor(page) );
feed.addEntry( e );
}
return feed.getString();
}
/** {@inheritDoc} */
@Override
public String generateWikiPageRSS( final Context wikiContext, final List< Page > changed, final Feed feed ) {
feed.setChannelTitle( m_engine.getApplicationName()+": "+wikiContext.getPage().getName() );
feed.setFeedURL( wikiContext.getViewURL( wikiContext.getPage().getName() ) );
final String language = m_engine.getManager( VariableManager.class ).getVariable( wikiContext, PROP_CHANNEL_LANGUAGE );
if( language != null ) {
feed.setChannelLanguage( language );
} else {
feed.setChannelLanguage( m_channelLanguage );
}
final String channelDescription = m_engine.getManager( VariableManager.class ).getVariable( wikiContext, PROP_CHANNEL_DESCRIPTION );
if( channelDescription != null ) {
feed.setChannelDescription( channelDescription );
}
changed.sort( new PageTimeComparator() );
int items = 0;
for( final Iterator< Page > i = changed.iterator(); i.hasNext() && items < 15; items++ ) {
final Page page = i.next();
final Entry e = new Entry();
e.setPage( page );
String url;
if( page instanceof Attachment ) {
url = m_engine.getURL( WikiContext.ATTACH, page.getName(), "version=" + page.getVersion() );
} else {
url = m_engine.getURL( WikiContext.VIEW, page.getName(), "version=" + page.getVersion() );
}
// Unfortunately, this is needed because the code will again go through replacement conversion
url = TextUtil.replaceString( url, "&amp;", "&" );
e.setURL( url );
e.setTitle( getEntryTitle(page) );
e.setContent( getEntryDescription(page) );
e.setAuthor( getAuthor(page) );
feed.addEntry( e );
}
return feed.getString();
}
/** {@inheritDoc} */
@Override
public String generateBlogRSS( final Context wikiContext, final List< Page > changed, final Feed feed ) {
if( log.isDebugEnabled() ) {
log.debug( "Generating RSS for blog, size=" + changed.size() );
}
final String ctitle = m_engine.getManager( VariableManager.class ).getVariable( wikiContext, PROP_CHANNEL_TITLE );
if( ctitle != null ) {
feed.setChannelTitle( ctitle );
} else {
feed.setChannelTitle( m_engine.getApplicationName() + ":" + wikiContext.getPage().getName() );
}
feed.setFeedURL( wikiContext.getViewURL( wikiContext.getPage().getName() ) );
final String language = m_engine.getManager( VariableManager.class ).getVariable( wikiContext, PROP_CHANNEL_LANGUAGE );
if( language != null ) {
feed.setChannelLanguage( language );
} else {
feed.setChannelLanguage( m_channelLanguage );
}
final String channelDescription = m_engine.getManager( VariableManager.class ).getVariable( wikiContext, PROP_CHANNEL_DESCRIPTION );
if( channelDescription != null ) {
feed.setChannelDescription( channelDescription );
}
changed.sort( new PageTimeComparator() );
int items = 0;
for( final Iterator< Page > i = changed.iterator(); i.hasNext() && items < 15; items++ ) {
final Page page = i.next();
final Entry e = new Entry();
e.setPage( page );
final String url;
if( page instanceof Attachment ) {
url = m_engine.getURL( WikiContext.ATTACH, page.getName(),null );
} else {
url = m_engine.getURL( WikiContext.VIEW, page.getName(),null );
}
e.setURL( url );
// Title
String pageText = m_engine.getManager( PageManager.class ).getPureText( page.getName(), WikiProvider.LATEST_VERSION );
String title = "";
final int firstLine = pageText.indexOf('\n');
if( firstLine > 0 ) {
title = pageText.substring( 0, firstLine ).trim();
}
if( title.length() == 0 ) {
title = page.getName();
}
// Remove wiki formatting
while( title.startsWith("!") ) {
title = title.substring(1);
}
e.setTitle( title );
// Description
if( firstLine > 0 ) {
int maxlen = pageText.length();
if( maxlen > MAX_CHARACTERS ) {
maxlen = MAX_CHARACTERS;
}
pageText = m_engine.getManager( RenderingManager.class ).textToHTML( wikiContext, pageText.substring( firstLine + 1, maxlen ).trim() );
if( maxlen == MAX_CHARACTERS ) {
pageText += "...";
}
e.setContent( pageText );
} else {
e.setContent( title );
}
e.setAuthor( getAuthor(page) );
feed.addEntry( e );
}
return feed.getString();
}
}