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.commons.lang3.time.DateFormatUtils;
022import org.apache.wiki.api.Release;
023import org.apache.wiki.api.core.Attachment;
024import org.apache.wiki.api.core.Context;
025import org.apache.wiki.api.core.ContextEnum;
026import org.apache.wiki.api.core.Engine;
027import org.apache.wiki.api.core.Page;
028import org.apache.wiki.api.exceptions.ProviderException;
029import org.apache.wiki.attachment.AttachmentManager;
030import org.jdom2.Element;
031import org.jdom2.Namespace;
032import org.jdom2.output.Format;
033import org.jdom2.output.XMLOutputter;
034
035import javax.servlet.ServletContext;
036import java.io.IOException;
037import java.io.StringWriter;
038import java.util.ArrayList;
039import java.util.Collection;
040import java.util.Date;
041import java.util.List;
042
043/**
044 *  Provides an Atom 1.0 standard feed, with enclosures.
045 *
046 */
047public class AtomFeed extends Feed {
048
049    private final Namespace m_atomNameSpace = Namespace.getNamespace("http://www.w3.org/2005/Atom");
050
051    /** Defines a SimpleDateFormat string for RFC3339-formatted dates. */
052    public static final String RFC3339FORMAT = "yyyy-MM-dd'T'HH:mm:ssZZ";
053
054    /**
055     *  Create a new AtomFeed for a given WikiContext.
056     *  
057     *  @param c A WikiContext.
058     */
059    public AtomFeed( final Context c )
060    {
061        super(c);
062    }
063
064    /**
065     *   This is a bit complicated right now, as there is no proper metadata store in JSPWiki.
066     *
067     *   @return An unique feed ID.
068     */
069    private String getFeedID()
070    {
071        return m_wikiContext.getEngine().getBaseURL(); // FIXME: This is not a feed id
072    }
073
074    private String getEntryID( final Entry e )
075    {
076        return e.getURL(); // FIXME: Not really a feed id!
077    }
078
079    private Collection<Element> getItems() {
080        final ArrayList< Element > list = new ArrayList<>();
081        final Engine engine = m_wikiContext.getEngine();
082        ServletContext servletContext = null;
083        if( m_wikiContext.getHttpRequest() != null ) {
084            servletContext = m_wikiContext.getHttpRequest().getSession().getServletContext();
085        }
086
087        for( final Entry e : m_entries ) {
088            final Page p = e.getPage();
089            final Element entryEl = getElement( "entry" );
090
091            //  Mandatory elements
092            entryEl.addContent( getElement( "id" ).setText( getEntryID( e ) ) );
093            entryEl.addContent( getElement( "title" ).setAttribute( "type", "html" ).setText( e.getTitle() ) );
094            entryEl.addContent( getElement( "updated" ).setText( DateFormatUtils.formatUTC( p.getLastModified(), RFC3339FORMAT ) ) );
095
096            //  Optional elements
097            entryEl.addContent( getElement( "author" ).addContent( getElement( "name" ).setText( e.getAuthor() ) ) );
098            entryEl.addContent( getElement( "link" ).setAttribute( "rel", "alternate" ).setAttribute( "href", e.getURL() ) );
099            entryEl.addContent( getElement( "content" ).setAttribute( "type", "html" ).setText( e.getContent() ) );
100
101            //  Check for enclosures
102            if( engine.getManager( AttachmentManager.class ).hasAttachments( p ) && servletContext != null ) {
103                try {
104                    final List< Attachment > c = engine.getManager( AttachmentManager.class ).listAttachments( p );
105                    for( final Attachment att : c ) {
106                        final Element attEl = getElement( "link" );
107                        attEl.setAttribute( "rel", "enclosure" );
108                        attEl.setAttribute( "href", engine.getURL( ContextEnum.PAGE_ATTACH.getRequestContext(), att.getName(), null ) );
109                        attEl.setAttribute( "length", Long.toString( att.getSize() ) );
110                        attEl.setAttribute( "type", getMimeType( servletContext, att.getFileName() ) );
111
112                        entryEl.addContent( attEl );
113                    }
114                } catch( final ProviderException ex ) {
115                    // FIXME: log.info("Can't get attachment data",ex);
116                }
117            }
118
119            list.add( entryEl );
120        }
121
122        return list;
123    }
124
125    /**
126     *  {@inheritDoc}
127     */
128    @Override
129    public String getString() {
130        final Element root = getElement("feed");
131        final Engine engine = m_wikiContext.getEngine();
132
133        Date lastModified = new Date(0L);
134
135        for( final Entry e : m_entries ) {
136            if( e.getPage().getLastModified().after( lastModified ) ) {
137                lastModified = e.getPage().getLastModified();
138            }
139        }
140
141        //  Mandatory parts
142        root.addContent( getElement( "title" ).setText( getChannelTitle() ) );
143        root.addContent( getElement( "id" ).setText( getFeedID() ) );
144        root.addContent( getElement( "updated" ).setText( DateFormatUtils.formatUTC( lastModified, RFC3339FORMAT ) ) );
145
146        //  Optional
147        // root.addContent( getElement("author").addContent(getElement("name").setText(format())))
148        root.addContent( getElement( "link" ).setAttribute( "href", engine.getBaseURL() ) );
149        root.addContent( getElement( "generator" ).setText( "JSPWiki " + Release.VERSTR ) );
150
151        final String rssFeedURL  = engine.getURL( ContextEnum.PAGE_NONE.getRequestContext(), "rss.jsp",
152                                                 "page=" + engine.encodeName( m_wikiContext.getPage().getName() ) +
153                                                 "&mode=" + m_mode +
154                                                 "&type=atom" );
155        final Element self = getElement( "link" ).setAttribute( "rel","self" );
156        self.setAttribute( "href", rssFeedURL );
157        root.addContent( self );
158
159        //  Items
160        root.addContent( getItems() );
161
162        //  aaand output
163        final XMLOutputter output = new XMLOutputter();
164        output.setFormat( Format.getPrettyFormat() );
165
166        try {
167            final StringWriter res = new StringWriter();
168            output.output( root, res );
169
170            return res.toString();
171        } catch( final IOException e ) {
172            return null;
173        }
174    }
175
176    private Element getElement( final String name ) {
177        return new Element( name, m_atomNameSpace );
178    }
179
180}