[seam-commits] Seam SVN: r7186 - in trunk/examples/wiki: src/etc/META-INF and 12 other directories.

seam-commits at lists.jboss.org seam-commits at lists.jboss.org
Tue Jan 22 10:17:34 EST 2008


Author: christian.bauer at jboss.com
Date: 2008-01-22 10:17:34 -0500 (Tue, 22 Jan 2008)
New Revision: 7186

Added:
   trunk/examples/wiki/src/main/org/jboss/seam/wiki/connectors/feed/FeedAggregateCache.java
Modified:
   trunk/examples/wiki/src/etc/META-INF/wiki.taglib.xml
   trunk/examples/wiki/src/etc/WEB-INF/urlrewrite.xml
   trunk/examples/wiki/src/etc/messages_en.properties
   trunk/examples/wiki/src/etc/messages_feedAggregator_en.properties
   trunk/examples/wiki/src/etc/messages_lastModifiedDocuments_en.properties
   trunk/examples/wiki/src/main/org/jboss/seam/wiki/connectors/cache/ConnectorCache.java
   trunk/examples/wiki/src/main/org/jboss/seam/wiki/connectors/feed/FeedAggregatorDAO.java
   trunk/examples/wiki/src/main/org/jboss/seam/wiki/connectors/feed/RomeFeedConnector.java
   trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/model/FeedEntry.java
   trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/model/WikiFeed.java
   trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/ui/FeedServlet.java
   trunk/examples/wiki/src/main/org/jboss/seam/wiki/plugin/feedAggregator/FeedAggregator.java
   trunk/examples/wiki/src/main/org/jboss/seam/wiki/plugin/feedAggregator/FeedAggregatorPreferences.java
   trunk/examples/wiki/src/main/org/jboss/seam/wiki/util/WikiUtil.java
   trunk/examples/wiki/src/test/org/jboss/seam/wiki/test/WikiBaseData.dbunit.xml
   trunk/examples/wiki/src/test/org/jboss/seam/wiki/test/connector/FeedConnectorTest.java
   trunk/examples/wiki/view/plugins/feedAggregator/plugin.xhtml
   trunk/examples/wiki/view/themes/default/css/feedAggregator.css
   trunk/examples/wiki/view/themes/sfwkorg/css/feedAggregator.css
   trunk/examples/wiki/view/themes/sfwkorg/css/sfwk.css
Log:
Implemented subscription for aggregated feeds

Modified: trunk/examples/wiki/src/etc/META-INF/wiki.taglib.xml
===================================================================
--- trunk/examples/wiki/src/etc/META-INF/wiki.taglib.xml	2008-01-22 15:16:57 UTC (rev 7185)
+++ trunk/examples/wiki/src/etc/META-INF/wiki.taglib.xml	2008-01-22 15:17:34 UTC (rev 7186)
@@ -13,6 +13,12 @@
     </function>
 
     <function>
+        <function-name>renderAggregateFeedURL</function-name>
+        <function-class>org.jboss.seam.wiki.util.WikiUtil</function-class>
+        <function-signature>java.lang.String renderAggregateFeedURL(java.lang.String)</function-signature>
+    </function>
+
+    <function>
         <function-name>renderFeedURL</function-name>
         <function-class>org.jboss.seam.wiki.util.WikiUtil</function-class>
         <function-signature>java.lang.String renderFeedURL(org.jboss.seam.wiki.core.model.Feed,java.lang.String,java.lang.String)</function-signature>

Modified: trunk/examples/wiki/src/etc/WEB-INF/urlrewrite.xml
===================================================================
--- trunk/examples/wiki/src/etc/WEB-INF/urlrewrite.xml	2008-01-22 15:16:57 UTC (rev 7185)
+++ trunk/examples/wiki/src/etc/WEB-INF/urlrewrite.xml	2008-01-22 15:17:34 UTC (rev 7186)
@@ -32,10 +32,10 @@
         <to last="true">/wiki.seam?nodeId=$1</to>
      </rule>
 
-    <!-- /service/Feed/atom (/Foo) (/Bar) /Feed/atom (/Comments/exclude/) (Tag/foobar) -->
+    <!-- /service/Feed/atom (/Area/Foo) (/Node/Bar) (/Comments/exclude/) (/Tag/foobar) (/Aggregate/My Aggregate) -->
     <rule>
-        <from casesensitive="true">^/service/Feed/atom(?:/([A-Z0-9]+[A-Za-z0-9]*))?(?:/([A-Z0-9]+[A-Za-z0-9]*))?(?:/Comments/([a-z]+))?(?:/Tag/(.+))?$</from>
-        <to last="true">/servlets/feeds/atom.seam?areaName=$1&amp;nodeName=$2&amp;comments=$3&amp;tag=$4</to>
+        <from casesensitive="true">^/service/Feed/atom(?:/Area/([A-Z0-9]+[A-Za-z0-9]*))?(?:/Node/([A-Z0-9]+[A-Za-z0-9]*))?(?:/Comments/([a-z]+))?(?:/Tag/(.+))?(?:/Aggregate/(.+))?$</from>
+        <to last="true">/servlets/feeds/atom.seam?areaName=$1&amp;nodeName=$2&amp;comments=$3&amp;tag=$4&amp;aggregate=$5</to>
     </rule>
 
     <!-- /service/File/123 -->

