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