    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}