Modified: trunk/examples/wiki/src/etc/messages_en.properties
===================================================================
--- trunk/examples/wiki/src/etc/messages_en.properties	2008-01-22 15:16:57 UTC (rev 7185)
+++ trunk/examples/wiki/src/etc/messages_en.properties	2008-01-22 15:17:34 UTC (rev 7186)
@@ -640,6 +640,7 @@
 lacewiki.msg.AccessDenied=Access Denied
 lacewiki.msg.FatalError=An unrecoverable error occured!
 lacewiki.msg.Trash.Emptied=All items in the trash have been permanently deleted.
+lacewiki.msg.AutomaticallyGeneratedFeed=Aggregated Feed
 
 # Preferences Editors
 lacewiki.preferences.editor.SelectNone=(None)

Modified: trunk/examples/wiki/src/etc/messages_feedAggregator_en.properties
===================================================================
--- trunk/examples/wiki/src/etc/messages_feedAggregator_en.properties	2008-01-22 15:16:57 UTC (rev 7185)
+++ trunk/examples/wiki/src/etc/messages_feedAggregator_en.properties	2008-01-22 15:17:34 UTC (rev 7186)
@@ -8,7 +8,9 @@
 feedAggregator.preferences.HideFeedInfo=Hide link to feed
 feedAggregator.preferences.HideDescription=Hide feed entry text
 feedAggregator.preferences.HideTitle=Hide feed entry title
+feedAggregator.preferences.AggregateId=Identifier for aggregate (enables subscription)
 
 feedAggregator.label.More=more...
 feedAggregator.label.By=by
+feedAggregator.label.Subscribe=Subscribe
 feedAggregator.label.NoEntriesFound=No feed entries found.
\ No newline at end of file

Modified: trunk/examples/wiki/src/etc/messages_lastModifiedDocuments_en.properties
===================================================================
--- trunk/examples/wiki/src/etc/messages_lastModifiedDocuments_en.properties	2008-01-22 15:16:57 UTC (rev 7185)
+++ trunk/examples/wiki/src/etc/messages_lastModifiedDocuments_en.properties	2008-01-22 15:17:34 UTC (rev 7186)
@@ -4,4 +4,4 @@
 lastModifiedDocuments.preferences.DocumentTitleLength=Truncate document titles after characters
 
 lastModifiedDocuments.label.Last=Last
-lastModifiedDocuments.label.ModifiedDocuments=Updates
+lastModifiedDocuments.label.ModifiedDocuments=Site Updates

