1. Project Clover database Tue Dec 20 2016 21:24:09 CET
  2. Package com.xpn.xwiki.plugin.feed

File FeedPlugin.java

 

Coverage histogram

../../../../../img/srcFileCovDistChart0.png
83% of files have more coverage

Code metrics

198
479
57
3
1,142
914
179
0.37
8.4
19
3.14

Classes

Class Line # Actions
FeedPlugin 72 463 0% 169 704
0.00%
FeedPlugin.SyndEntryComparator 87 7 0% 5 14
0.00%
FeedPlugin.EntriesComparator 106 9 0% 5 16
0.00%
 

Contributing tests

This file is covered by 1 test. .

Source view

1    /*
2    * See the NOTICE file distributed with this work for additional
3    * information regarding copyright ownership.
4    *
5    * This is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU Lesser General Public License as
7    * published by the Free Software Foundation; either version 2.1 of
8    * the License, or (at your option) any later version.
9    *
10    * This software is distributed in the hope that it will be useful,
11    * but WITHOUT ANY WARRANTY; without even the implied warranty of
12    * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13    * Lesser General Public License for more details.
14    *
15    * You should have received a copy of the GNU Lesser General Public
16    * License along with this software; if not, write to the Free
17    * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
18    * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
19    */
20    package com.xpn.xwiki.plugin.feed;
21   
22    import java.io.IOException;
23    import java.io.StringReader;
24    import java.io.StringWriter;
25    import java.lang.reflect.Constructor;
26    import java.net.URL;
27    import java.util.ArrayList;
28    import java.util.Collection;
29    import java.util.Collections;
30    import java.util.Comparator;
31    import java.util.Date;
32    import java.util.HashMap;
33    import java.util.List;
34    import java.util.Map;
35    import java.util.Vector;
36   
37    import org.apache.commons.lang3.StringUtils;
38    import org.slf4j.Logger;
39    import org.slf4j.LoggerFactory;
40    import org.xwiki.cache.Cache;
41    import org.xwiki.cache.CacheException;
42    import org.xwiki.cache.CacheManager;
43    import org.xwiki.cache.config.CacheConfiguration;
44    import org.xwiki.cache.eviction.LRUEvictionConfiguration;
45    import org.xwiki.rendering.converter.ConversionException;
46    import org.xwiki.rendering.converter.Converter;
47    import org.xwiki.rendering.renderer.printer.DefaultWikiPrinter;
48    import org.xwiki.rendering.renderer.printer.WikiPrinter;
49    import org.xwiki.rendering.syntax.Syntax;
50   
51    import com.sun.syndication.feed.synd.SyndCategory;
52    import com.sun.syndication.feed.synd.SyndContent;
53    import com.sun.syndication.feed.synd.SyndEntry;
54    import com.sun.syndication.feed.synd.SyndEntryImpl;
55    import com.sun.syndication.feed.synd.SyndFeed;
56    import com.sun.syndication.feed.synd.SyndFeedImpl;
57    import com.sun.syndication.feed.synd.SyndImage;
58    import com.sun.syndication.feed.synd.SyndImageImpl;
59    import com.sun.syndication.io.SyndFeedOutput;
60    import com.xpn.xwiki.XWiki;
61    import com.xpn.xwiki.XWikiContext;
62    import com.xpn.xwiki.XWikiException;
63    import com.xpn.xwiki.api.Api;
64    import com.xpn.xwiki.doc.XWikiDocument;
65    import com.xpn.xwiki.objects.BaseObject;
66    import com.xpn.xwiki.objects.classes.BaseClass;
67    import com.xpn.xwiki.plugin.XWikiDefaultPlugin;
68    import com.xpn.xwiki.plugin.XWikiPluginInterface;
69    import com.xpn.xwiki.user.api.XWikiRightService;
70    import com.xpn.xwiki.web.Utils;
71   
 
