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}