Modified: trunk/examples/wiki/src/main/org/jboss/seam/wiki/connectors/cache/ConnectorCache.java
===================================================================
--- trunk/examples/wiki/src/main/org/jboss/seam/wiki/connectors/cache/ConnectorCache.java	2008-01-22 15:16:57 UTC (rev 7185)
+++ trunk/examples/wiki/src/main/org/jboss/seam/wiki/connectors/cache/ConnectorCache.java	2008-01-22 15:17:34 UTC (rev 7186)
@@ -38,12 +38,12 @@
         List<T> result = Collections.EMPTY_LIST;
 
         if (cacheKey == null) {
-            log.debug("cache miss, retrieving it from connector, asynchronously: " + isCacheMissResolvedAsynchronously());
+            log.debug("cache miss, retrieving it from connector, asynchronously: " + isFirstCacheMissResolvedAsynchronously());
 
             // The following operations modify the structure of the cache map, which is ok because this
             // method is synchronized by Seam. The write triggered (later) by the AysncUpdater is not
             // synchronized, but we never modify the structure of the cache map then, only now.
-            if (isCacheMissResolvedAsynchronously()) {
+            if (getAsyncUpdater() != null && isFirstCacheMissResolvedAsynchronously()) {
 
                 // Write an empty list into the cache
                 write(key, Collections.EMPTY_LIST, currentTime);
@@ -59,22 +59,26 @@
             }
 
         } else {
-            log.debug("cache hit, checking age of cached entry");
+            log.debug("cache hit");
 
-            // Check updateTimestamp of cached entry
-            if (currentTime - cacheKey.getUpdateTimestamp() > (getUpdateTimeoutSeconds()*1000) ) {
-                log.debug("cached entry is older than maximum cache time, refreshing...");
+            if (getUpdateTimeoutSeconds() != 0) {
 
-                // Start asynchronous updating, might take a while - but should never take longer than cache timeout!
-                getAsyncUpdater().updateCacheAsynchronously(this, cacheKey);
+                log.debug("checking age of cached entry against update timeout");
+                // Check updateTimestamp of cached entry
+                if (currentTime - cacheKey.getUpdateTimestamp() > (getUpdateTimeoutSeconds()*1000) ) {
+                    log.debug("cached entry is older than maximum cache time, refreshing...");
 
-                // Meanwhile, update the timestamp so that the next caller doesn't also start asynchronous updating
-                // .. we expect to be finished with that before the next caller runs into a cache timeout again!
-                cacheKey.setAccessTimestamp(currentTime);
-                cacheKey.setUpdateTimestamp(currentTime);
+                    // Start asynchronous updating, might take a while - but should never take longer than cache timeout!
+                    getAsyncUpdater().updateCacheAsynchronously(this, cacheKey);
 
-            } else {
-                log.debug("cached entry is still inside maximum cache time");
+                    // Meanwhile, update the timestamp so that the next caller doesn't also start asynchronous updating
+                    // .. we expect to be finished with that before the next caller runs into a cache timeout again!
+                    cacheKey.setAccessTimestamp(currentTime);
+                    cacheKey.setUpdateTimestamp(currentTime);
+
+                } else {
+                    log.debug("cached entry is still inside maximum cache time");
+                }
             }
 
             // Read the value from the cache
@@ -116,17 +120,17 @@
         return (ConnectorCacheAsyncUpdater<T, K>) Component.getInstance(getAsyncUpdaterClass());
     };
 
-    private ConnectorCacheKey<K> findKey(ConnectorCacheKey<K> key) {
+    protected ConnectorCacheKey<K> findKey(ConnectorCacheKey<K> key) {
         for (ConnectorCacheKey keyOfMap : cache.keySet()) {
             if (keyOfMap.equals(key)) return keyOfMap;
         }
         return null;
     }
 
-    protected abstract long getUpdateTimeoutSeconds();
+    protected long getUpdateTimeoutSeconds() { return 0; }
     protected abstract long getIdleTimeoutSeconds();
-    protected abstract Class<? extends ConnectorCacheAsyncUpdater<T, K>> getAsyncUpdaterClass();
-    protected boolean isCacheMissResolvedAsynchronously() { return true; }
+    protected Class<? extends ConnectorCacheAsyncUpdater<T, K>> getAsyncUpdaterClass() { return null; };
+    protected boolean isFirstCacheMissResolvedAsynchronously() { return true; }
     protected List<T> udpateCacheSynchronously(ConnectorCache<T, K> cache, ConnectorCacheKey<K> key) {
         return Collections.EMPTY_LIST;
     };

Added: trunk/examples/wiki/src/main/org/jboss/seam/wiki/connectors/feed/FeedAggregateCache.java
===================================================================
--- trunk/examples/wiki/src/main/org/jboss/seam/wiki/connectors/feed/FeedAggregateCache.java	                        (rev 0)
+++ trunk/examples/wiki/src/main/org/jboss/seam/wiki/connectors/feed/FeedAggregateCache.java	2008-01-22 15:17:34 UTC (rev 7186)
@@ -0,0 +1,88 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package org.jboss.seam.wiki.connectors.feed;
+
+import org.jboss.seam.annotations.Scope;
+import org.jboss.seam.annotations.Synchronized;
+import org.jboss.seam.annotations.Name;
+import org.jboss.seam.annotations.AutoCreate;
+import org.jboss.seam.ScopeType;
+import org.jboss.seam.wiki.connectors.cache.ConnectorCache;
+import org.jboss.seam.wiki.connectors.cache.ConnectorCacheKey;
+import org.jboss.seam.wiki.core.model.Feed;
+import org.jboss.seam.wiki.core.model.FeedEntry;
+
+import java.net.URL;
+import java.util.List;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Collections;
+import java.io.Serializable;
+
+/**
+ * Caches transient feeds.
+ *
+ * TODO: Maybe we should use a simple synchronized collection instead, but Seam does the job.
+ *
+ * @author Christian Bauer
+ */
+ at Name("feedAggregateCache")
+ at Scope(ScopeType.APPLICATION)
+ at Synchronized
+ at AutoCreate
+public class FeedAggregateCache extends ConnectorCache<FeedEntryDTO, FeedAggregateCache.FeedAggregateCacheKey> {
+
+    public void put(String aggregateId, List<FeedEntryDTO> feedEntries) {
+        long currentTime = System.currentTimeMillis();
+        FeedAggregateCacheKey newKey = new FeedAggregateCacheKey(aggregateId);
+
+        write(new ConnectorCacheKey<FeedAggregateCacheKey>(newKey), feedEntries, currentTime);
+        purge(currentTime);
+    }
+
+    public List<FeedEntryDTO> get(String aggregateId) {
+        long currentTime = System.currentTimeMillis();
+        FeedAggregateCacheKey newKey = new FeedAggregateCacheKey(aggregateId);
+
+        List<FeedEntryDTO> result = read(new ConnectorCacheKey<FeedAggregateCacheKey>(newKey), currentTime);
+        purge(currentTime);
+        return result;
+    }
+
+    protected long getIdleTimeoutSeconds() {
+        return 36000;
+    }
+
+    public static class FeedAggregateCacheKey implements Serializable {
+        private String aggregateId;
+
+        public FeedAggregateCacheKey(String aggregateId) {
+            this.aggregateId = aggregateId;
+        }
+
+        public String getAggregateId() {
+            return aggregateId;
+        }
+
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+
+            FeedAggregateCacheKey that = (FeedAggregateCacheKey) o;
+
+            if (!aggregateId.equals(that.aggregateId)) return false;
+
+            return true;
+        }
+
+        public int hashCode() {
+            return aggregateId.hashCode();
+        }
+    }
+
+
+}

Modified: trunk/examples/wiki/src/main/org/jboss/seam/wiki/connectors/feed/FeedAggregatorDAO.java
===================================================================
--- trunk/examples/wiki/src/main/org/jboss/seam/wiki/connectors/feed/FeedAggregatorDAO.java	2008-01-22 15:16:57 UTC (rev 7185)
+++ trunk/examples/wiki/src/main/org/jboss/seam/wiki/connectors/feed/FeedAggregatorDAO.java	2008-01-22 15:17:34 UTC (rev 7186)
@@ -7,11 +7,13 @@
 package org.jboss.seam.wiki.connectors.feed;
 
 import org.jboss.seam.ScopeType;
+import org.jboss.seam.wiki.core.model.Feed;
 import org.jboss.seam.annotations.*;
 import org.jboss.seam.log.Log;
 
 import java.util.*;
 import java.io.Serializable;
+import java.net.URL;
 
 /**
  * @author Christian Bauer
@@ -27,16 +29,23 @@
     @In("feedConnectorCache")
     FeedConnector feedConnector;
 
-    public List<FeedEntryDTO> getLatestFeedEntries(int numberOfFeedEntries, String[] feedURLs) {
+    @In
+    FeedAggregateCache feedAggregateCache;
+
+    public List<FeedEntryDTO> getLatestFeedEntries(int numberOfFeedEntries, URL[] feedURLs) {
+        return getLatestFeedEntries(numberOfFeedEntries, feedURLs, null);
+    }
+
+    public List<FeedEntryDTO> getLatestFeedEntries(int numberOfFeedEntries, URL[] feedURLs, String aggregateId) {
         if (feedURLs == null) return Collections.EMPTY_LIST;
 
         List<FeedEntryDTO> feedEntries = new ArrayList<FeedEntryDTO>();
 
-        for (String feedURL : feedURLs) {
+        for (URL feedURL : feedURLs) {
             // For each feed, get the feed entries and put them in a sorted collection,
             // so we get overall sorting
             log.debug("retrieving feed entries from connector for feed URL: " + feedURL);
-            List<FeedEntryDTO> result = feedConnector.getFeedEntries(feedURL);
+            List<FeedEntryDTO> result = feedConnector.getFeedEntries(feedURL.toString());
             log.debug("retrieved feed entries: " + result.size());
             feedEntries.addAll(result);
             log.debug("number of aggregated feed entries so far: " + feedEntries.size());
@@ -56,6 +65,12 @@
             }
         );
 
+        if (aggregateId != null) {
+            log.debug("caching aggregated feed entries under id: " + aggregateId);
+            // Cache the result for later requests through FeedServlet (by aggregateId)
+            feedAggregateCache.put(aggregateId, feedEntries);
+        }
+
         return feedEntries.size() > numberOfFeedEntries
                 ? new ArrayList<FeedEntryDTO>(feedEntries).subList(0, numberOfFeedEntries)
                 : new ArrayList<FeedEntryDTO>(feedEntries);

Modified: trunk/examples/wiki/src/main/org/jboss/seam/wiki/connectors/feed/RomeFeedConnector.java
===================================================================
--- trunk/examples/wiki/src/main/org/jboss/seam/wiki/connectors/feed/RomeFeedConnector.java	2008-01-22 15:16:57 UTC (rev 7185)
+++ trunk/examples/wiki/src/main/org/jboss/seam/wiki/connectors/feed/RomeFeedConnector.java	2008-01-22 15:17:34 UTC (rev 7186)
@@ -73,7 +73,11 @@
                 SyndEntry syndEntry = (SyndEntry)o;
                 FeedEntry fe = convertSyndEntry(syndEntry);
 
-                // Linking it in our model makes it persistable
+                // Append some information on the title
+                fe.setTitlePrefix("(" + feed.getTitle() + ") ");
+                //fe.setTitleSuffix(" (" + fe.getAuthor() + ")");
+
+                // Linking it in our model makes it persistable/cachable
                 feed.getFeedEntries().add(fe);
 
                 // Now project them so the client has a unified view without iterating collections of Feeds
@@ -121,7 +125,8 @@
         if (syndEntry.getDescription() != null) {
             SyndContent description = syndEntry.getDescription();
 
-            feedEntry.setDescriptionType(description.getType());
+            // TODO: Hardcode 'html', otherwise the ROME stuff craps out and kills Firefox feed renderer...
+            feedEntry.setDescriptionType("html");
             feedEntry.setDescriptionValue(description.getValue());
         }
 

Modified: trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/model/FeedEntry.java
===================================================================
--- trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/model/FeedEntry.java	2008-01-22 15:16:57 UTC (rev 7185)
+++ trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/model/FeedEntry.java	2008-01-22 15:17:34 UTC (rev 7186)
@@ -42,6 +42,12 @@
     @Length(min = 3, max = 1024)
     private String title;
 
+    @Transient
+    private String titlePrefix = "";
+
+    @Transient
+    private String titleSuffix = "";
+
     @Column(name = "AUTHOR", nullable = false)
     @Length(min = 3, max = 255)
     private String author;
@@ -81,6 +87,22 @@
         return title;
     }
 
+    public String getTitlePrefix() {
+        return titlePrefix;
+    }
+
+    public void setTitlePrefix(String titlePrefix) {
+        this.titlePrefix = titlePrefix;
+    }
+
+    public String getTitleSuffix() {
+        return titleSuffix;
+    }
+
+    public void setTitleSuffix(String titleSuffix) {
+        this.titleSuffix = titleSuffix;
+    }
+
     public String getTitleStripped() {
         return stripHTMLTags(title);
     }

Modified: trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/model/WikiFeed.java
===================================================================
--- trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/model/WikiFeed.java	2008-01-22 15:16:57 UTC (rev 7185)
+++ trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/model/WikiFeed.java	2008-01-22 15:17:34 UTC (rev 7186)
@@ -39,6 +39,13 @@
     }
 
     public String getURL() {
-        return directory.getWikiURL().length() > 0 ?  "/"+directory.getWikiURL() : "";
+        if (directory.getWikiURL().length() >0) {
+            if (directory.getArea().getWikiname().equals(directory.getWikiname())) {
+                return "/Area/"+directory.getArea().getWikiname();
+            } else {
+                return "/Area/"+directory.getArea().getWikiname() + "/Node/" + directory.getWikiname();
+            }
+        }
+        return ""; // Root, no area or node
     }
 }

Modified: trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/ui/FeedServlet.java
===================================================================
--- trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/ui/FeedServlet.java	2008-01-22 15:16:57 UTC (rev 7185)
+++ trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/ui/FeedServlet.java	2008-01-22 15:17:34 UTC (rev 7186)
@@ -18,6 +18,8 @@
 import org.jboss.seam.wiki.core.dao.WikiNodeFactory;
 import org.jboss.seam.wiki.util.WikiUtil;
 import org.jboss.seam.wiki.preferences.Preferences;
+import org.jboss.seam.wiki.connectors.feed.FeedAggregateCache;
+import org.jboss.seam.wiki.connectors.feed.FeedEntryDTO;
 import org.apache.commons.logging.LogFactory;
 import org.apache.commons.logging.Log;
 
@@ -79,17 +81,8 @@
         String feedIdParam = request.getParameter("feedId");
         String areaNameParam = request.getParameter("areaName");
         String nodeNameParam = request.getParameter("nodeName");
+        String aggregateParam = request.getParameter("aggregate");
         log.debug(">>> feed request id: '" + feedIdParam + "' area name: '" + areaNameParam + "' node name: '" + nodeNameParam + "'");
-        log.debug("full request URL: " + request.getRequestURL().toString());
-        if (log.isDebugEnabled()) {
-            Map<String,String[]> params = (Map<String,String[]>)request.getParameterMap();
-            for (Map.Entry<String, String[]> entry : params.entrySet()) {
-                log.debug("request param: " + entry.getKey());
-                for (String s : entry.getValue()) {
-                    log.debug("value: '" +s + "'");
-                }
-            }
-        }
 
         // Feed type
         String pathInfo = request.getPathInfo();
@@ -120,6 +113,7 @@
         }
 
         // TODO: Seam should use its transaction interceptor for java beans: http://jira.jboss.com/jira/browse/JBSEAM-957
+        // and that would allow us to break up this gigantic if/then/else clause easily...
         UserTransaction userTx = null;
         boolean startedTx = false;
         try {
@@ -132,8 +126,26 @@
             Feed feed = null;
 
             // Find the feed, depending on variations of request parameters
-            if (feedIdParam != null && feedIdParam.length() >0) {
+            if (aggregateParam != null && aggregateParam.length() > 0) {
+
+                log.debug("trying to retrieve aggregated feed from cache: " + aggregateParam);
+
+                FeedAggregateCache aggregateCache = (FeedAggregateCache)Component.getInstance(FeedAggregateCache.class);
+                List<FeedEntryDTO> result = aggregateCache.get(aggregateParam);
+                if (result != null) {
+                    feed = new Feed();
+                    feed.setAuthor(Messages.instance().get("lacewiki.msg.AutomaticallyGeneratedFeed"));
+                    feed.setTitle(Messages.instance().get("lacewiki.msg.AutomaticallyGeneratedFeed") + ": " + aggregateParam);
+                    feed.setPublishedDate(new Date());
+                    feed.setLink( ((WikiPreferences) Preferences.getInstance("Wiki")).getBaseUrl() );
+                    for (FeedEntryDTO feedEntryDTO : result) {
+                        feed.getFeedEntries().add(feedEntryDTO.getFeedEntry());
+                    }
+                }
+
+            } else if (feedIdParam != null && feedIdParam.length() >0) {
                 try {
+
                     log.debug("trying to retrieve feed for id: " + feedIdParam);
                     Long feedId = Long.valueOf(feedIdParam);
                     FeedDAO feedDAO = (FeedDAO)Component.getInstance(FeedDAO.class);
@@ -142,6 +154,8 @@
                     log.debug("feed identifier couldn't be converted to java.lang.Long");
                     response.sendError(HttpServletResponse.SC_NOT_FOUND, "Feed " + feedIdParam);
                 }
+
+
             } else if (areaNameParam != null && areaNameParam.matches("^[A-Z0-9]+.*")) {
                 log.debug("trying to retrieve area: " + areaNameParam);
                 WikiNodeDAO nodeDAO = (WikiNodeDAO)Component.getInstance(WikiNodeDAO.class);
@@ -189,8 +203,18 @@
                 }
             }
 
+            // TODO: Refactor this mess a little
             log.debug("finally rendering feed");
-            SyndFeed syndFeed = createSyndFeed(request.getRequestURL().toString(), syndFeedType,  feed, currentAccessLevel, tag, comments);
+            SyndFeed syndFeed =
+                    createSyndFeed(
+                        request.getRequestURL().toString(),
+                        syndFeedType,
+                        feed,
+                        currentAccessLevel,
+                        tag,
+                        comments,
+                        aggregateParam
+                    );
 
             // Write feed to output
             response.setContentType(syndFeedType.contentType);
@@ -214,16 +238,26 @@
     }
 
     public SyndFeed createSyndFeed(String baseURI, SyndFeedType syndFeedType, Feed feed, Integer currentAccessLevel) {
-        return createSyndFeed(baseURI, syndFeedType, feed, currentAccessLevel, null, Comments.include);
+        return createSyndFeed(baseURI, syndFeedType, feed, currentAccessLevel, null, Comments.include, null);
     }
 
-    public SyndFeed createSyndFeed(String baseURI, SyndFeedType syndFeedType, Feed feed, Integer currentAccessLevel, String tag, Comments comments) {
+    public SyndFeed createSyndFeed(String baseURI,
+                                   SyndFeedType syndFeedType,
+                                   Feed feed,
+                                   Integer currentAccessLevel,
+                                   String tag,
+                                   Comments comments,
+                                   String aggregateParam) {
 
         WikiPreferences prefs = (WikiPreferences) Preferences.getInstance("Wiki");
 
         // Create feed
         SyndFeed syndFeed = new SyndFeedImpl();
-        syndFeed.setUri(baseURI + "?feedId=" + feed.getId());
+        String feedUri =
+                feed.getId() != null
+                    ? "?feedId="+feed.getId()
+                    : "?aggregate="+WikiUtil.encodeURL(aggregateParam);
+        syndFeed.setUri(baseURI + feedUri);
         syndFeed.setFeedType(syndFeedType.feedType);
         syndFeed.setTitle(prefs.getFeedTitlePrefix() + feed.getTitle());
         if (tag != null) {
@@ -251,7 +285,7 @@
 
             SyndEntry syndEntry;
             syndEntry = new SyndEntryImpl();
-            syndEntry.setTitle(entry.getTitle());
+            syndEntry.setTitle(entry.getTitlePrefix() + entry.getTitle() + entry.getTitleSuffix());
             syndEntry.setLink(entry.getLink());
             syndEntry.setUri(entry.getLink());
             syndEntry.setAuthor(entry.getAuthor());

Modified: trunk/examples/wiki/src/main/org/jboss/seam/wiki/plugin/feedAggregator/FeedAggregator.java
===================================================================
--- trunk/examples/wiki/src/main/org/jboss/seam/wiki/plugin/feedAggregator/FeedAggregator.java	2008-01-22 15:16:57 UTC (rev 7185)
+++ trunk/examples/wiki/src/main/org/jboss/seam/wiki/plugin/feedAggregator/FeedAggregator.java	2008-01-22 15:17:34 UTC (rev 7186)
@@ -6,17 +6,17 @@
  */
 package org.jboss.seam.wiki.plugin.feedAggregator;
 
+import org.jboss.seam.ScopeType;
 import org.jboss.seam.annotations.*;
-import org.jboss.seam.ScopeType;
 import org.jboss.seam.log.Log;
 import org.jboss.seam.wiki.connectors.feed.FeedAggregatorDAO;
 import org.jboss.seam.wiki.connectors.feed.FeedEntryDTO;
 
+import java.io.Serializable;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
 import java.util.List;
-import java.util.ArrayList;
-import java.net.URL;
-import java.net.MalformedURLException;
-import java.io.Serializable;
 
 /**
  * @author Christian Bauer
@@ -46,31 +46,47 @@
 
         if (prefs.getUrls() == null || prefs.getUrls().length() < 8) return;
 
+        List<URL> validURLs = getValidURLs(prefs.getUrls());
+        log.debug("aggregating feeds: " + validURLs.size());
+
+        String aggregateId =
+                prefs.getAggregateId() != null && prefs.getAggregateId().length() > 0
+                    ? prefs.getAggregateId()
+                    : null;
+
+        if (aggregateId != null) {
+            log.debug("aggregating under subscribable identifier: "+ aggregateId);
+        }
+
+        feedEntries =
+            feedAggregatorDAO.getLatestFeedEntries(
+                prefs.getNumberOfFeedEntries().intValue(),
+                validURLs.toArray(new URL[validURLs.size()]),
+                aggregateId
+            );
+    }
+
+    private List<URL> getValidURLs(String spaceSeparatedURLs) {
+
         // Split the URLs by space
-        String[] urls = prefs.getUrls().split(" ");
+        String[] urls = spaceSeparatedURLs.split(" ");
 
         // First check if the URLs are valid, if not we might as well just skip it...
-        List<String> validUrls = new ArrayList<String>();
+        List<URL> validUrls = new ArrayList<URL>();
         for (String url : urls) {
             try {
-                URL testUrl = new URL(url);
-                if (!testUrl.getProtocol().equals("http")) {
+                URL u = new URL(url);
+                if (!u.getProtocol().equals("http")) {
                     log.debug("skipping URL with unsupported protocol: " + url);
                     continue;
                 }
+                validUrls.add(u);
             } catch (MalformedURLException e) {
                 log.debug("skipping invalid URL: " + url);
                 continue;
             }
-            validUrls.add(url);
         }
-
-        log.debug("aggregating feeds: " + validUrls.size());
-        feedEntries =
-            feedAggregatorDAO.getLatestFeedEntries(
-                prefs.getNumberOfFeedEntries().intValue(),
-                validUrls.toArray(new String[validUrls.size()])
-            );
+        return validUrls;
     }
 
 }

Modified: trunk/examples/wiki/src/main/org/jboss/seam/wiki/plugin/feedAggregator/FeedAggregatorPreferences.java
===================================================================
--- trunk/examples/wiki/src/main/org/jboss/seam/wiki/plugin/feedAggregator/FeedAggregatorPreferences.java	2008-01-22 15:16:57 UTC (rev 7185)
+++ trunk/examples/wiki/src/main/org/jboss/seam/wiki/plugin/feedAggregator/FeedAggregatorPreferences.java	2008-01-22 15:17:34 UTC (rev 7186)
@@ -55,6 +55,14 @@
     private Long truncateDescription;
 
     @PreferenceProperty(
+        description = "#{messages['feedAggregator.preferences.AggregateId']}",
+        visibility = {PreferenceVisibility.INSTANCE},
+        editorIncludeName = "NumberRange"
+    )
+    @Length(min = 0, max = 255)
+    private String aggregateId;
+
+    @PreferenceProperty(
         description = "#{messages['feedAggregator.preferences.HideDate']}",
         visibility = {PreferenceVisibility.INSTANCE}
     )
@@ -100,6 +108,10 @@
         return truncateDescription;
     }
 
+    public String getAggregateId() {
+        return aggregateId;
+    }
+
     public Boolean getHideDate() {
         return hideDate;
     }

Modified: trunk/examples/wiki/src/main/org/jboss/seam/wiki/util/WikiUtil.java
===================================================================
--- trunk/examples/wiki/src/main/org/jboss/seam/wiki/util/WikiUtil.java	2008-01-22 15:16:57 UTC (rev 7185)
+++ trunk/examples/wiki/src/main/org/jboss/seam/wiki/util/WikiUtil.java	2008-01-22 15:17:34 UTC (rev 7186)
@@ -23,6 +23,7 @@
 import java.io.IOException;
 import java.math.BigDecimal;
 import java.net.URLEncoder;
+import java.net.URLDecoder;
 import java.text.SimpleDateFormat;
 import java.util.Collection;
 import java.util.Collections;
@@ -96,6 +97,15 @@
                 accessLevels.indexOf(new Role.AccessLevel(accessLevel, null))
                );
     }
+    
+    public static String renderAggregateFeedURL(String aggregateId) {
+        if (aggregateId == null) return "";
+        StringBuilder url = new StringBuilder();
+        url.append(Component.getInstance("basePath"))
+            .append("/service/Feed/atom/Aggregate/")
+            .append(aggregateId);
+        return url.toString();
+    }
 
     public static String renderFeedURL(Feed feed, String tag, String comments) {
         if (feed == null || feed.getId() == null) return "";
@@ -158,6 +168,14 @@
         }
     }
 
+    public static String decodeURL(String string) {
+        try {
+            return URLDecoder.decode(string, "UTF-8");
+        } catch (Exception ex) {
+            throw new RuntimeException(ex);
+        }
+    }
+
     public static String escapeEmailURL(String string) {
         WikiPreferences wikiPrefs = (WikiPreferences) Preferences.getInstance("Wiki");
         return string.length() >= 7 && string.substring(0, 7).equals("mailto:")

Modified: trunk/examples/wiki/src/test/org/jboss/seam/wiki/test/WikiBaseData.dbunit.xml
===================================================================
--- trunk/examples/wiki/src/test/org/jboss/seam/wiki/test/WikiBaseData.dbunit.xml	2008-01-22 15:16:57 UTC (rev 7185)
+++ trunk/examples/wiki/src/test/org/jboss/seam/wiki/test/WikiBaseData.dbunit.xml	2008-01-22 15:17:34 UTC (rev 7186)
@@ -262,7 +262,7 @@
     <WIKI_FILE
         NODE_ID="8" FILE_REVISION="0"/>
     <WIKI_DOCUMENT
-        NODE_ID="8" CONTENT="Testdocument Three&#xA;&#xA;&lt;div class=&quot;homepageNews&quot;>&#xA;[&lt;=feedAggregator[truncateDescription=250][numberOfFeedEntries=10][urls=http://rss.slashdot.org/Slashdot/slashdot http://feeds.engadget.com/weblogsinc/engadget]]&#xA;&lt;/div>&#xA;"
+        NODE_ID="8" CONTENT="Testdocument Three&#xA;&#xA;&lt;div class=&quot;homepageNews&quot;>&#xA;[&lt;=feedAggregator[truncateDescription=250][numberOfFeedEntries=10][aggregateId=My Aggregated Feed][urls=http://rss.slashdot.org/Slashdot/slashdot http://feeds.engadget.com/weblogsinc/engadget]]&#xA;&lt;/div>&#xA;"
         HEADER="[NULL]" FOOTER="[NULL]"
         HEADER_MACROS="[NULL]" CONTENT_MACROS="feedAggregator" FOOTER_MACROS="[NULL]"
         NAME_AS_TITLE="true" ENABLE_COMMENTS="true" ENABLE_COMMENT_FORM="true" ENABLE_COMMENTS_ON_FEEDS="true"/>

Modified: trunk/examples/wiki/src/test/org/jboss/seam/wiki/test/connector/FeedConnectorTest.java
===================================================================
--- trunk/examples/wiki/src/test/org/jboss/seam/wiki/test/connector/FeedConnectorTest.java	2008-01-22 15:16:57 UTC (rev 7185)
+++ trunk/examples/wiki/src/test/org/jboss/seam/wiki/test/connector/FeedConnectorTest.java	2008-01-22 15:17:34 UTC (rev 7186)
@@ -23,6 +23,7 @@
 import java.util.List;
 import java.util.ArrayList;
 import java.util.Date;
+import java.net.URL;
 
 /**
  * @author Christian Bauer
@@ -42,8 +43,8 @@
             protected void renderResponse() throws Exception {
                 FeedAggregatorDAO dao = (FeedAggregatorDAO)getInstance(FeedAggregatorDAO.class);
 
-                String[] feedURLs = {
-                    "http://foo", "http://bar", "http:WrongURL"
+                URL[] feedURLs = {
+                    new URL("http://foo"), new URL("http://bar")
                 };
 
                 List<FeedEntryDTO> dtos = dao.getLatestFeedEntries(30, feedURLs);

Modified: trunk/examples/wiki/view/plugins/feedAggregator/plugin.xhtml
===================================================================
--- trunk/examples/wiki/view/plugins/feedAggregator/plugin.xhtml	2008-01-22 15:16:57 UTC (rev 7185)
+++ trunk/examples/wiki/view/plugins/feedAggregator/plugin.xhtml	2008-01-22 15:17:34 UTC (rev 7186)
@@ -9,10 +9,29 @@
 
     <s:div styleClass="feedAggregator box">
 
-        <s:div styleClass="boxHeader">
+
+        <s:div styleClass="boxHeader" rendered="#{empty preferences.get('FeedAggregator', currentMacro).aggregateId}">
             <h:outputText value="#{preferences.get('FeedAggregator', currentMacro).title}"/>
         </s:div>
+    
+        <h:panelGrid rendered="#{not empty preferences.get('FeedAggregator', currentMacro).aggregateId}"
+                     columns="2" cellpadding="0" cellspacing="0" border="0"
+                     columnClasses="feedAggregatorSubscribe, feedAggregatorTitle"
+                     styleClass="boxHeader fullWidth">
 
+            <h:panelGrid columns="2" cellpadding="0" cellspacing="0" border="0">
+                <h:outputLink value="#{wiki:renderAggregateFeedURL(preferences.get('FeedAggregator', currentMacro).aggregateId)}">
+                    <h:graphicImage value="/themes/#{preferences.get('Wiki').themeName}/img/icon.atom.ongrey.gif" width="18" height="18" alt="Atom"/>
+                </h:outputLink>
+                <h:outputLink value="#{wiki:renderAggregateFeedURL(preferences.get('FeedAggregator', currentMacro).aggregateId)}">
+                    <h:outputText value="#{messages['feedAggregator.label.Subscribe']}"/>
+                </h:outputLink>
+            </h:panelGrid>
+
+            <h:outputText value="#{preferences.get('FeedAggregator', currentMacro).title}"/>
+
+        </h:panelGrid>
+
         <div class="boxContent">
             <h:dataTable value="#{feedAggregator.feedEntries}" var="feDTO"
                          rendered="#{not empty feedAggregator.feedEntries and feedAggregator.feedEntries.size() > 0}"

Modified: trunk/examples/wiki/view/themes/default/css/feedAggregator.css
===================================================================
--- trunk/examples/wiki/view/themes/default/css/feedAggregator.css	2008-01-22 15:16:57 UTC (rev 7185)
+++ trunk/examples/wiki/view/themes/default/css/feedAggregator.css	2008-01-22 15:17:34 UTC (rev 7186)
@@ -1,3 +1,11 @@
+.feedAggregatorTitle {
+    text-align: right;
+}
+
+.feedAggregatorSubscribe {
+    text-align: left;
+}
+
 .feedEntryColumn {
     padding: 10px;
 }

Modified: trunk/examples/wiki/view/themes/sfwkorg/css/feedAggregator.css
===================================================================
--- trunk/examples/wiki/view/themes/sfwkorg/css/feedAggregator.css	2008-01-22 15:16:57 UTC (rev 7185)
+++ trunk/examples/wiki/view/themes/sfwkorg/css/feedAggregator.css	2008-01-22 15:17:34 UTC (rev 7186)
@@ -1,3 +1,11 @@
+.feedAggregatorTitle {
+    text-align: right;
+}
+
+.feedAggregatorSubscribe {
+    text-align: left;
+}
+
 .feedEntryColumn {
     padding: 10px;
 }
@@ -13,4 +21,4 @@
 
 .feedEntryBody {
     line-height: 130%;
-}
\ No newline at end of file
+}

Modified: trunk/examples/wiki/view/themes/sfwkorg/css/sfwk.css
===================================================================
--- trunk/examples/wiki/view/themes/sfwkorg/css/sfwk.css	2008-01-22 15:16:57 UTC (rev 7185)
+++ trunk/examples/wiki/view/themes/sfwkorg/css/sfwk.css	2008-01-22 15:17:34 UTC (rev 7186)
@@ -280,6 +280,7 @@
     border-bottom: 1px solid #b6b6b6;
     padding-left: 0;
     padding-bottom: 5px;
+    text-align: right;
 }
 
 .homepageNews .boxContent {
@@ -294,6 +295,10 @@
     padding-left: 0;
 }
 
+.homepageNews .feedAggregatorSubscribe {
+    font-size: 110%;
+}
+
 .homepageNews .feedEntryColumn {
     padding: 0;
 }




More information about the seam-commits mailing list