72    public class FeedPlugin extends XWikiDefaultPlugin implements XWikiPluginInterface
73    {
74    private Cache<SyndFeed> feedCache;
75   
76    private int refreshPeriod;
77   
78    private Map<String, UpdateThread> updateThreads = new HashMap<String, UpdateThread>();
79   
80    private Converter syntaxConverter;
81   
82    /**
83    * Log object to log messages in this class.
84    */
85    private static final Logger LOGGER = LoggerFactory.getLogger(FeedPlugin.class);
86   
 
87    public static class SyndEntryComparator implements Comparator<SyndEntry>
88    {
 
89  0 toggle @Override
90    public int compare(SyndEntry entry1, SyndEntry entry2)
91    {
92  0 if ((entry1.getPublishedDate() == null) && (entry2.getPublishedDate() == null)) {
93  0 return 0;
94    }
95  0 if (entry1.getPublishedDate() == null) {
96  0 return 1;
97    }
98  0 if (entry2.getPublishedDate() == null) {
99  0 return -1;
100    }
101   
102  0 return (-entry1.getPublishedDate().compareTo(entry2.getPublishedDate()));
103    }
104    }
105   
 
106    public static class EntriesComparator implements Comparator<com.xpn.xwiki.api.Object>
107    {
 
108  0 toggle @Override
109    public int compare(com.xpn.xwiki.api.Object entry1, com.xpn.xwiki.api.Object entry2)
110    {
111  0 BaseObject bobj1 = entry1.getXWikiObject();
112  0 BaseObject bobj2 = entry1.getXWikiObject();
113   
114  0 if ((bobj1.getDateValue("date") == null) && (bobj2.getDateValue("date") == null)) {
115  0 return 0;
116    }
117  0 if (bobj1.getDateValue("date") == null) {
118  0 return 1;
119    }
120  0 if (bobj2.getDateValue("date") == null) {
121  0 return -1;
122    }
123   
124  0 return (-bobj1.getDateValue("date").compareTo(bobj2.getDateValue("date")));
125    }
126    }
127   
 
128  0 toggle public FeedPlugin(String name, String className, XWikiContext context)
129    {
130  0 super(name, className, context);
131   
132  0 init(context);
133    }
134   
 
135  0 toggle @Override
136    public String getName()
137    {
138  0 return "feed";
139    }
140   
 
141  0 toggle @Override
142    public Api getPluginApi(XWikiPluginInterface plugin, XWikiContext context)
143    {
144  0 return new FeedPluginApi((FeedPlugin) plugin, context);
145    }
146   
 
147  0 toggle @Override
148    public void flushCache()
149    {
150  0 if (this.feedCache != null) {
151  0 this.feedCache.dispose();
152    }
153  0 this.feedCache = null;
154    }
155   
 
156  0 toggle @Override
157    public void init(XWikiContext context)
158    {
159  0 super.init(context);
160   
161  0 prepareCache(context);
162  0 this.refreshPeriod = (int) context.getWiki().ParamAsLong("xwiki.plugins.feed.cacherefresh", 3600);
163   
164    // Make sure we have this class
165  0 try {
166  0 getAggregatorURLClass(context);
167    } catch (XWikiException e) {
168    }
169   
170    // Make sure we have this class
171  0 try {
172  0 getFeedEntryClass(context);
173    } catch (XWikiException e) {
174    }
175    }
176   
 
177  0 toggle public void initCache(XWikiContext context) throws XWikiException
178    {
179  0 int iCapacity = 100;
180  0 try {
181  0 String capacity = context.getWiki().Param("xwiki.plugins.feed.cache.capacity");
182  0 if (capacity != null) {
183  0 iCapacity = Integer.parseInt(capacity);
184    }
185    } catch (Exception e) {
186    }
187   
188  0 initCache(iCapacity, context);
189    }
190   
 
191  0 toggle public void initCache(int iCapacity, XWikiContext context) throws XWikiException
192    {
193  0 try {
194  0 CacheConfiguration configuration = new CacheConfiguration();
195  0 configuration.setConfigurationId("xwiki.plugin.feedcache");
196  0 LRUEvictionConfiguration lru = new LRUEvictionConfiguration();
197  0 lru.setMaxEntries(iCapacity);
198  0 lru.setMaxIdle(this.refreshPeriod);
199  0 configuration.put(LRUEvictionConfiguration.CONFIGURATIONID, lru);
200   
201  0 CacheManager cacheManager = Utils.getComponent(CacheManager.class);
202  0 this.feedCache = cacheManager.createNewLocalCache(configuration);
203    } catch (CacheException e) {
204  0 throw new XWikiException(XWikiException.MODULE_XWIKI_CACHE, XWikiException.ERROR_CACHE_INITIALIZING,
205    "Failed to create cache");
206    }
207    }
208   
 
209  0 toggle protected void prepareCache(XWikiContext context)
210    {
211  0 try {
212  0 if (this.feedCache == null) {
213  0 initCache(context);
214    }
215    } catch (XWikiException e) {
216    }
217    }
218   
 
219  0 toggle public SyndFeed getFeeds(String sfeeds, XWikiContext context) throws IOException
220    {
221  0 return getFeeds(sfeeds, false, true, context);
222    }
223   
 
224  0 toggle public SyndFeed getFeeds(String sfeeds, boolean force, XWikiContext context) throws IOException
225    {
226  0 return getFeeds(sfeeds, false, force, context);
227    }
228   
 
229  0 toggle public SyndFeed getFeeds(String sfeeds, boolean ignoreInvalidFeeds, boolean force, XWikiContext context)
230    throws IOException
231    {
232  0 String[] feeds;
233  0 if (sfeeds.indexOf("\n") != -1) {
234  0 feeds = sfeeds.split("\n");
235    } else {
236  0 feeds = sfeeds.split("\\|");
237    }
238  0 List<SyndEntry> entries = new ArrayList<SyndEntry>();
239  0 SyndFeed outputFeed = new SyndFeedImpl();
240  0 if (context.getDoc() != null) {
241  0 outputFeed.setTitle(context.getDoc().getFullName());
242  0 outputFeed.setUri(context.getWiki().getURL(context.getDoc().getFullName(), "view", context));
243  0 outputFeed.setAuthor(context.getDoc().getAuthor());
244    } else {
245  0 outputFeed.setTitle("XWiki Feeds");
246  0 outputFeed.setAuthor("XWiki Team");
247    }
248  0 for (int i = 0; i < feeds.length; i++) {
249  0 SyndFeed feed = getFeed(feeds[i], ignoreInvalidFeeds, force, context);
250  0 if (feed != null) {
251  0 entries.addAll(feed.getEntries());
252    }
253    }
254  0 SyndEntryComparator comp = new SyndEntryComparator();
255  0 Collections.sort(entries, comp);
256  0 outputFeed.setEntries(entries);
257   
258  0 return outputFeed;
259    }
260   
 
261  0 toggle public SyndFeed getFeed(String sfeed, XWikiContext context) throws IOException
262    {
263  0 return getFeed(sfeed, true, false, context);
264    }
265   
 
266  0 toggle public SyndFeed getFeed(String sfeed, boolean force, XWikiContext context) throws IOException
267    {
268  0 return getFeed(sfeed, true, force, context);
269    }
270   
 
271  0 toggle public SyndFeed getFeed(String sfeed, boolean ignoreInvalidFeeds, boolean force, XWikiContext context)
272    throws IOException
273    {
274  0 SyndFeed feed = null;
275  0 prepareCache(context);
276   
277  0 if (!force) {
278  0 feed = this.feedCache.get(sfeed);
279    }
280   
281  0 if (feed == null) {
282  0 feed = getFeedForce(sfeed, ignoreInvalidFeeds, context);
283    }
284   
285  0 if (feed != null) {
286  0 this.feedCache.set(sfeed, feed);
287    }
288   
289  0 return feed;
290    }
291   
 
292  0 toggle public SyndFeed getFeedForce(String sfeed, boolean ignoreInvalidFeeds, XWikiContext context) throws IOException
293    {
294  0 try {
295  0 URL feedURL = new URL(sfeed);
296  0 XWikiFeedFetcher feedFetcher = new XWikiFeedFetcher();
297  0 feedFetcher.setUserAgent(context.getWiki().Param("xwiki.plugins.feed.useragent",
298    context.getWiki().getHttpUserAgent(context)));
299  0 SyndFeed feed =
300    feedFetcher.retrieveFeed(
301    feedURL,
302    (int) context.getWiki().ParamAsLong("xwiki.plugins.feed.timeout",
303    context.getWiki().getHttpTimeout(context)));
304  0 return feed;
305    } catch (Exception ex) {
306  0 if (ignoreInvalidFeeds) {
307  0 @SuppressWarnings("unchecked")
308    Map<String, Exception> map = (Map<String, Exception>) context.get("invalidFeeds");
309  0 if (map == null) {
310  0 map = new HashMap<String, Exception>();
311  0 context.put("invalidFeeds", map);
312    }
313  0 map.put(sfeed, ex);
314   
315  0 return null;
316    }
317   
318  0 throw new java.io.IOException("Error processing " + sfeed + ": " + ex.getMessage());
319    }
320    }
321   
 
322  0 toggle public int updateFeeds(XWikiContext context) throws XWikiException
323    {
324  0 return updateFeeds("XWiki.FeedList", context);
325    }
326   
 
327  0 toggle public int updateFeeds(String feedDoc, XWikiContext context) throws XWikiException
328    {
329  0 return updateFeeds(feedDoc, false, context);
330    }
331   
 
332  0 toggle public int updateFeeds(String feedDoc, boolean fullContent, XWikiContext context) throws XWikiException
333    {
334  0 return updateFeeds(feedDoc, fullContent, true, context);
335    }
336   
 
337  0 toggle public int updateFeeds(String feedDoc, boolean fullContent, boolean oneDocPerEntry, XWikiContext context)
338    throws XWikiException
339    {
340  0 return updateFeeds(feedDoc, fullContent, oneDocPerEntry, false, context);
341    }
342   
 
343  0 toggle public int updateFeeds(String feedDoc, boolean fullContent, boolean oneDocPerEntry, boolean force,
344    XWikiContext context) throws XWikiException
345    {
346  0 return updateFeeds(feedDoc, fullContent, oneDocPerEntry, force, "Feeds", context);
347    }
348   
 
349  0 toggle public int updateFeeds(String feedDoc, boolean fullContent, boolean oneDocPerEntry, boolean force, String space,
350    XWikiContext context) throws XWikiException
351    {
352    // Make sure we have this class
353  0 getAggregatorURLClass(context);
354   
355  0 XWikiDocument doc = context.getWiki().getDocument(feedDoc, context);
356  0 Vector<BaseObject> objs = doc.getObjects("XWiki.AggregatorURLClass");
357  0 if (objs == null) {
358  0 return 0;
359    }
360   
361  0 int total = 0;
362  0 int nbfeeds = 0;
363  0 int nbfeedsErrors = 0;
364  0 for (BaseObject obj : objs) {
365  0 if (obj != null) {
366  0 String feedurl = obj.getStringValue("url");
367  0 String feedname = obj.getStringValue("name");
368  0 nbfeeds++;
369  0 int nb = updateFeed(feedDoc, feedname, feedurl, fullContent, oneDocPerEntry, force, space, context);
370  0 if (nb != -1) {
371  0 total += nb;
372    } else {
373  0 nbfeedsErrors++;
374    }
375   
376  0 UpdateThread updateThread = this.updateThreads.get(context.getWikiId() + ":" + space);
377  0 if (updateThread != null) {
378  0 updateThread.setNbLoadedFeeds(nbfeeds + updateThread.getNbLoadedFeeds());
379  0 updateThread.setNbLoadedFeedsErrors(nbfeedsErrors + updateThread.getNbLoadedFeedsErrors());
380    }
381  0 if (context.get("feedimgurl") != null) {
382  0 obj.set("imgurl", context.get("feedimgurl"), context);
383  0 context.remove("feedimgurl");
384    }
385  0 obj.set("nb", nb, context);
386  0 obj.set("date", new Date(), context);
387   
388    // Update original document
389  0 context.getWiki().saveDocument(doc, context);
390    }
391    }
392  0 return total;
393    }
394   
 
395  0 toggle public int updateFeedsInSpace(String spaceReference, boolean fullContent, boolean oneDocPerEntry, boolean force,
396    XWikiContext context) throws XWikiException
397    {
398    // Make sure we have this class
399  0 getAggregatorURLClass(context);
400   
401  0 String sql =
402    ", BaseObject as obj where doc.fullName=obj.name and obj.className='XWiki.AggregatorURLClass' and doc.space='"
403    + spaceReference + "'";
404  0 int total = 0;
405  0 List<String> feedDocList = context.getWiki().getStore().searchDocumentsNames(sql, context);
406  0 if (feedDocList != null) {
407  0 for (int i = 0; i < feedDocList.size(); i++) {
408  0 String feedDocName = feedDocList.get(i);
409  0 try {
410  0 total += updateFeeds(feedDocName, fullContent, oneDocPerEntry, force, spaceReference, context);
411    } catch (XWikiException e) {
412    // an exception occurred while updating feedDocName, don't fail completely, put the exception in the
413    // context and then pass to the next feed
414  0 @SuppressWarnings("unchecked")
415    Map<String, Exception> map = (Map<String, Exception>) context.get("updateFeedError");
416  0 if (map == null) {
417  0 map = new HashMap<String, Exception>();
418  0 context.put("updateFeedError", map);
419    }
420  0 map.put(feedDocName, e);
421    // and log it
422  0 LOGGER.error("Failed to update feeds in document " + feedDocName, e);
423    }
424    }
425    }
426  0 return total;
427    }
428   
 
429  0 toggle public boolean startUpdateFeedsInSpace(String space, boolean fullContent, int scheduleTimer, XWikiContext context)
430    throws XWikiException
431    {
432  0 UpdateThread updateThread = this.updateThreads.get(context.getWikiId() + ":" + space);
433  0 if (updateThread == null) {
434  0 updateThread = new UpdateThread(space, fullContent, scheduleTimer, this, context);
435  0 this.updateThreads.put(context.getWikiId() + ":" + space, updateThread);
436  0 Thread thread = new Thread(updateThread);
437  0 thread.start();
438  0 return true;
439    } else {
440  0 return false;
441    }
442    }
443   
 
444  0 toggle public void stopUpdateFeedsInSpace(String space, XWikiContext context) throws XWikiException
445    {
446  0 UpdateThread updateThread = this.updateThreads.get(context.getWikiId() + ":" + space);
447  0 if (updateThread != null) {
448  0 updateThread.stopUpdate();
449    }
450    }
451   
 
452  0 toggle public void removeUpdateThread(String space, UpdateThread thread, XWikiContext context)
453    {
454    // make sure the update thread is removed.
455    // this is called by the update thread when the loop is last exited
456  0 if (thread == this.updateThreads.get(context.getWikiId() + ":" + space)) {
457  0 this.updateThreads.remove(context.getWikiId() + ":" + space);
458    }
459    }
460   
 
461  0 toggle public UpdateThread getUpdateThread(String space, XWikiContext context)
462    {
463  0 return this.updateThreads.get(context.getWikiId() + ":" + space);
464    }
465   
 
466  0 toggle public Collection<String> getActiveUpdateThreads()
467    {
468  0 return this.updateThreads.keySet();
469    }
470   
 
471  0 toggle public int updateFeed(String feedname, String feedurl, boolean oneDocPerEntry, XWikiContext context)
472    {
473  0 return updateFeed(feedname, feedurl, false, oneDocPerEntry, context);
474    }
475   
 
476  0 toggle public int updateFeed(String feedname, String feedurl, boolean fullContent, boolean oneDocPerEntry,
477    XWikiContext context)
478    {
479  0 return updateFeed(feedname, feedurl, fullContent, oneDocPerEntry, false, context);
480    }
481   
 
482  0 toggle public int updateFeed(String feedname, String feedurl, boolean fullContent, boolean oneDocPerEntry, boolean force,
483    XWikiContext context)
484    {
485  0 return updateFeed(feedname, feedurl, fullContent, oneDocPerEntry, force, "Feeds", context);
486    }
487   
 
488  0 toggle public int updateFeed(String feedname, String feedurl, boolean fullContent, boolean oneDocPerEntry, boolean force,
489    String space, XWikiContext context)
490    {
491  0 return this.updateFeed("", feedname, feedurl, fullContent, oneDocPerEntry, force, space, context);
492    }
493   
 
494  0 toggle public int updateFeed(String feedDocumentName, String feedname, String feedurl, boolean fullContent,
495    boolean oneDocPerEntry, boolean force, String space, XWikiContext context)
496    {
497  0 try {
498    // Make sure we have this class
499  0 getFeedEntryClass(context);
500   
501  0 SyndFeed feed = getFeedForce(feedurl, true, context);
502  0 if (feed != null) {
503  0 if (feed.getImage() != null) {
504  0 context.put("feedimgurl", feed.getImage().getUrl());
505    }
506  0 return saveFeed(feedDocumentName, feedname, feedurl, feed, fullContent, oneDocPerEntry, force, space,
507    context);
508    } else {
509  0 return 0;
510    }
511    } catch (Exception e) {
512  0 @SuppressWarnings("unchecked")
513    Map<String, Exception> map = (Map<String, Exception>) context.get("updateFeedError");
514  0 if (map == null) {
515  0 map = new HashMap<String, Exception>();
516  0 context.put("updateFeedError", map);
517    }
518  0 map.put(feedurl, e);
519    }
520  0 return -1;
521    }
522   
 
523  0 toggle private int saveFeed(String feedDocumentName, String feedname, String feedurl, SyndFeed feed, boolean fullContent,
524    boolean oneDocPerEntry, boolean force, String space, XWikiContext context) throws XWikiException
525    {
526  0 XWikiDocument doc = null;
527  0 Vector<BaseObject> objs = null;
528  0 int nbtotal = 0;
529   
530  0 String prefix = space + ".Feed";
531  0 if (!oneDocPerEntry) {
532  0 doc =
533    context.getWiki().getDocument(
534    prefix + "_" + context.getWiki().clearName(feedname, true, true, context), context);
535  0 objs = doc.getObjects("XWiki.FeedEntryClass");
536  0 if (!StringUtils.isBlank(doc.getContent())) {
537  0 this.prepareFeedEntryDocument(doc, context);
538    }
539    }
540   
541  0 @SuppressWarnings("unchecked")
542    List<SyndEntry> entries = feed.getEntries();
543  0 int nb = entries.size();
544  0 for (int i = nb - 1; i >= 0; i--) {
545  0 SyndEntry entry = entries.get(i);
546  0 if (oneDocPerEntry) {
547  0 String hashCode = "" + entry.getLink().hashCode();
548  0 String pagename = feedname + "_" + hashCode.replaceAll("-", "") + "_" + entry.getTitle();
549  0 doc =
550    context.getWiki().getDocument(
551    prefix + "_" + context.getWiki().clearName(pagename, true, true, context), context);
552  0 if (doc.isNew() || force) {
553    // Set the document date to the current date
554  0 doc.setDate(new Date());
555  0 if (!StringUtils.isBlank(feedDocumentName)) {
556    // Set the feed document as parent of this feed entry
557  0 doc.setParent(feedDocumentName);
558    }
559  0 if (!StringUtils.isBlank(context.getWiki()
560    .Param("xwiki.plugins.feed.creationDateIsPublicationDate"))) {
561    // Set the creation date to the feed date if it exists, otherwise the current date
562  0 doc.setCreationDate((entry.getPublishedDate() == null) ? new Date() : entry.getPublishedDate());
563    }
564  0 if (StringUtils.isBlank(doc.getContent())) {
565  0 this.prepareFeedEntryDocument(doc, context);
566    }
567  0 if (force) {
568  0 BaseObject obj = doc.getObject("XWiki.FeedEntryClass");
569  0 if (obj == null) {
570  0 saveEntry(feedname, feedurl, entry, doc, fullContent, context);
571    } else {
572  0 saveEntry(feedname, feedurl, entry, doc, obj, fullContent, context);
573    }
574    } else {
575  0 saveEntry(feedname, feedurl, entry, doc, fullContent, context);
576    }
577  0 nbtotal++;
578  0 context.getWiki().saveDocument(doc, context);
579    }
580    } else {
581  0 BaseObject obj = postExist(objs, entry, context);
582  0 if (obj == null) {
583  0 saveEntry(feedname, feedurl, entry, doc, fullContent, context);
584  0 nbtotal++;
585  0 } else if (force) {
586  0 saveEntry(feedname, feedurl, entry, doc, obj, fullContent, context);
587  0 nbtotal++;
588    }
589    }
590    }
591   
592  0 if (!oneDocPerEntry) {
593  0 context.getWiki().saveDocument(doc, context);
594    }
595   
596  0 return nbtotal;
597    }
598   
599    /**
600    * Prepares a feed entry document by setting its content, syntax and creator according to the configuration.
601    *
602    * @param document the document to prepare as a feed entry document
603    * @param context the XWiki context when preparing the feed entry document
604    */
 
605  0 toggle private void prepareFeedEntryDocument(XWikiDocument document, XWikiContext context)
606    {
607  0 String content;
608  0 String syntaxId;
609  0 if (StringUtils.isBlank(context.getWiki().Param("xwiki.plugins.feed.entrySyntaxId"))) {
610  0 syntaxId = context.getWiki().getDefaultDocumentSyntax();
611    } else {
612  0 syntaxId = context.getWiki().Param("xwiki.plugins.feed.entrySyntaxId");
613    }
614  0 if (StringUtils.isBlank(context.getWiki().Param("xwiki.plugins.feed.entryContent"))) {
615    // If entry content is not configured, we do the best guess between XWiki syntaxes 1 and 2
616  0 content =
617  0 StringUtils.equals(Syntax.XWIKI_1_0.toIdString(), syntaxId)
618    ? "#includeForm(\"XWiki.FeedEntryClassSheet\")"
619    : "{{include reference=\"XWiki.FeedEntryClassSheet\" /}}";
620    } else {
621  0 content = context.getWiki().Param("xwiki.plugins.feed.entryContent");
622    }
623  0 String feedEntryCreator =
624    context.getWiki().getXWikiPreference("feed_documentCreator", "xwiki.plugins.feed.documentCreator",
625    XWikiRightService.SUPERADMIN_USER_FULLNAME, context);
626   
627  0 document.setCreator(feedEntryCreator);
628  0 document.setAuthor(feedEntryCreator);
629  0 document.setContentAuthor(feedEntryCreator);
630   
631  0 document.setContent(content);
632    // Let the document try to create the Syntax object from the syntax id. If it fails (for example if the syntax
633    // ID precised in configuration
634    // does not correspond to any syntax) it will fallback on the document default Syntax ID (which is a XWiki core
635    // configuration entry).
636  0 document.setSyntaxId(syntaxId);
637    }
638   
 
639  0 toggle public BaseClass getAggregatorURLClass(XWikiContext context) throws XWikiException
640    {
641  0 XWikiDocument doc;
642  0 boolean needsUpdate = false;
643   
644  0 doc = context.getWiki().getDocument("XWiki.AggregatorURLClass", context);
645   
646  0 BaseClass bclass = doc.getXClass();
647  0 if (context.get("initdone") != null) {
648  0 return bclass;
649    }
650   
651  0 bclass.setName("XWiki.AggregatorURLClass");
652  0 if (!"internal".equals(bclass.getCustomMapping())) {
653  0 needsUpdate = true;
654  0 bclass.setCustomMapping("internal");
655    }
656   
657  0 needsUpdate |= bclass.addTextField("name", "Name", 80);
658  0 needsUpdate |= bclass.addTextField("url", "url", 80);
659  0 needsUpdate |= bclass.addTextField("imgurl", "Image url", 80);
660  0 needsUpdate |= bclass.addDateField("date", "date", "dd/MM/yyyy HH:mm:ss");
661  0 needsUpdate |= bclass.addNumberField("nb", "nb", 5, "integer");
662   
663  0 if (doc.getCreatorReference() == null) {
664  0 needsUpdate = true;
665  0 doc.setCreator(XWikiRightService.SUPERADMIN_USER);
666    }
667  0 if (doc.getAuthorReference() == null) {
668  0 needsUpdate = true;
669  0 doc.setAuthorReference(doc.getCreatorReference());
670    }
671  0 if (StringUtils.isBlank(doc.getTitle())) {
672  0 needsUpdate = true;
673  0 doc.setTitle("XWiki Aggregator URL Class");
674    }
675  0 if (StringUtils.isBlank(doc.getContent()) || !Syntax.XWIKI_2_0.equals(doc.getSyntax())) {
676  0 needsUpdate = true;
677  0 doc.setContent("{{include reference=\"XWiki.ClassSheet\" /}}");
678  0 doc.setSyntax(Syntax.XWIKI_2_0);
679    }
680  0 if (StringUtils.isBlank(doc.getParent())) {
681  0 needsUpdate = true;
682  0 doc.setParent("XWiki.XWikiClasses");
683    }
684  0 if (!doc.isHidden()) {
685  0 needsUpdate = true;
686  0 doc.setHidden(true);
687    }
688   
689  0 if (needsUpdate) {
690  0 context.getWiki().saveDocument(doc, context);
691    }
692   
693  0 return bclass;
694    }
695   
 
696  0 toggle public BaseClass getFeedEntryClass(XWikiContext context) throws XWikiException
697    {
698  0 XWikiDocument doc;
699  0 boolean needsUpdate = false;
700   
701  0 doc = context.getWiki().getDocument("XWiki.FeedEntryClass", context);
702   
703  0 BaseClass bclass = doc.getXClass();
704  0 if (context.get("initdone") != null) {
705  0 return bclass;
706    }
707   
708  0 bclass.setName("XWiki.FeedEntryClass");
709  0 if (!"internal".equals(bclass.getCustomMapping())) {
710  0 needsUpdate = true;
711  0 bclass.setCustomMapping("internal");
712    }
713   
714  0 needsUpdate |= bclass.addTextField("title", "Title", 80);
715  0 needsUpdate |= bclass.addTextField("author", "Author", 40);
716  0 needsUpdate |= bclass.addTextField("feedurl", "Feed URL", 80);
717  0 needsUpdate |= bclass.addTextField("feedname", "Feed Name", 40);
718  0 needsUpdate |= bclass.addTextField("url", "URL", 80);
719  0 needsUpdate |= bclass.addTextField("category", "Category", 255);
720  0 needsUpdate |= bclass.addTextAreaField("content", "Content", 80, 10);
721  0 needsUpdate |= bclass.addTextAreaField("fullContent", "Full Content", 80, 10);
722  0 needsUpdate |= bclass.addTextAreaField("xml", "XML", 80, 10);
723  0 needsUpdate |= bclass.addDateField("date", "date", "dd/MM/yyyy HH:mm:ss");
724  0 needsUpdate |= bclass.addNumberField("flag", "Flag", 5, "integer");
725  0 needsUpdate |= bclass.addNumberField("read", "Read", 5, "integer");
726  0 needsUpdate |= bclass.addStaticListField("tags", "Tags", 1, true, true, "", null, null);
727   
728  0 if (doc.getCreatorReference() == null) {
729  0 needsUpdate = true;
730  0 doc.setCreator(XWikiRightService.SUPERADMIN_USER);
731    }
732   
733  0 if (doc.getAuthorReference() == null) {
734  0 needsUpdate = true;
735  0 doc.setAuthorReference(doc.getCreatorReference());
736    }
737   
738  0 if (StringUtils.isBlank(doc.getTitle())) {
739  0 needsUpdate = true;
740  0 doc.setTitle("XWiki Feed Entry Class");
741    }
742   
743  0 if (StringUtils.isBlank(doc.getContent()) || !Syntax.XWIKI_2_0.equals(doc.getSyntax())) {
744  0 needsUpdate = true;
745  0 doc.setContent("{{include reference=\"XWiki.ClassSheet\" /}}");
746  0 doc.setSyntax(Syntax.XWIKI_2_0);
747    }
748   
749  0 if (StringUtils.isBlank(doc.getParent())) {
750  0 needsUpdate = true;
751  0 doc.setParent("XWiki.XWikiClasses");
752    }
753   
754  0 if (!doc.isHidden()) {
755  0 needsUpdate = true;
756  0 doc.setHidden(true);
757    }
758   
759  0 if (needsUpdate) {
760  0 context.getWiki().saveDocument(doc, context);
761    }
762  0 return bclass;
763   
764    }
765   
 
766  0 toggle private void saveEntry(String feedname, String feedurl, SyndEntry entry, XWikiDocument doc, boolean fullContent,
767    XWikiContext context) throws XWikiException
768    {
769  0 int id = doc.createNewObject("XWiki.FeedEntryClass", context);
770  0 BaseObject obj = doc.getObject("XWiki.FeedEntryClass", id);
771  0 saveEntry(feedname, feedurl, entry, doc, obj, fullContent, context);
772    }
773   
 
774  0 toggle private void saveEntry(String feedname, String feedurl, SyndEntry entry, XWikiDocument doc, BaseObject obj,
775    boolean fullContent, XWikiContext context) throws XWikiException
776    {
777  0 obj.setStringValue("feedname", feedname);
778  0 obj.setStringValue("title", entry.getTitle());
779    // set document title to the feed title
780  0 String title;
781  0 try {
782    // Strips HTML tags that the feed can output, since they are not supported in document titles.
783    // (For example Google Blog search adds a <b> tag around matched keyword in title)
784    // If some day wiki syntax is supported in document titles, we might want to convert to wiki syntax instead.
785  0 title = this.stripHtmlTags(entry.getTitle());
786    } catch (ConversionException e) {
787  0 LOGGER.warn("Failed to strip HTML tags from entry title : " + e.getMessage());
788    // Nevermind, we will use the original title
789  0 title = entry.getTitle();
790    }
791  0 doc.setTitle(title);
792  0 obj.setIntValue("flag", 0);
793  0 @SuppressWarnings("unchecked")
794    List<SyndCategory> categList = entry.getCategories();
795  0 StringBuffer categs = new StringBuffer("");
796  0 if (categList != null) {
797  0 for (SyndCategory categ : categList) {
798  0 if (categs.length() != 0) {
799  0 categs.append(", ");
800    }
801  0 categs.append(categ.getName());
802    }
803    }
804  0 obj.setStringValue("category", categs.toString());
805   
806  0 StringBuffer contents = new StringBuffer("");
807  0 String description = (entry.getDescription() == null) ? null : entry.getDescription().getValue();
808   
809  0 @SuppressWarnings("unchecked")
810    List<SyndContent> contentList = entry.getContents();
811  0 if (contentList != null && contentList.size() > 0) {
812  0 for (SyndContent content : contentList) {
813  0 if (contents.length() != 0) {
814  0 contents.append("\n");
815    }
816  0 contents.append(content.getValue());
817    }
818    }
819   
820    // If we find more data in the description we will use that one instead of the content field
821  0 if ((description != null) && (description.length() > contents.length())) {
822  0 obj.setLargeStringValue("content", description);
823    } else {
824  0 obj.setLargeStringValue("content", contents.toString());
825    }
826   
827  0 Date edate = entry.getPublishedDate();
828  0 if (edate == null) {
829  0 edate = new Date();
830    }
831   
832  0 obj.setDateValue("date", edate);
833  0 obj.setStringValue("url", entry.getLink());
834  0 obj.setStringValue("author", entry.getAuthor());
835  0 obj.setStringValue("feedurl", feedurl);
836   
837    // TODO: need to get entry xml or serialization
838    // obj.setLargeStringValue("xml", entry.toString());
839  0 obj.setLargeStringValue("xml", "");
840   
841  0 if (fullContent) {
842  0 String url = entry.getLink();
843  0 if ((url != null) && (!url.trim().equals(""))) {
844  0 try {
845  0 String sfullContent = context.getWiki().getURLContent(url, context);
846  0 obj.setLargeStringValue("fullContent",
847  0 (sfullContent.length() > 65000) ? sfullContent.substring(0, 65000) : sfullContent);
848    } catch (Exception e) {
849  0 obj.setLargeStringValue("fullContent", "Exception while reading fullContent: " + e.getMessage());
850    }
851    } else {
852  0 obj.setLargeStringValue("fullContent", "No url");
853    }
854    }
855    }
856   
 
857  0 toggle private BaseObject postExist(Vector<BaseObject> objs, SyndEntry entry, XWikiContext context)
858    {
859  0 if (objs == null) {
860  0 return null;
861    }
862  0 String title = context.getWiki().clearName(entry.getTitle(), true, true, context);
863  0 for (BaseObject obj : objs) {
864  0 if (obj != null) {
865  0 String title2 = obj.getStringValue("title");
866  0 if (title2 == null) {
867  0 title2 = "";
868    } else {
869  0 title2 = context.getWiki().clearName(title2, true, true, context);
870    }
871   
872  0 if (title2.compareTo(title) == 0) {
873  0 return obj;
874    }
875    }
876    }
877  0 return null;
878    }
879   
 
880  0 toggle public List<com.xpn.xwiki.api.Object> search(String query, XWikiContext context) throws XWikiException
881    {
882  0 String[] queryTab = query.split(" ");
883   
884  0 if (queryTab.length > 0) {
885  0 String sql =
886    "select distinct obj.number, obj.name from BaseObject as obj, StringProperty as prop , LargeStringProperty as lprop "
887    + "where obj.className='XWiki.FeedEntryClass' and obj.id=prop.id.id and obj.id=lprop.id.id ";
888   
889  0 for (int i = 0; i < queryTab.length; i++) {
890  0 sql += " and (prop.value LIKE '%" + queryTab[i] + "%' or lprop.value LIKE '%" + queryTab[i] + "%')";
891    }
892  0 List<Object[]> res = context.getWiki().search(sql, context);
893   
894  0 if (res == null) {
895  0 return null;
896    }
897   
898  0 List<com.xpn.xwiki.api.Object> apiObjs = new ArrayList<com.xpn.xwiki.api.Object>();
899  0 for (Object obj[] : res) {
900  0 try {
901  0 XWikiDocument doc = context.getWiki().getDocument((String) obj[1], context);
902  0 if (context.getWiki().getRightService().checkAccess("view", doc, context)) {
903  0 BaseObject bObj = doc.getObject("XWiki.FeedEntryClass", ((Integer) obj[0]).intValue());
904  0 com.xpn.xwiki.api.Object apiObj = new com.xpn.xwiki.api.Object(bObj, context);
905  0 apiObjs.add(apiObj);
906    }
907    } catch (Exception e) {
908  0 @SuppressWarnings("unchecked")
909    Map<String, Exception> map = (Map<String, Exception>) context.get("searchFeedError");
910  0 if (map == null) {
911  0 map = new HashMap<String, Exception>();
912  0 context.put("searchFeedError", map);
913    }
914  0 map.put(query, e);
915    }
916    }
917   
918  0 Collections.sort(apiObjs, new EntriesComparator());
919  0 return apiObjs;
920    }
921  0 return null;
922    }
923   
 
924  0 toggle public com.xpn.xwiki.api.Object getFeedInfosbyGuid(String guid, XWikiContext context) throws XWikiException
925    {
926  0 return getFeedInfosbyGuid("XWiki.FeedList", guid, context);
927    }
928   
 
929  0 toggle public com.xpn.xwiki.api.Object getFeedInfosbyGuid(String feedDoc, String guid, XWikiContext context)
930    throws XWikiException
931    {
932  0 XWikiDocument doc = context.getWiki().getDocument(feedDoc, context);
933  0 Vector<BaseObject> objs = doc.getObjects("XWiki.AggregatorURLClass");
934  0 for (BaseObject obj : objs) {
935  0 if (guid.compareTo(obj.getStringValue("guid")) == 0) {
936  0 return new com.xpn.xwiki.api.Object(obj, context);
937    }
938    }
939   
940  0 return null;
941    }
942   
943    /**
944    * @see FeedPluginApi#getSyndEntrySource(String, Map)
945    */
 
946  0 toggle public SyndEntrySource getSyndEntrySource(String className, Map<String, Object> params, XWikiContext context)
947    throws XWikiException
948    {
949  0 try {
950  0 Class< ? extends SyndEntrySource> sesc = Class.forName(className).asSubclass(SyndEntrySource.class);
951  0 Constructor< ? extends SyndEntrySource> ctor = null;
952  0 if (params != null) {
953  0 try {
954  0 ctor = sesc.getConstructor(new Class[] {Map.class});
955  0 return ctor.newInstance(new Object[] {params});
956    } catch (Throwable t) {
957    }
958    }
959  0 ctor = sesc.getConstructor(new Class[] {});
960  0 return ctor.newInstance(new Object[] {});
961    } catch (Throwable t) {
962  0 throw new XWikiException(XWikiException.MODULE_XWIKI_PLUGINS, XWikiException.ERROR_XWIKI_UNKNOWN, "", t);
963    }
964    }
965   
966    /**
967    * @see FeedPluginApi#getFeedEntry()
968    */
 
969  0 toggle public SyndEntry getFeedEntry(XWikiContext context)
970    {
971  0 return new SyndEntryImpl();
972    }
973   
974    /**
975    * @see FeedPluginApi#getFeedImage()
976    */
 
977  0 toggle public SyndImage getFeedImage(XWikiContext context)
978    {
979  0 return new SyndImageImpl();
980    }
981   
982    /**
983    * @see FeedPluginApi#getFeed()
984    */
 
985  0 toggle public SyndFeed getFeed(XWikiContext context)
986    {
987  0 return new SyndFeedImpl();
988    }
989   
990    /**
991    * @see FeedPluginApi#getFeed(List, SyndEntrySourceApi, Map)
992    */
 
993  0 toggle public SyndFeed getFeed(List<Object> list, SyndEntrySource source, Map<String, Object> sourceParams,
994    XWikiContext context) throws XWikiException
995    {
996  0 SyndFeed feed = getFeed(context);
997  0 List<SyndEntry> entries = new ArrayList<SyndEntry>();
998  0 for (int i = 0; i < list.size(); i++) {
999  0 SyndEntry entry = getFeedEntry(context);
1000  0 try {
1001  0 source.source(entry, list.get(i), sourceParams, context);
1002  0 entries.add(entry);
1003    } catch (Throwable t) {
1004    // skip this entry
1005    }
1006    }
1007  0 feed.setEntries(entries);
1008  0 return feed;
1009    }
1010   
1011    /**
1012    * @see FeedPluginApi#getFeed(String, int, int, SyndEntrySourceApi, Map)
1013    */
 
1014  0 toggle public SyndFeed getFeed(String query, int count, int start, SyndEntrySource source,
1015    Map<String, Object> sourceParams, XWikiContext context) throws XWikiException
1016    {
1017  0 List<Object> entries = new ArrayList<Object>();
1018  0 entries.addAll(context.getWiki().getStore().searchDocumentsNames(query, count, start, context));
1019  0 return getFeed(entries, source, sourceParams, context);
1020    }
1021   
1022    /**
1023    * @see FeedPluginApi#getFeed(List, SyndEntrySourceApi, Map, Map)
1024    */
 
1025  0 toggle public SyndFeed getFeed(List<Object> list, SyndEntrySource source, Map<String, Object> sourceParams,
1026    Map<String, Object> metadata, XWikiContext context) throws XWikiException
1027    {
1028  0 SyndFeed feed = getFeed(list, source, sourceParams, context);
1029  0 fillFeedMetadata(feed, metadata, context);
1030  0 return feed;
1031    }
1032   
1033    /**
1034    * @see FeedPluginApi#getFeed(String, int, int, SyndEntrySourceApi, Map, Map)
1035    */
 
1036  0 toggle public SyndFeed getFeed(String query, int count, int start, SyndEntrySource source,
1037    Map<String, Object> sourceParams, Map<String, Object> metadata, XWikiContext context) throws XWikiException
1038    {
1039  0 SyndFeed feed = getFeed(query, count, start, source, sourceParams, context);
1040  0 fillFeedMetadata(feed, metadata, context);
1041  0 return feed;
1042    }
1043   
 
1044  0 toggle private void fillFeedMetadata(SyndFeed feed, Map<String, Object> metadata, XWikiContext context)
1045    {
1046  0 XWiki xwiki = context.getWiki();
1047  0 XWikiDocument doc = context.getDoc();
1048   
1049  0 if (metadata.containsKey("author")) {
1050  0 feed.setAuthor(String.valueOf(metadata.get("author")));
1051  0 } else if (doc != null) {
1052  0 feed.setAuthor(xwiki.getUserName(doc.getAuthor(), null, false, context));
1053    }
1054   
1055  0 if (metadata.containsKey("description")) {
1056  0 feed.setDescription(String.valueOf(metadata.get("description")));
1057    }
1058   
1059  0 if (metadata.containsKey("copyright")) {
1060  0 feed.setCopyright(String.valueOf(metadata.get("copyright")));
1061    } else {
1062  0 feed.setCopyright(xwiki.getSpaceCopyright(context));
1063    }
1064   
1065  0 if (metadata.containsKey("encoding")) {
1066  0 feed.setEncoding(String.valueOf(metadata.get("encoding")));
1067    } else {
1068  0 feed.setEncoding(xwiki.getEncoding());
1069    }
1070   
1071    // TODO: rename "url" to "link" for consistency ?
1072  0 if (metadata.containsKey("url")) {
1073  0 feed.setLink(String.valueOf(metadata.get("url")));
1074    } else {
1075  0 feed.setLink("http://" + context.getRequest().getServerName());
1076    }
1077   
1078  0 if (metadata.containsKey("title")) {
1079  0 feed.setTitle(String.valueOf(metadata.get("title")));
1080    }
1081   
1082  0 if (metadata.containsKey("language")) {
1083  0 feed.setLanguage(String.valueOf(metadata.get("language")));
1084  0 } else if (doc != null) {
1085  0 feed.setLanguage(doc.getDefaultLanguage());
1086    }
1087    }
1088   
1089    /**
1090    * @see FeedPluginApi#getFeedOutput(SyndFeed, String)
1091    */
 
1092  0 toggle public String getFeedOutput(SyndFeed feed, String type, XWikiContext context)
1093    {
1094  0 feed.setFeedType(type);
1095  0 StringWriter writer = new StringWriter();
1096  0 SyndFeedOutput output = new SyndFeedOutput();
1097  0 try {
1098  0 output.output(feed, writer);
1099  0 writer.close();
1100  0 return writer.toString();
1101    } catch (Exception e) {
1102  0 e.printStackTrace();
1103  0 return "";
1104    }
1105    }
1106   
1107    /**
1108    * @see FeedPluginApi#getFeedOutput(List, SyndEntrySourceApi, Map, Map, String)
1109    */
 
1110  0 toggle public String getFeedOutput(List<Object> list, SyndEntrySource source, Map<String, Object> sourceParams,
1111    Map<String, Object> metadata, String type, XWikiContext context) throws XWikiException
1112    {
1113  0 SyndFeed feed = getFeed(list, source, sourceParams, metadata, context);
1114  0 return getFeedOutput(feed, type, context);
1115    }
1116   
1117    /**
1118    * @see FeedPluginApi#getFeedOutput(String, int, int, SyndEntrySourceApi, Map, Map, String)
1119    */
 
1120  0 toggle public String getFeedOutput(String query, int count, int start, SyndEntrySource source,
1121    Map<String, Object> sourceParams, Map<String, Object> metadata, String type, XWikiContext context)
1122    throws XWikiException
1123    {
1124  0 SyndFeed feed = getFeed(query, count, start, source, sourceParams, metadata, context);
1125  0 return getFeedOutput(feed, type, context);
1126    }
1127   
1128    /**
1129    * @param originalString the string to strip the HTML tags from
1130    * @return the passed string, stripped from HTML markup
1131    * @throws ConversionException when the conversion fails
1132    */
 
1133  0 toggle private String stripHtmlTags(String originalString) throws ConversionException
1134    {
1135  0 if (this.syntaxConverter == null) {
1136  0 this.syntaxConverter = Utils.getComponent(Converter.class);
1137    }
1138  0 WikiPrinter printer = new DefaultWikiPrinter();
1139  0 this.syntaxConverter.convert(new StringReader(originalString), Syntax.HTML_4_01, Syntax.PLAIN_1_0, printer);
1140  0 return printer.toString();
1141    }
1142    }