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.rss;
020
021import org.apache.wiki.api.core.Context;
022import org.apache.wiki.api.core.ContextEnum;
023import org.apache.wiki.api.core.Engine;
024import org.apache.wiki.api.core.Page;
025import org.apache.wiki.util.XhtmlUtil;
026import org.jdom2.Element;
027import org.jdom2.Namespace;
028
029import java.text.SimpleDateFormat;
030import java.util.Calendar;
031
032
033/**
034 * Provides an implementation of an RSS 1.0 feed.  In addition, this class is
035 * capable of adding RSS 1.0 Wiki Extensions to the Feed, as defined in
036 * <A HREF="http://usemod.com/cgi-bin/mb.pl?ModWiki">UseMod:ModWiki</A>.
037 */
038public class RSS10Feed extends Feed {
039
040    private static final Namespace NS_XMNLS = Namespace.getNamespace( "http://purl.org/rss/1.0/" );
041    private static final Namespace NS_RDF = Namespace.getNamespace( "rdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#" );
042    private static final Namespace NS_DC = Namespace.getNamespace( "dc", "http://purl.org/dc/elements/1.1/" );
043    private static final Namespace NS_WIKI = Namespace.getNamespace( "wiki", "http://purl.org/rss/1.0/modules/wiki/" );
044
045    /**
046     * Create an RSS 1.0 feed for a given context.
047     *
048     * @param context The WikiContext.
049     */
050    public RSS10Feed( final Context context ) {
051        super( context );
052    }
053
054    private Element getRDFItems() {
055        final Element items = new Element( "items", NS_XMNLS );
056        final Element rdfseq = new Element( "Seq", NS_RDF );
057
058        for( final Entry e : m_entries ) {
059            final String url = e.getURL();
060            rdfseq.addContent( new Element( "li", NS_RDF ).setAttribute( "resource", url, NS_RDF ) );
061        }
062        items.addContent( rdfseq );
063
064        return items;
065    }
066
067    private void addItemList( final Element root ) {
068        final SimpleDateFormat iso8601fmt = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
069        final Engine engine = m_wikiContext.getEngine();
070
071        for( final Entry entry : m_entries ) {
072            final String url = entry.getURL();
073
074            final Element item = new Element( "item", NS_XMNLS );
075            item.setAttribute( "about", url, NS_RDF );
076            item.addContent( new Element( "title", NS_XMNLS ).addContent( entry.getTitle() ) );
077            //FIXME this needs to be an absolute url, not a relative one
078            item.addContent( new Element( "link", NS_XMNLS ).addContent( url ) );
079
080            final Element content = new Element( "description", NS_XMNLS );
081            // TODO: Add a size limiter here
082            content.addContent( entry.getContent() );
083            item.addContent( content );
084
085            final Page p = entry.getPage();
086            if( p.getVersion() != -1 ) {
087                item.addContent( new Element( "version", NS_WIKI ).addContent( Integer.toString( p.getVersion() ) ) );
088            }
089
090            if( p.getVersion() > 1 ) {
091                item.addContent( new Element( "diff", NS_WIKI )
092                                         .addContent( engine.getURL( ContextEnum.PAGE_DIFF.getRequestContext(), p.getName(), "r1=-1" ) ) );
093            }
094
095            //
096            //  Modification date.
097            final Calendar cal = Calendar.getInstance();
098            cal.setTime(p.getLastModified());
099            cal.add( Calendar.MILLISECOND,
100                    - ( cal.get( Calendar.ZONE_OFFSET ) +
101                             ( cal.getTimeZone().inDaylightTime( p.getLastModified() ) ? cal.get( Calendar.DST_OFFSET )
102                                                                                       : 0 ) ) );
103
104            item.addContent( new Element( "date", NS_DC ).addContent( iso8601fmt.format( cal.getTime() ) ) );
105
106            //
107            //  Author
108            String author = entry.getAuthor();
109            if( author == null ) {
110                author = "unknown";
111            }
112
113            final Element contributor = new Element( "creator", NS_DC );
114            item.addContent( contributor );
115
116            /*
117            Element description = new Element("Description", NS_RDF);
118            if( m_wikiContext.getEngine().pageExists(author) ) {
119                description.setAttribute( "link", engine.getURL( WikiContext.VIEW, author, null, true ), NS_XMNLS );
120            }
121
122            description.addContent( new Element("value", NS_XMNLS).addContent( author) );
123            contributor.addContent( description );
124           */
125
126            // Not too many aggregators seem to like this.  Therefore we're just adding the name here.
127            contributor.addContent( author );
128
129            //
130            //  PageHistory
131            item.addContent( new Element( "history", NS_WIKI )
132                                    .addContent( engine.getURL( ContextEnum.PAGE_INFO.getRequestContext(), p.getName(), null ) ) );
133
134            //
135            // Add to root
136            root.addContent( item );
137        }
138    }
139
140    private Element getChannelElement() {
141        final Element channel = new Element( "channel", NS_XMNLS );
142        channel.setAttribute( "about", m_feedURL, NS_RDF )
143               .addContent( new Element( "link", NS_XMNLS ).addContent( m_feedURL ) );
144
145        if( m_channelTitle != null ) {
146            channel.addContent( new Element( "title", NS_XMNLS ).addContent( m_channelTitle ) );
147        }
148
149        if( m_channelDescription != null ) {
150            channel.addContent( new Element( "description", NS_XMNLS ).addContent( m_channelDescription ) );
151        }
152
153        if( m_channelLanguage != null ) {
154            channel.addContent( new Element( "language", NS_DC ).addContent( m_channelLanguage ) );
155        }
156        channel.addContent( getRDFItems() );
157
158        return channel;
159    }
160
161    /**
162     * {@inheritDoc}
163     */
164    @Override
165    public String getString() {
166        final Element root = new Element( "RDF", NS_RDF );
167        root.addContent( getChannelElement() );
168        root.addNamespaceDeclaration( NS_XMNLS );
169        root.addNamespaceDeclaration( NS_RDF );
170        root.addNamespaceDeclaration( NS_DC );
171        root.addNamespaceDeclaration( NS_WIKI );
172        addItemList( root );
173
174        return XhtmlUtil.serialize( root, true );
175    }
176
177}