Clover Coverage Report - Pebble 2.5-SNAPSHOT
Coverage timestamp: Sat Jun 12 2010 09:39:29 EST
../../../../img/srcFileCovDistChart7.png 33% of files have more coverage
529   1.854   219   4,37
116   1.024   0,41   121
121     1,81  
1    
This report was generated with an evaluation server license. Purchase Clover or configure your license.
 
  Blog       Line # 103 529 0% 219 246 67,9% 0.6788512
 
  (688)
 
1    /*
2    * Copyright (c) 2003-2006, Simon Brown
3    * All rights reserved.
4    *
5    * Redistribution and use in source and binary forms, with or without
6    * modification, are permitted provided that the following conditions are met:
7    *
8    * - Redistributions of source code must retain the above copyright
9    * notice, this list of conditions and the following disclaimer.
10    *
11    * - Redistributions in binary form must reproduce the above copyright
12    * notice, this list of conditions and the following disclaimer in
13    * the documentation and/or other materials provided with the
14    * distribution.
15    *
16    * - Neither the name of Pebble nor the names of its contributors may
17    * be used to endorse or promote products derived from this software
18    * without specific prior written permission.
19    *
20    * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21    * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22    * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23    * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
24    * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25    * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26    * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27    * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28    * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29    * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30    * POSSIBILITY OF SUCH DAMAGE.
31    */
32    package net.sourceforge.pebble.domain;
33   
34    import java.io.File;
35    import java.lang.reflect.Constructor;
36    import java.util.ArrayList;
37    import java.util.Arrays;
38    import java.util.Calendar;
39    import java.util.Collection;
40    import java.util.Collections;
41    import java.util.Date;
42    import java.util.Iterator;
43    import java.util.LinkedList;
44    import java.util.List;
45    import java.util.Properties;
46    import java.util.StringTokenizer;
47   
48    import javax.servlet.http.HttpServletRequest;
49   
50    import net.sourceforge.pebble.Configuration;
51    import net.sourceforge.pebble.Constants;
52    import net.sourceforge.pebble.PebbleContext;
53    import net.sourceforge.pebble.PluginProperties;
54    import net.sourceforge.pebble.aggregator.NewsFeedCache;
55    import net.sourceforge.pebble.aggregator.NewsFeedEntry;
56    import net.sourceforge.pebble.api.confirmation.CommentConfirmationStrategy;
57    import net.sourceforge.pebble.api.confirmation.TrackBackConfirmationStrategy;
58    import net.sourceforge.pebble.api.decorator.ContentDecorator;
59    import net.sourceforge.pebble.api.event.EventDispatcher;
60    import net.sourceforge.pebble.api.event.blog.BlogEvent;
61    import net.sourceforge.pebble.api.event.blog.BlogListener;
62    import net.sourceforge.pebble.api.event.blogentry.BlogEntryListener;
63    import net.sourceforge.pebble.api.event.comment.CommentListener;
64    import net.sourceforge.pebble.api.event.trackback.TrackBackListener;
65    import net.sourceforge.pebble.api.permalink.PermalinkProvider;
66    import net.sourceforge.pebble.confirmation.DefaultConfirmationStrategy;
67    import net.sourceforge.pebble.dao.CategoryDAO;
68    import net.sourceforge.pebble.dao.DAOFactory;
69    import net.sourceforge.pebble.dao.PersistenceException;
70    import net.sourceforge.pebble.decorator.ContentDecoratorChain;
71    import net.sourceforge.pebble.decorator.HideUnapprovedResponsesDecorator;
72    import net.sourceforge.pebble.event.AuditListener;
73    import net.sourceforge.pebble.event.DefaultEventDispatcher;
74    import net.sourceforge.pebble.event.EventListenerList;
75    import net.sourceforge.pebble.event.blogentry.EmailSubscriptionListener;
76    import net.sourceforge.pebble.index.AuthorIndex;
77    import net.sourceforge.pebble.index.AuthorIndexListener;
78    import net.sourceforge.pebble.index.BlogEntryIndex;
79    import net.sourceforge.pebble.index.BlogEntryIndexListener;
80    import net.sourceforge.pebble.index.CategoryIndex;
81    import net.sourceforge.pebble.index.CategoryIndexListener;
82    import net.sourceforge.pebble.index.EmailSubscriptionList;
83    import net.sourceforge.pebble.index.ResponseIndex;
84    import net.sourceforge.pebble.index.ResponseIndexListener;
85    import net.sourceforge.pebble.index.SearchIndex;
86    import net.sourceforge.pebble.index.SearchIndexListener;
87    import net.sourceforge.pebble.index.StaticPageIndex;
88    import net.sourceforge.pebble.index.TagIndex;
89    import net.sourceforge.pebble.index.TagIndexListener;
90    import net.sourceforge.pebble.logging.AbstractLogger;
91    import net.sourceforge.pebble.logging.CombinedLogFormatLogger;
92    import net.sourceforge.pebble.permalink.DefaultPermalinkProvider;
93    import net.sourceforge.pebble.util.StringUtils;
94   
95    import org.apache.commons.logging.Log;
96    import org.apache.commons.logging.LogFactory;
97   
98    /**
99    * Represents a blog.
100    *
101    * @author Simon Brown
102    */
 
103    public class Blog extends AbstractBlog {
104   
105    private static final Log log = LogFactory.getLog(Blog.class);
106   
107    public static final String ABOUT_KEY = "about";
108    public static final String EMAIL_KEY = "email";
109    public static final String BLOG_OWNERS_KEY = "blogOwners";
110    public static final String BLOG_PUBLISHERS_KEY = "blogPublishers";
111    public static final String BLOG_CONTRIBUTORS_KEY = "blogContributors";
112    public static final String BLOG_READERS_KEY = "blogReaders";
113    public static final String PRIVATE_KEY = "private";
114    public static final String LUCENE_ANALYZER_KEY = "luceneAnalyzer";
115    public static final String CONTENT_DECORATORS_KEY = "decorators";
116    public static final String BLOG_LISTENERS_KEY = "blogListeners";
117    public static final String BLOG_ENTRY_LISTENERS_KEY = "blogEntryListeners";
118    public static final String COMMENT_LISTENERS_KEY = "commentListeners";
119    public static final String TRACKBACK_LISTENERS_KEY = "trackBackListeners";
120    public static final String EVENT_DISPATCHER_KEY = "eventDispatcher";
121    public static final String LOGGER_KEY = "logger";
122    public static final String PERMALINK_PROVIDER_KEY = "permalinkProviderName";
123    public static final String COMMENT_CONFIRMATION_STRATEGY_KEY = "commentConfirmationStrategy";
124    public static final String TRACKBACK_CONFIRMATION_STRATEGY_KEY = "trackBackConfirmationStrategy";
125    public static final String RICH_TEXT_EDITOR_FOR_COMMENTS_ENABLED_KEY = "richTextEditorForCommentsEnabled";
126    public static final String HOME_PAGE_KEY = "homePage";
127   
128    /** the ID of this blog */
129    private String id = "default";
130   
131    /** the collection of Year instance that this root blog is managing */
132    private List<Year> years;
133   
134    /** the root category associated with this blog */
135    private Category rootCategory;
136   
137    /** the referer filter associated with this blog */
138    private RefererFilterManager refererFilterManager;
139   
140    /** the editable theme belonging to this blog */
141    private Theme editableTheme;
142   
143    /** the permalink provider in use */
144    private PermalinkProvider permalinkProvider;
145   
146    /** the log used to log referers, requests, etc */
147    private AbstractLogger logger;
148   
149    /** the decorator chain associated with this blog */
150    private ContentDecoratorChain decoratorChain;
151   
152    private CommentConfirmationStrategy commentConfirmationStrategy;
153    private TrackBackConfirmationStrategy trackBackConfirmationStrategy;
154   
155    /** the event dispatcher */
156    private EventDispatcher eventDispatcher;
157   
158    /** the event listener list */
159    private EventListenerList eventListenerList;
160   
161    /** the plugin properties */
162    private PluginProperties pluginProperties;
163   
164    private SearchIndex searchIndex;
165    private BlogEntryIndex blogEntryIndex;
166    private ResponseIndex responseIndex;
167    private TagIndex tagIndex;
168    private CategoryIndex categoryIndex;
169    private AuthorIndex authorIndex;
170    private StaticPageIndex staticPageIndex;
171   
172    private EmailSubscriptionList emailSubscriptionList;
173   
174    /**
175    * Creates a new Blog instance, based at the specified location.
176    *
177    * @param root an absolute path pointing to the root directory of the blog
178    */
 
179  1334 toggle public Blog(String root) {
180  1334 super(root);
181   
182    // probably Blog should be made a final class if init is called from here -
183    // see javadoc comment on AbstractBlog.init() for reasons
184  1334 init();
185    }
186   
187    /**
188    * Initialize this blog - prepare it for use.
189    * Note: As this blog instance is passed to the various participants while
190    * it is being initialized, this method is dependent on the correct order
191    * of calls: Keep in mind that 'this' is only partly initialized until the
192    * end of this method...
193    */
194   
 
195  1334 toggle protected void init() {
196  1334 super.init();
197   
198  1334 try {
199  1334 Class c = Class.forName(getPermalinkProviderName());
200  1334 setPermalinkProvider((PermalinkProvider)c.newInstance());
201    } catch (Exception e) {
202  0 error("Could not load permalink provider \"" + getPermalinkProviderName() + "\"");
203  0 e.printStackTrace();
204  0 setPermalinkProvider(new DefaultPermalinkProvider());
205    }
206   
207    // load categories
208  1334 try {
209  1334 DAOFactory factory = DAOFactory.getConfiguredFactory();
210  1334 CategoryDAO dao = factory.getCategoryDAO();
211  1334 rootCategory = dao.getCategories(this);
212    } catch (PersistenceException pe) {
213  0 pe.printStackTrace();
214    }
215   
216  1334 refererFilterManager = new RefererFilterManager(this);
217  1334 pluginProperties = new PluginProperties(this);
218  1334 years = new ArrayList();
219   
220    // create the various indexes for this blog
221  1334 searchIndex = new SearchIndex(this);
222  1334 blogEntryIndex = new BlogEntryIndex(this);
223  1334 responseIndex = new ResponseIndex(this);
224  1334 tagIndex = new TagIndex(this);
225  1334 categoryIndex = new CategoryIndex(this);
226  1334 authorIndex = new AuthorIndex(this);
227  1334 staticPageIndex = new StaticPageIndex(this);
228   
229  1334 decoratorChain = new ContentDecoratorChain(this);
230   
231  1334 try {
232  1334 Class c = Class.forName(getCommentConfirmationStrategyName());
233  1334 commentConfirmationStrategy = (CommentConfirmationStrategy)c.newInstance();
234    } catch (Exception e) {
235  0 error("Could not load comment confirmation strategy \"" + getCommentConfirmationStrategyName() + "\"");
236  0 e.printStackTrace();
237  0 commentConfirmationStrategy = new DefaultConfirmationStrategy();
238    }
239   
240  1334 try {
241  1334 Class c = Class.forName(getTrackBackConfirmationStrategyName());
242  1334 trackBackConfirmationStrategy = (TrackBackConfirmationStrategy)c.newInstance();
243    } catch (Exception e) {
244  0 error("Could not load TrackBack confirmation strategy \"" + getTrackBackConfirmationStrategyName() + "\"");
245  0 e.printStackTrace();
246  0 trackBackConfirmationStrategy = new DefaultConfirmationStrategy();
247    }
248   
249  1334 emailSubscriptionList = new EmailSubscriptionList(this);
250   
251  1334 initLogger();
252  1334 initEventDispatcher();
253  1334 initBlogListeners();
254  1334 initBlogEntryListeners();
255  1334 initCommentListeners();
256  1334 initTrackBackListeners();
257  1334 initDecorators();
258    }
259   
260    /**
261    * Initialises the logger for this blog.
262    */
 
263  1334 toggle private void initLogger() {
264  1334 log.debug("Initializing logger");
265   
266  1334 try {
267  1334 Class c = Class.forName(getLoggerName());
268  1334 Constructor cons = c.getConstructor(new Class[] {Blog.class});
269  1334 this.logger = (AbstractLogger)cons.newInstance(new Object[] {this});
270    } catch (Exception e) {
271  0 error("Could not start logger \"" + getLoggerName() + "\"");
272  0 e.printStackTrace();
273  0 this.logger = new CombinedLogFormatLogger(this);
274    }
275    }
276   
277    /**
278    * Initialises the event dispatcher for this blog.
279    */
 
280  1334 toggle private void initEventDispatcher() {
281  1334 log.debug("Initializing event dispatcher");
282  1334 eventListenerList = new EventListenerList();
283   
284  1334 try {
285  1334 Class c = Class.forName(getEventDispatcherName());
286  1334 this.eventDispatcher = (EventDispatcher)c.newInstance();
287    } catch (Exception e) {
288  0 e.printStackTrace();
289  0 this.eventDispatcher = new DefaultEventDispatcher();
290    }
291   
292  1334 eventDispatcher.setEventListenerList(eventListenerList);
293    }
294   
295    /**
296    * Initialises any blog listeners configured for this blog.
297    */
 
298  1334 toggle private void initBlogListeners() {
299  1334 log.debug("Registering blog listeners");
300   
301  1334 for (String className : getBlogListeners()) {
302  0 try {
303  0 Class c = Class.forName(className.trim());
304  0 BlogListener listener = (BlogListener) c.newInstance();
305  0 eventListenerList.addBlogListener(listener);
306    } catch (Exception e) {
307  0 error("Could not start blog listener \"" + className + "\" - check the class name is correct on the <a href=\"viewPlugins.secureaction#blogListeners\">plugins page</a>.");
308  0 log.error("Blog listener " + className + " could not be registered", e);
309    }
310    }
311    }
312   
313    /**
314    * Initialises any blog entry listeners configured for this blog.
315    */
 
316  1334 toggle private void initBlogEntryListeners() {
317  1334 log.debug("Registering blog entry listeners");
318   
319  1334 for (String className : getBlogEntryListeners()) {
320  1334 try {
321  1334 Class c = Class.forName(className.trim());
322  1334 BlogEntryListener listener = (BlogEntryListener) c.newInstance();
323  1334 eventListenerList.addBlogEntryListener(listener);
324    } catch (Exception e) {
325  0 error("Could not start blog entry listener \"" + className + "\" - check the class name is correct on the <a href=\"viewPlugins.secureaction#blogEntryListeners\">plugins page</a>.");
326  0 log.error("Blog entry listener " + className + " could not be registered", e);
327    }
328    }
329   
330    // these are required to keep the various indexes up to date
331  1334 eventListenerList.addBlogEntryListener(new BlogEntryIndexListener());
332  1334 eventListenerList.addBlogEntryListener(new TagIndexListener());
333  1334 eventListenerList.addBlogEntryListener(new CategoryIndexListener());
334  1334 eventListenerList.addBlogEntryListener(new AuthorIndexListener());
335  1334 eventListenerList.addBlogEntryListener(new SearchIndexListener());
336  1334 eventListenerList.addBlogEntryListener(new AuditListener());
337  1334 try {
338  1334 eventListenerList.addBlogEntryListener(new EmailSubscriptionListener());
339    } catch (Throwable t) {
340  0 final String text = "Error while starting e-mail subscription listener - add mail.jar and activation.jar to the server classpath if you want to enable this listener.";
341  0 warn(text);
342  0 if(t instanceof NoClassDefFoundError &&
343    t.getMessage() != null &&
344    t.getMessage().indexOf("javax/mail/Session") > -1) {
345  0 log.warn(text); // consider exception already handled well...
346    } else {
347  0 log.warn(text, t);
348    }
349    }
350    }
351   
352    /**
353    * Initialises any comment listeners configured for this blog.
354    */
 
355  1334 toggle private void initCommentListeners() {
356  1334 log.debug("Registering comment listeners");
357   
358  1334 for (String className : getCommentListeners()) {
359  6670 try {
360  6670 Class c = Class.forName(className.trim());
361  6670 CommentListener listener = (CommentListener) c.newInstance();
362  6670 eventListenerList.addCommentListener(listener);
363    } catch (Exception e) {
364  0 error("Could not start comment listener \"" + className + "\" - check the class name is correct on the <a href=\"viewPlugins.secureaction#commentListeners\">plugins page</a>.");
365  0 log.error("Comment listener " + className + " could not be registered", e);
366    }
367    }
368   
369  1334 eventListenerList.addCommentListener(new ResponseIndexListener());
370  1334 eventListenerList.addCommentListener(new AuditListener());
371    }
372   
373    /**
374    * Initialises any TrackBack listeners configured for this blog.
375    */
 
376  1334 toggle private void initTrackBackListeners() {
377  1334 log.debug("Registering TrackBack listeners");
378   
379  1334 for (String className : getTrackBackListeners()) {
380  6670 try {
381  6670 Class c = Class.forName(className.trim());
382  6670 TrackBackListener listener = (TrackBackListener) c.newInstance();
383  6670 eventListenerList.addTrackBackListener(listener);
384    } catch (Exception e) {
385  0 error("Could not start TrackBack listener \"" + className + "\" - check the class name is correct on the <a href=\"viewPlugins.secureaction#trackbackListeners\">plugins page</a>.");
386  0 log.error("TrackBack listener " + className + " could not be registered", e);
387    }
388    }
389   
390  1334 eventListenerList.addTrackBackListener(new ResponseIndexListener());
391  1334 eventListenerList.addTrackBackListener(new AuditListener());
392    }
393   
394    /**
395    * Initialises any content decorators configufred for this blog.
396    */
 
397  1334 toggle private void initDecorators() {
398  1334 log.debug("Registering decorators");
399   
400  1334 decoratorChain.add(new HideUnapprovedResponsesDecorator());
401   
402  1334 for (String className : getContentDecorators()) {
403  8004 try {
404  8004 Class c = Class.forName(className.trim());
405  8004 ContentDecorator decorator = (ContentDecorator) c.newInstance();
406  8004 decorator.setBlog(this);
407  8004 decoratorChain.add(decorator);
408    } catch (Exception e) {
409  0 error("Could not start decorator \"" + className + "\" - check the class name is correct on the <a href=\"viewPlugins.secureaction#contentDecorators\">plugins page</a>.");
410  0 e.printStackTrace();
411  0 log.error(className + " could not be started", e);
412    }
413    }
414    }
415   
416    /**
417    * Gets the default properties for a Blog.
418    *
419    * @return a Properties instance
420    */
 
421  1334 toggle protected Properties getDefaultProperties() {
422  1334 Properties defaultProperties = new Properties();
423  1334 defaultProperties.setProperty(NAME_KEY, "My blog");
424  1334 defaultProperties.setProperty(DESCRIPTION_KEY, "");
425  1334 defaultProperties.setProperty(IMAGE_KEY, "");
426  1334 defaultProperties.setProperty(AUTHOR_KEY, "Blog Owner");
427  1334 defaultProperties.setProperty(EMAIL_KEY, "blog@yourdomain.com");
428  1334 defaultProperties.setProperty(TIMEZONE_KEY, "Europe/London");
429  1334 defaultProperties.setProperty(LANGUAGE_KEY, "en");
430  1334 defaultProperties.setProperty(COUNTRY_KEY, "GB");
431  1334 defaultProperties.setProperty(CHARACTER_ENCODING_KEY, "UTF-8");
432  1334 defaultProperties.setProperty(RECENT_BLOG_ENTRIES_ON_HOME_PAGE_KEY, "3");
433  1334 defaultProperties.setProperty(RECENT_RESPONSES_ON_HOME_PAGE_KEY, "3");
434  1334 defaultProperties.setProperty(THEME_KEY, "default");
435  1334 defaultProperties.setProperty(PRIVATE_KEY, FALSE);
436  1334 defaultProperties.setProperty(LUCENE_ANALYZER_KEY, "org.apache.lucene.analysis.SimpleAnalyzer");
437  1334 defaultProperties.setProperty(CONTENT_DECORATORS_KEY,
438    "net.sourceforge.pebble.decorator.RadeoxDecorator\n" +
439    "net.sourceforge.pebble.decorator.HtmlDecorator\n" +
440    "net.sourceforge.pebble.decorator.EscapeMarkupDecorator\n" +
441    "net.sourceforge.pebble.decorator.RelativeUriDecorator\n" +
442    "net.sourceforge.pebble.decorator.ReadMoreDecorator\n" +
443    "net.sourceforge.pebble.decorator.BlogTagsDecorator");
444  1334 defaultProperties.setProperty(BLOG_ENTRY_LISTENERS_KEY,
445    "net.sourceforge.pebble.event.blogentry.XmlRpcNotificationListener");
446  1334 defaultProperties.setProperty(COMMENT_LISTENERS_KEY,
447    "net.sourceforge.pebble.event.response.IpAddressListener\r\n" +
448    "net.sourceforge.pebble.event.response.LinkSpamListener\r\n" +
449    "net.sourceforge.pebble.event.response.ContentSpamListener\r\n" +
450    "net.sourceforge.pebble.event.response.SpamScoreListener\r\n" +
451    "net.sourceforge.pebble.event.response.MarkApprovedWhenAuthenticatedListener\r\n" +
452    "#net.sourceforge.pebble.event.response.DeleteRejectedListener\r\n" +
453    "#net.sourceforge.pebble.event.comment.EmailAuthorNotificationListener");
454  1334 defaultProperties.setProperty(TRACKBACK_LISTENERS_KEY,
455    "net.sourceforge.pebble.event.response.IpAddressListener\r\n" +
456    "net.sourceforge.pebble.event.response.LinkSpamListener\r\n" +
457    "net.sourceforge.pebble.event.response.ContentSpamListener\r\n" +
458    "net.sourceforge.pebble.event.response.SpamScoreListener\r\n" +
459    "net.sourceforge.pebble.event.response.MarkApprovedWhenAuthenticatedListener\r\n" +
460    "#net.sourceforge.pebble.event.response.DeleteRejectedListener\r\n" +
461    "#net.sourceforge.pebble.event.trackback.EmailAuthorNotificationListener");
462  1334 defaultProperties.setProperty(PERMALINK_PROVIDER_KEY, "net.sourceforge.pebble.permalink.DefaultPermalinkProvider");
463  1334 defaultProperties.setProperty(EVENT_DISPATCHER_KEY, "net.sourceforge.pebble.event.DefaultEventDispatcher");
464  1334 defaultProperties.setProperty(LOGGER_KEY, "net.sourceforge.pebble.logging.CombinedLogFormatLogger");
465  1334 defaultProperties.setProperty(COMMENT_CONFIRMATION_STRATEGY_KEY, "net.sourceforge.pebble.confirmation.DefaultConfirmationStrategy");
466  1334 defaultProperties.setProperty(TRACKBACK_CONFIRMATION_STRATEGY_KEY, "net.sourceforge.pebble.confirmation.DefaultConfirmationStrategy");
467  1334 defaultProperties.setProperty(RICH_TEXT_EDITOR_FOR_COMMENTS_ENABLED_KEY, "true");
468   
469  1334 return defaultProperties;
470    }
471   
472    /**
473    * Gets the ID of this blog.
474    *
475    * @return the ID as a String
476    */
 
477  8594 toggle public String getId() {
478  8594 return this.id;
479    }
480   
481    /**
482    * Sets the ID of this blog.
483    *
484    * @param id the ID as a String
485    */
 
486  136 toggle public void setId(String id) {
487  136 this.id = id;
488    }
489   
490    /**
491    * Gets the URL where this blog is deployed.
492    *
493    * @return a URL as a String
494    */
 
495  568 toggle public String getUrl() {
496  568 Configuration config = PebbleContext.getInstance().getConfiguration();
497  568 String url = config.getUrl();
498   
499  568 if (url == null || url.length() == 0) {
500  0 return "";
501  568 } else if (BlogManager.getInstance().isMultiBlog()) {
502  62 if (config.isVirtualHostingEnabled()) {
503  0 return url.substring(0, url.indexOf("://")+3) + getId() + "." + url.substring(url.indexOf("://")+3);
504    } else {
505  62 return url + getId() + "/";
506    }
507    } else {
508  506 return url;
509    }
510    }
511   
512    /**
513    * Gets the relative URL where this blog is deployed.
514    *
515    * @return a URL as a String
516    */
 
517  0 toggle public String getRelativeUrl() {
518  0 if (BlogManager.getInstance().isMultiBlog()) {
519  0 return "/" + getId() + "/";
520    } else {
521  0 return "/";
522    }
523    }
524   
525    /**
526    * Gets the about description of this blog.
527    *
528    * @return a String
529    */
 
530  0 toggle public String getAbout() {
531  0 return properties.getProperty(ABOUT_KEY);
532    }
533   
534    /**
535    * Gets the home page to be used for this blog.
536    *
537    * @return a String
538    */
 
539  0 toggle public String getHomePage() {
540  0 return properties.getProperty(HOME_PAGE_KEY);
541    }
542   
543    /**
544    * Gets the e-mail address of the blog owner.
545    *
546    * @return the e-mail address
547    */
 
548  20 toggle public String getEmail() {
549  20 return properties.getProperty(EMAIL_KEY);
550    }
551   
552    /**
553    * Gets a Collection of e-mail addresses.
554    *
555    * @return a Collection of String instances
556    */
 
557  14 toggle public Collection getEmailAddresses() {
558  14 return Arrays.asList(getEmail().split(","));
559    }
560   
561    /**
562    * Gets the first of multiple e-mail addresses.
563    *
564    * @return the firt e-mail address as a String
565    */
 
566  6 toggle public String getFirstEmailAddress() {
567  6 Collection emailAddresses = getEmailAddresses();
568  6 if (emailAddresses != null && !emailAddresses.isEmpty()) {
569  6 return (String)emailAddresses.iterator().next();
570    } else {
571  0 return "";
572    }
573    }
574   
575    /**
576    * Gets a comma separated list of the users that are blog owners
577    * for this blog.
578    *
579    * @return a String containng a comma separated list of user names
580    */
 
581  20 toggle public String getBlogOwnersAsString() {
582  20 return properties.getProperty(BLOG_OWNERS_KEY);
583    }
584   
585    /**
586    * Gets a list of the users that are blog owners for this blog.
587    *
588    * @return a String containng a comma separated list of user names
589    */
 
590  14 toggle public List<String> getBlogOwners() {
591  14 String commaSeparatedUsers = getBlogOwnersAsString();
592  14 List<String> users = new LinkedList<String>();
593  14 if (commaSeparatedUsers != null) {
594  8 StringTokenizer tok = new StringTokenizer(commaSeparatedUsers, ",");
595  18 while (tok.hasMoreTokens()) {
596  10 users.add(tok.nextToken().trim());
597    }
598    }
599   
600  14 return users;
601    }
602   
603    /**
604    * Gets a comma separated list of the users that are blog publishers
605    * for this blog.
606    *
607    * @return a String containng a comma separated list of user names
608    */
 
609  0 toggle public String getBlogPublishersAsString() {
610  0 return properties.getProperty(BLOG_PUBLISHERS_KEY);
611    }
612   
613    /**
614    * Gets a list of the users that are blog publishers for this blog.
615    *
616    * @return a String containng a comma separated list of user names
617    */
 
618  0 toggle public List<String> getBlogPublishers() {
619  0 String commaSeparatedUsers = getBlogPublishersAsString();
620  0 List<String> users = new LinkedList<String>();
621  0 if (commaSeparatedUsers != null) {
622  0 StringTokenizer tok = new StringTokenizer(commaSeparatedUsers, ",");
623  0 while (tok.hasMoreTokens()) {
624  0 users.add(tok.nextToken().trim());
625    }
626    }
627   
628  0 return users;
629    }
630   
631    /**
632    * Gets a comma separated list of the users that are blog contributors
633    * for this blog.
634    *
635    * @return a String containng a comma separated list of user names
636    */
 
637  164 toggle public String getBlogContributorsAsString() {
638  164 return properties.getProperty(BLOG_CONTRIBUTORS_KEY);
639    }
640   
641    /**
642    * Gets a list of the users that are blog contributors for this blog.
643    *
644    * @return a String containng a comma separated list of user names
645    */
 
646  156 toggle public List<String> getBlogContributors() {
647  156 String commaSeparatedUsers = getBlogContributorsAsString();
648  156 List<String> users = new LinkedList<String>();
649  156 if (commaSeparatedUsers != null) {
650  134 StringTokenizer tok = new StringTokenizer(commaSeparatedUsers, ",");
651  272 while (tok.hasMoreTokens()) {
652  138 users.add(tok.nextToken().trim());
653    }
654    }
655   
656  156 return users;
657    }
658   
659    /**
660    * Gets a comma separated list of the users that are blog readers
661    * for this blog.
662    *
663    * @return a String containng a comma separated list of user names
664    */
 
665  0 toggle public String getBlogReadersAsString() {
666  0 return properties.getProperty(BLOG_READERS_KEY);
667    }
668   
669    /**
670    * Gets a list of the users that are blog readers for this blog.
671    *
672    * @return a String containng a comma separated list of user names
673    */
 
674  0 toggle public List<String> getBlogReaders() {
675  0 String commaSeparatedUsers = getBlogReadersAsString();
676  0 List<String> users = new LinkedList<String>();
677  0 if (commaSeparatedUsers != null) {
678  0 StringTokenizer tok = new StringTokenizer(commaSeparatedUsers, ",");
679  0 while (tok.hasMoreTokens()) {
680  0 users.add(tok.nextToken().trim());
681    }
682    }
683   
684  0 return users;
685    }
686   
687    /**
688    * Gets the name of the Lucene analyzer to use.
689    *
690    * @return a fully qualified class name
691    */
 
692  206 toggle public String getLuceneAnalyzer() {
693  206 return properties.getProperty(LUCENE_ANALYZER_KEY);
694    }
695   
696    /**
697    * Gets the name of the logger in use.
698    *
699    * @return a fully qualified class name
700    */
 
701  1336 toggle public String getLoggerName() {
702  1336 return properties.getProperty(LOGGER_KEY);
703    }
704   
705    /**
706    * Gets a Collection containing the names of users that are blog owners
707    * for this blog.
708    *
709    * @return a Collection containng user names as Strings
710    */
 
711  170 toggle public Collection getUsersInRole(String roleName) {
712  170 List<String> users = new LinkedList<String>();
713   
714  170 if (roleName.equals(Constants.BLOG_OWNER_ROLE)) {
715  14 users = getBlogOwners();
716  156 } else if (roleName.equals(Constants.BLOG_PUBLISHER_ROLE)) {
717  0 users = getBlogPublishers();
718  156 } else if (roleName.equals(Constants.BLOG_CONTRIBUTOR_ROLE)) {
719  156 users = getBlogContributors();
720  0 } else if (roleName.equals(Constants.BLOG_READER_ROLE)) {
721  0 users = getBlogReaders();
722    }
723   
724  170 return users;
725    }
726   
727    /**
728    * Determines whether the specified user is in the specified role.
729    *
730    * @param roleName the name of the role
731    * @param user the name of the user
732    * @return true if the user is a member of the role or the list of users
733    * is empty, false otherwise
734    */
 
735  156 toggle public boolean isUserInRole(String roleName, String user) {
736  156 Collection users = getUsersInRole(roleName);
737  156 if (users.isEmpty() || users.contains(user)) {
738  148 return true;
739    }
740   
741  8 return false;
742    }
743   
744    /**
745    * Gets the Year instance for the specified year.
746    *
747    * @param year the year as an int (e.g. 2003)
748    * @return a Year instance
749    */
 
750  878 toggle public Year getBlogForYear(int year) {
751  878 Iterator it = years.iterator();
752  878 Year y;
753  926 while (it.hasNext()) {
754  594 y = (Year)it.next();
755  594 if (y.getYear() == year) {
756  546 return y;
757    }
758    }
759   
760  332 y = new Year(this, year);
761  332 years.add(y);
762  332 Collections.sort(years);
763   
764  332 return y;
765    }
766   
767    /**
768    * Gets the Year instance representing this year.
769    *
770    * @return a Year instance for this year
771    */
 
772  26 toggle public Year getBlogForThisYear() {
773  26 Calendar cal = getCalendar();
774  26 return getBlogForYear(cal.get(Calendar.YEAR));
775    }
776   
777    /**
778    * Gets all Years managed by this root blog.
779    *
780    * @return a Collection of Year instances
781    */
 
782  0 toggle public List getYears() {
783  0 return years;
784    }
785   
786    /**
787    * Gets all Years managed by this root blog, in reverse order.
788    *
789    * @return a Collection of Year instances
790    */
 
791  0 toggle public List<Year> getArchives() {
792  0 List<Year> list = new LinkedList<Year>();
793  0 int firstYear = getBlogForFirstMonth().getYear().getYear();
794  0 int thisYear = getBlogForThisYear().getYear();
795    // only add years that are in range
796  0 Calendar cal = getCalendar();
797  0 for (Year year : years) {
798  0 if (year.getYear() >= firstYear && year.getYear() <= thisYear) {
799  0 list.add(year);
800    }
801    }
802  0 Collections.reverse(list);
803  0 return list;
804    }
805   
806    /**
807    * Gets the Month instance representing the first month that
808    * contains blog entries.
809    *
810    * @return a Month instance
811    */
 
812  10 toggle public Month getBlogForFirstMonth() {
813  10 if (getBlogEntryIndex() == null) {
814  0 return getBlogForThisMonth();
815    }
816   
817  10 List<String> blogEntryIds = getBlogEntryIndex().getBlogEntries();
818  10 if (blogEntryIds == null || blogEntryIds.isEmpty()) {
819  4 return getBlogForThisMonth();
820    }
821   
822  6 String firstBlogEntryId = blogEntryIds.get(blogEntryIds.size()-1);
823  6 if (firstBlogEntryId == null) {
824  0 return getBlogForThisMonth();
825    }
826   
827  6 long dateInMillis = Long.parseLong(firstBlogEntryId);
828  6 Date date = new Date(dateInMillis);
829  6 return getBlogForDay(date).getMonth();
830    }
831   
832    /**
833    * Gets a Day intance for the specified Date.
834    *
835    * @param date a java.util.Date instance
836    * @return a Day instance representing the specified Date
837    */
 
838  466 toggle public Day getBlogForDay(Date date) {
839  466 Calendar cal = getCalendar();
840  466 cal.setTime(date);
841   
842  466 int year = cal.get(Calendar.YEAR);
843  466 int month = (cal.get(Calendar.MONTH) + 1);
844  466 int day = cal.get(Calendar.DAY_OF_MONTH);
845   
846  466 return getBlogForDay(year, month, day);
847    }
848   
849    /**
850    * Gets the Day instance for today.
851    *
852    * @return a Day instance
853    */
 
854  24 toggle public Day getBlogForToday() {
855  24 return this.getBlogForDay(getCalendar().getTime());
856    }
857   
858    /**
859    * Gets a Day intance for the specified year, month and day.
860    *
861    * @param year the year as an int
862    * @param month the month as an int
863    * @param day the day as an int
864    * @return a Day instance representing the specified year, month and day
865    */
 
866  516 toggle public Day getBlogForDay(int year, int month, int day) {
867  516 return getBlogForMonth(year, month).getBlogForDay(day);
868    }
869   
870    /**
871    * Gets a Month intance for the specified year and month.
872    *
873    * @param year the year as an int
874    * @param month the month as an int
875    * @return a Month instance representing the specified year and month
876    */
 
877  830 toggle public Month getBlogForMonth(int year, int month) {
878  830 return getBlogForYear(year).getBlogForMonth(month);
879    }
880   
881    /**
882    * Gets the Month instance representing this month.
883    *
884    * @return a Month instance for this month
885    */
 
886  12 toggle public Month getBlogForThisMonth() {
887  12 Calendar cal = getCalendar();
888  12 return getBlogForMonth(cal.get(Calendar.YEAR), (cal.get(Calendar.MONTH) + 1));
889    }
890   
891    /**
892    * Given a Year, this method returns the Year instance
893    * representing the previous year.
894    *
895    * @param year a Year instance
896    * @return a Year representing the previous year
897    */
 
898  4 toggle public Year getBlogForPreviousYear(Year year) {
899  4 return getBlogForYear(year.getYear() - 1);
900    }
901   
902    /**
903    * Given a Year, this method returns the Year instance
904    * representing the next year.
905    *
906    * @param year a Year instance
907    * @return a Year representing the next year
908    */
 
909  4 toggle public Year getBlogForNextYear(Year year) {
910  4 return getBlogForYear(year.getYear() + 1);
911    }
912   
913    /**
914    * Gets all blog entries for this blog.
915    *
916    * @return a List of BlogEntry objects
917    */
 
918  22 toggle public List getBlogEntries() {
919  22 List blogEntries = new ArrayList();
920  22 BlogService service = new BlogService();
921   
922  44 for (int year = years.size()-1; year >= 0; year--) {
923  22 Year y = (Year)years.get(year);
924  22 Month[] months = y.getMonths();
925  286 for (int month = 11; month >= 0; month--) {
926  264 try {
927  264 blogEntries.addAll(service.getBlogEntries(this, y.getYear(), months[month].getMonth()));
928    } catch (BlogServiceException e) {
929  0 log.error(e);
930    }
931    }
932    }
933   
934  22 return blogEntries;
935    }
936   
937    /**
938    * Gets all unpublished blog entries for this blog.
939    *
940    * @return a List of BlogEntry objects
941    */
 
942  0 toggle public List<BlogEntry> getUnpublishedBlogEntries() {
943  0 List<BlogEntry> blogEntries = new ArrayList<BlogEntry>();
944  0 BlogService service = new BlogService();
945   
946  0 List<String> blogEntryIds = blogEntryIndex.getUnpublishedBlogEntries();
947  0 for (String blogEntryId : blogEntryIds) {
948  0 try {
949  0 blogEntries.add(service.getBlogEntry(this, blogEntryId));
950    } catch (BlogServiceException e) {
951  0 log.error(e);
952    }
953    }
954   
955  0 return blogEntries;
956    }
957   
958    /**
959    * Gets the number of blog entries for this blog.
960    *
961    * @return an int
962    */
 
963  16 toggle public int getNumberOfBlogEntries() {
964  16 return blogEntryIndex.getNumberOfBlogEntries();
965    }
966   
967    /**
968    * Gets the number of published blog entries for this blog.
969    *
970    * @return an int
971    */
 
972  0 toggle public int getNumberOfPublishedBlogEntries() {
973  0 return blogEntryIndex.getNumberOfPublishedBlogEntries();
974    }
975   
976    /**
977    * Gets the number of unpublished blog entries for this blog.
978    *
979    * @return an int
980    */
 
981  0 toggle public int getNumberOfUnpublishedBlogEntries() {
982  0 return blogEntryIndex.getNumberOfUnpublishedBlogEntries();
983    }
984   
985    /**
986    * Gets the number of static pages for this blog.
987    *
988    * @return an int
989    */
 
990  0 toggle public int getNumberOfStaticPages() {
991  0 return staticPageIndex.getNumberOfStaticPages();
992    }
993   
994    /**
995    * Gets the most recent blog entries, the number
996    * of which is specified.
997    *
998    * @param numberOfEntries the number of entries to get
999    * @return a List containing the most recent blog entries
1000    */
 
1001  34 toggle public List getRecentBlogEntries(int numberOfEntries) {
1002  34 BlogService service = new BlogService();
1003  34 List<String> blogEntryIds = blogEntryIndex.getBlogEntries();
1004  34 List blogEntries = new ArrayList();
1005  34 for (String blogEntryId : blogEntryIds) {
1006  34 try {
1007  34 BlogEntry blogEntry = service.getBlogEntry(this, blogEntryId);
1008  34 blogEntries.add(blogEntry);
1009    } catch (BlogServiceException e) {
1010  0 log.error(e);
1011    }
1012   
1013  34 if (blogEntries.size() == numberOfEntries) {
1014  14 break;
1015    }
1016    }
1017   
1018  34 return blogEntries;
1019    }
1020   
1021    /**
1022    * Gets the most recent published blog entries, the number of which
1023    * is taken from the recentBlogEntriesOnHomePage property.
1024    *
1025    * @return a List containing the most recent blog entries
1026    */
 
1027  20 toggle public List getRecentPublishedBlogEntries() {
1028  20 return getRecentPublishedBlogEntries(getRecentBlogEntriesOnHomePage());
1029    }
1030   
1031    /**
1032    * Gets the most recent published blog entries, the number of which
1033    * is specified
1034    *
1035    * @param number the number of blog entries to get
1036    * @return a List containing the most recent blog entries
1037    */
 
1038  48 toggle public List getRecentPublishedBlogEntries(int number) {
1039  48 BlogService service = new BlogService();
1040  48 List<String> blogEntryIds = blogEntryIndex.getPublishedBlogEntries();
1041  48 List blogEntries = new ArrayList();
1042  48 for (String blogEntryId : blogEntryIds) {
1043  12 if (blogEntries.size() == number) {
1044  2 break;
1045    }
1046   
1047  10 try {
1048  10 BlogEntry blogEntry = service.getBlogEntry(this, blogEntryId);
1049  10 if (blogEntry != null) {
1050  10 blogEntries.add(blogEntry);
1051    }
1052    } catch (BlogServiceException e) {
1053  0 log.error(e);
1054    }
1055    }
1056   
1057  48 return blogEntries;
1058    }
1059   
1060    /**
1061    * Gets blog entries for a given list of IDs.
1062    *
1063    * @param blogEntryIds the list of blog entry IDs
1064    * @return a List containing the blog entries
1065    */
 
1066  2 toggle public List<BlogEntry> getBlogEntries(List<String> blogEntryIds) {
1067  2 BlogService service = new BlogService();
1068  2 List<BlogEntry> blogEntries = new LinkedList<BlogEntry>();
1069  2 for (String blogEntryId : blogEntryIds) {
1070  0 try {
1071  0 BlogEntry blogEntry = service.getBlogEntry(this, blogEntryId);
1072  0 if (blogEntry != null) {
1073  0 blogEntries.add(blogEntry);
1074    }
1075    } catch (BlogServiceException e) {
1076  0 log.error(e);
1077    }
1078    }
1079   
1080  2 return blogEntries;
1081    }
1082   
1083    /**
1084    * Gets the most recent published blog entries for a given category, the
1085    * number of which is taken from the recentBlogEntriesOnHomePage property.
1086    *
1087    * @param category a category
1088    * @return a List containing the most recent blog entries
1089    */
 
1090  2 toggle public List getRecentPublishedBlogEntries(Category category) {
1091  2 BlogService service = new BlogService();
1092  2 List<String> blogEntryIds = categoryIndex.getRecentBlogEntries(category);
1093  2 List blogEntries = new ArrayList();
1094  2 for (String blogEntryId : blogEntryIds) {
1095  2 try {
1096  2 BlogEntry blogEntry = service.getBlogEntry(this, blogEntryId);
1097  2 if (blogEntry != null && blogEntry.isPublished()) {
1098  2 blogEntries.add(blogEntry);
1099    }
1100    } catch (BlogServiceException e) {
1101  0 log.error(e);
1102    }
1103   
1104  2 if (blogEntries.size() == getRecentBlogEntriesOnHomePage()) {
1105  0 break;
1106    }
1107    }
1108   
1109  2 return blogEntries;
1110    }
1111   
1112    /**
1113    * Gets the most recent published blog entries for a given category, the
1114    * number of which is taken from the recentBlogEntriesOnHomePage property.
1115    *
1116    * @param author the author's username
1117    * @return a List containing the most recent blog entries
1118    */
 
1119  0 toggle public List getRecentPublishedBlogEntries(String author) {
1120  0 BlogService service = new BlogService();
1121  0 List<String> blogEntryIds = authorIndex.getRecentBlogEntries(author);
1122  0 List blogEntries = new ArrayList();
1123  0 for (String blogEntryId : blogEntryIds) {
1124  0 try {
1125  0 BlogEntry blogEntry = service.getBlogEntry(this, blogEntryId);
1126  0 if (blogEntry != null && blogEntry.isPublished()) {
1127  0 blogEntries.add(blogEntry);
1128    }
1129    } catch (BlogServiceException e) {
1130  0 log.error(e);
1131    }
1132   
1133  0 if (blogEntries.size() == getRecentBlogEntriesOnHomePage()) {
1134  0 break;
1135    }
1136    }
1137   
1138  0 return blogEntries;
1139    }
1140   
1141    /**
1142    * Gets the most recent published blog entries for a given tag, the
1143    * number of which is taken from the recentBlogEntriesOnHomePage property.
1144    *
1145    * @param tag a tag
1146    * @return a List containing the most recent blog entries
1147    */
 
1148  0 toggle public List getRecentPublishedBlogEntries(Tag tag) {
1149  0 BlogService service = new BlogService();
1150  0 List<String> blogEntryIds = tagIndex.getRecentBlogEntries(tag);
1151  0 List blogEntries = new ArrayList();
1152  0 for (String blogEntryId : blogEntryIds) {
1153  0 try {
1154  0 BlogEntry blogEntry = service.getBlogEntry(this, blogEntryId);
1155  0 if (blogEntry != null && blogEntry.isPublished()) {
1156  0 blogEntries.add(blogEntry);
1157    }
1158    } catch (BlogServiceException e) {
1159  0 log.error(e);
1160    }
1161   
1162  0 if (blogEntries.size() == getRecentBlogEntriesOnHomePage()) {
1163  0 break;
1164    }
1165    }
1166   
1167  0 return blogEntries;
1168    }
1169   
1170    /**
1171    * Gets the most recent responses.
1172    *
1173    * @return a List containing the most recent blog entries
1174    */
 
1175  2 toggle public List<Response> getRecentApprovedResponses() {
1176  2 BlogService service = new BlogService();
1177  2 List<String> responseIds = responseIndex.getApprovedResponses();
1178  2 List<Response> responses = new ArrayList<Response>();
1179  2 for (String responseId : responseIds) {
1180  0 try {
1181  0 Response response = service.getResponse(this, responseId);
1182  0 if (response != null && response.getBlogEntry().isPublished()) {
1183  0 responses.add(response);
1184    }
1185    } catch (BlogServiceException e) {
1186  0 log.error(e);
1187    }
1188   
1189  0 if (responses.size() == getRecentResponsesOnHomePage()) {
1190  0 break;
1191    }
1192    }
1193   
1194  2 return responses;
1195    }
1196   
1197    /**
1198    * Gets the list of approved responses.
1199    *
1200    * @return a List of response IDs
1201    */
 
1202  4 toggle public List<String> getApprovedResponses() {
1203  4 return responseIndex.getApprovedResponses();
1204    }
1205   
1206    /**
1207    * Gets the list of pending responses.
1208    *
1209    * @return a List of response IDs
1210    */
 
1211  0 toggle public List<String> getPendingResponses() {
1212  0 return responseIndex.getPendingResponses();
1213    }
1214   
1215    /**
1216    * Gets the list of rejected responses.
1217    *
1218    * @return a List of response IDs
1219    */
 
1220  0 toggle public List<String> getRejectedResponses() {
1221  0 return responseIndex.getRejectedResponses();
1222    }
1223   
1224    /**
1225    * Gets the number of responses.
1226    *
1227    * @return the number of responses
1228    */
 
1229  0 toggle public int getNumberOfResponses() {
1230  0 return responseIndex.getNumberOfResponses();
1231    }
1232   
1233    /**
1234    * Gets the number of approved responses.
1235    *
1236    * @return the number of approved responses
1237    */
 
1238  0 toggle public int getNumberOfApprovedResponses() {
1239  0 return responseIndex.getNumberOfApprovedResponses();
1240    }
1241   
1242    /**
1243    * Gets the number of pending responses.
1244    *
1245    * @return the number of pending responses
1246    */
 
1247  0 toggle public int getNumberOfPendingResponses() {
1248  0 return responseIndex.getNumberOfPendingResponses();
1249    }
1250   
1251    /**
1252    * Gets the number of rejected responses.
1253    *
1254    * @return the number of rejected responses
1255    */
 
1256  0 toggle public int getNumberOfRejectedResponses() {
1257  0 return responseIndex.getNumberOfRejectedResponses();
1258    }
1259   
1260    /**
1261    * Gets the date that this blog was last updated through the addition
1262    * of a blog entry.
1263    *
1264    * @return a Date instance representing the time of the most recent entry
1265    */
 
1266  28 toggle public Date getLastModified() {
1267  28 Date date = new Date(0);
1268  28 List blogEntries = getRecentPublishedBlogEntries(1);
1269  28 if (blogEntries.size() == 1) {
1270  6 date = ((BlogEntry)blogEntries.get(0)).getDate();
1271    }
1272   
1273  28 return date;
1274    }
1275   
1276    /**
1277    * Gets the date of the most recent response.
1278    *
1279    * @return a Date instance representing the time of the most recent entry
1280    */
 
1281  0 toggle public Date getDateOfLastResponse() {
1282  0 List<Response> responses = this.getRecentApprovedResponses();
1283  0 if (responses != null && responses.size() > 0) {
1284  0 return responses.get(0).getDate();
1285    } else {
1286  0 return new Date(0);
1287    }
1288    }
1289   
 
1290  6 toggle public BlogEntry getPreviousBlogEntry(BlogEntry blogEntry) {
1291  6 Day firstDay = getBlogForFirstMonth().getBlogForFirstDay();
1292  6 Day day = getBlogForDay(blogEntry.getDate());
1293   
1294  6 String blogEntryId = day.getPreviousBlogEntry(blogEntry.getId());
1295  28 while (day != firstDay && blogEntryId == null) {
1296  22 day = day.getPreviousDay();
1297  22 blogEntryId = day.getLastBlogEntry();
1298    }
1299   
1300  6 if (blogEntryId != null) {
1301  4 BlogService service = new BlogService();
1302  4 try {
1303  4 BlogEntry previousBlogEntry = service.getBlogEntry(this, blogEntryId);
1304  4 return previousBlogEntry;
1305    } catch (BlogServiceException e) {
1306    // do nothing
1307    }
1308    }
1309   
1310  2 return null;
1311    }
1312   
 
1313  6 toggle public BlogEntry getNextBlogEntry(BlogEntry blogEntry) {
1314  6 Day lastDay = getBlogForToday();
1315  6 Day day = getBlogForDay(blogEntry.getDate());
1316   
1317  6 String blogEntryId = day.getNextBlogEntry(blogEntry.getId());
1318  10 while (day != lastDay && blogEntryId == null) {
1319  4 day = day.getNextDay();
1320  4 blogEntryId = day.getFirstBlogEntry();
1321    }
1322   
1323  6 if (blogEntryId != null) {
1324  4 BlogService service = new BlogService();
1325  4 try {
1326  4 BlogEntry nextBlogEntry = service.getBlogEntry(this, blogEntryId);
1327  4 return nextBlogEntry;
1328    } catch (BlogServiceException e) {
1329    // do nothing
1330    }
1331    }
1332   
1333  2 return null;
1334    }
1335   
1336    /**
1337    * Gets the categories associated with this blog.
1338    *
1339    * @return a List of Category instances
1340    */
 
1341  188 toggle public List<Category> getCategories() {
1342  188 CategoryBuilder builder = new CategoryBuilder(this, rootCategory);
1343  188 return builder.getCategories();
1344    }
1345   
1346    /**
1347    * Gets a specific category.
1348    *
1349    * @return a Category instance
1350    */
 
1351  168 toggle public Category getCategory(String id) {
1352  168 CategoryBuilder builder = new CategoryBuilder(this, rootCategory);
1353  168 return builder.getCategory(id);
1354    }
1355   
1356    /**
1357    * Gets the root category for this blog.
1358    *
1359    * @return a Category instance
1360    */
 
1361  724 toggle public Category getRootCategory() {
1362  724 return this.rootCategory;
1363    }
1364   
1365    /**
1366    * Sets the root category for this blog.
1367    *
1368    * @param category a Category instance
1369    */
 
1370  2 toggle public void setRootCategory(Category category) {
1371  2 this.rootCategory = category;
1372    }
1373   
1374    /**
1375    * Adds a category.
1376    *
1377    * @param category the Category to be added
1378    */
 
1379  64 toggle public synchronized void addCategory(Category category) {
1380  64 if (getCategory(category.getId()) == null) {
1381  62 CategoryBuilder builder = new CategoryBuilder(this, rootCategory);
1382  62 builder.addCategory(category);
1383    }
1384    }
1385   
1386    /**
1387    * Removes a category.
1388    *
1389    * @param category the Category to be removed
1390    */
 
1391  0 toggle public synchronized void removeCategory(Category category) {
1392  0 if (getCategory(category.getId()) != null) {
1393  0 CategoryBuilder builder = new CategoryBuilder(this, rootCategory);
1394  0 builder.removeCategory(category);
1395    }
1396    }
1397   
1398    /**
1399    * Gets the list of tags associated with this blog.
1400    */
 
1401  0 toggle public List<Tag> getTags() {
1402  0 return tagIndex.getTags();
1403    }
1404   
1405    /**
1406    * Gets the tag with the specified name.
1407    *
1408    * @param name the name as a String
1409    * @return a Tag instance
1410    */
 
1411  44 toggle public Tag getTag(String name) {
1412  44 return new Tag(name, this);
1413    }
1414   
1415    /**
1416    * Gets the object managing referer filters.
1417    *
1418    * @return a RefererFilterManager instance
1419    */
 
1420  0 toggle public RefererFilterManager getRefererFilterManager() {
1421  0 return this.refererFilterManager;
1422    }
1423   
1424    /**
1425    * Gets the search index.
1426    *
1427    * @return a BlogEntryIndex instance
1428    */
 
1429  120 toggle public SearchIndex getSearchIndex() {
1430  120 return this.searchIndex;
1431    }
1432   
1433    /**
1434    * Gets the blog entry index.
1435    *
1436    * @return a BlogEntryIndex instance
1437    */
 
1438  352 toggle public BlogEntryIndex getBlogEntryIndex() {
1439  352 return this.blogEntryIndex;
1440    }
1441   
1442    /**
1443    * Gets the response index.
1444    *
1445    * @return a ResponseIndex instance
1446    */
 
1447  242 toggle public ResponseIndex getResponseIndex() {
1448  242 return this.responseIndex;
1449    }
1450   
1451    /**
1452    * Gets the tag index.
1453    *
1454    * @return a TagIndex instance
1455    */
 
1456  122 toggle public TagIndex getTagIndex() {
1457  122 return this.tagIndex;
1458    }
1459   
1460    /**
1461    * Gets the category index.
1462    *
1463    * @return a CategoryIndex instance
1464    */
 
1465  96 toggle public CategoryIndex getCategoryIndex() {
1466  96 return this.categoryIndex;
1467    }
1468   
1469    /**
1470    * Gets the author index.
1471    *
1472    * @return a AuthorIndex instance
1473    */
 
1474  330 toggle public AuthorIndex getAuthorIndex() {
1475  330 return this.authorIndex;
1476    }
1477   
1478    /**
1479    * Gets the story index.
1480    *
1481    * @return a StaticPageIndex instance
1482    */
 
1483  0 toggle public StaticPageIndex getStaticPageIndex() {
1484  0 return this.staticPageIndex;
1485    }
1486   
1487    /**
1488    * Logs this request for blog.
1489    *
1490    * @param request the HttpServletRequest instance for this request
1491    */
 
1492  0 toggle public synchronized void log(HttpServletRequest request, int status) {
1493  0 String externalUri = (String)request.getAttribute(Constants.EXTERNAL_URI);
1494  0 if (externalUri.matches("/images/.+")) {
1495    // do nothing, we don't want to log the following types of requests
1496    // - a blog's images
1497    } else {
1498    // log the request
1499  0 logger.log(request, status);
1500    }
1501    }
1502   
1503    /**
1504    * Gets an object representing the editable theme.
1505    *
1506    * @return an EditableTheme instance
1507    */
 
1508  10 toggle public Theme getEditableTheme() {
1509  10 return editableTheme;
1510    }
1511   
1512    /**
1513    * Sets an object representing the editable theme.
1514    *
1515    * @param editableTheme an EditableTheme instance
1516    */
 
1517  1326 toggle public void setEditableTheme(Theme editableTheme) {
1518  1326 this.editableTheme = editableTheme;
1519    }
1520   
1521    /**
1522    * Gets the location where the blog files are stored.
1523    *
1524    * @return an absolute, local path on the filing system
1525    */
 
1526  1454 toggle public String getFilesDirectory() {
1527  1454 return getRoot() + File.separator + "files";
1528    }
1529   
1530    /**
1531    * Gets the location where the blog theme is stored.
1532    *
1533    * @return an absolute, local path on the filing system
1534    */
 
1535  4 toggle public String getThemeDirectory() {
1536  4 return getRoot() + File.separator + "theme";
1537    }
1538   
1539    /**
1540    * Gets the location where the plugin properties file is stored.
1541    *
1542    * @return an absolute, local path on the filing system
1543    */
 
1544  1404 toggle public String getPluginPropertiesFile() {
1545  1404 return getRoot() + File.separator + "plugin.properties";
1546    }
1547   
1548    /**
1549    * Determines whether this blog is public.
1550    *
1551    * @return true if public, false otherwise
1552    */
 
1553  22 toggle public boolean isPublic() {
1554  22 return properties.getProperty(PRIVATE_KEY).equalsIgnoreCase(FALSE);
1555    }
1556   
1557    /**
1558    * Determines whether this blog is private.
1559    *
1560    * @return true if public, false otherwise
1561    */
 
1562  2 toggle public boolean isPrivate() {
1563  2 return properties.getProperty(PRIVATE_KEY).equalsIgnoreCase(TRUE);
1564    }
1565   
1566    /**
1567    * Called to start (i.e. activate/initialise, restore the theme, etc) this
1568    * blog.
1569    */
 
1570  1320 toggle void start() {
1571  1320 log.debug("Starting blog with ID " + getId());
1572   
1573    // reindex the blog if the indexes don't exist
1574  1320 File indexes = new File(getIndexesDirectory());
1575  1320 if (!indexes.exists()) {
1576  0 indexes.mkdir();
1577  0 reindex();
1578    }
1579   
1580  1320 File imagesDirectory = new File(getImagesDirectory());
1581  1320 if (!imagesDirectory.exists()) {
1582  1318 imagesDirectory.mkdir();
1583    }
1584   
1585  1320 File filesDirectory = new File(getFilesDirectory());
1586  1320 if (!filesDirectory.exists()) {
1587  1318 filesDirectory.mkdir();
1588    }
1589   
1590  1320 File logDirectory = new File(getLogsDirectory());
1591  1320 if (!logDirectory.exists()) {
1592  1318 logDirectory.mkdir();
1593    }
1594   
1595  1320 logger.start();
1596  1320 editableTheme.restore();
1597   
1598    // call blog listeners
1599  1320 eventDispatcher.fireBlogEvent(new BlogEvent(this, BlogEvent.BLOG_STARTED));
1600  1320 log.info("Started blog with ID " + getId());
1601    }
1602   
1603    /**
1604    * Called to shutdown this blog.
1605    */
 
1606  1322 toggle void stop() {
1607  1322 log.debug("Stopping blog with ID " + getId());
1608   
1609  1322 logger.stop();
1610  1322 editableTheme.backup();
1611   
1612    // call blog listeners
1613  1322 eventDispatcher.fireBlogEvent(new BlogEvent(this, BlogEvent.BLOG_STOPPED));
1614  1322 log.info("Stopped blog with ID " + getId());
1615    }
1616   
1617    /**
1618    * Gets the logger associated with this blog.
1619    *
1620    * @return an AbstractLogger implementation
1621    */
 
1622  6 toggle public AbstractLogger getLogger() {
1623  6 return this.logger;
1624    }
1625   
1626    /**
1627    * Gets the list of plugins.
1628    *
1629    * @return a comma separated list of class names
1630    */
 
1631  1334 toggle public List<String> getContentDecorators() {
1632  1334 return getStringsFromProperty(CONTENT_DECORATORS_KEY);
1633    }
1634   
1635    /**
1636    * Gets the decorator manager associated with this blog.
1637    *
1638    * @return a BlogEntryDecoratorManager instance
1639    */
 
1640  26 toggle public ContentDecoratorChain getContentDecoratorChain() {
1641  26 return this.decoratorChain;
1642    }
1643   
1644    /**
1645    * Gets the list of blog listeners as strings.
1646    *
1647    * @return The list of class names
1648    */
 
1649  1334 toggle public List<String> getBlogListeners() {
1650  1334 return getStringsFromProperty(BLOG_LISTENERS_KEY);
1651    }
1652   
1653    /**
1654    * Gets the list of blog entry listeners as strings.
1655    *
1656    * @return The list of class names
1657    */
 
1658  1334 toggle public List<String> getBlogEntryListeners() {
1659  1334 return getStringsFromProperty(BLOG_ENTRY_LISTENERS_KEY);
1660    }
1661   
1662    /**
1663    * Gets the list of comment listeners as strings.
1664    *
1665    * @return The list of class names
1666    */
 
1667  1334 toggle public List<String> getCommentListeners() {
1668  1334 return getStringsFromProperty(COMMENT_LISTENERS_KEY);
1669    }
1670   
1671    /**
1672    * Gets the list of TrackBack listeners as strings.
1673    *
1674    * @return The list of class names
1675    */
 
1676  1334 toggle public List<String> getTrackBackListeners() {
1677  1334 return getStringsFromProperty(TRACKBACK_LISTENERS_KEY);
1678    }
1679   
1680    /**
1681    * Gets the name the event dispatcher.
1682    *
1683    * @return a String
1684    */
 
1685  1336 toggle public String getEventDispatcherName() {
1686  1336 return getProperty(EVENT_DISPATCHER_KEY);
1687    }
1688   
1689    /**
1690    * Gets the event dispatcher in use.
1691    *
1692    * @return an EventDispatcher implementation
1693    */
 
1694  374 toggle public EventDispatcher getEventDispatcher() {
1695  374 return this.eventDispatcher;
1696    }
1697   
1698    /**
1699    * Gets the event listsner list.
1700    */
 
1701  40 toggle public EventListenerList getEventListenerList() {
1702  40 return this.eventListenerList;
1703    }
1704   
 
1705  838 toggle public PluginProperties getPluginProperties() {
1706  838 return this.pluginProperties;
1707    }
1708   
1709    /**
1710    * Gets the name of the permalink provider.
1711    *
1712    * @return the fully qualified class name of the permalink provider
1713    */
 
1714  1336 toggle public String getPermalinkProviderName() {
1715  1336 return properties.getProperty(PERMALINK_PROVIDER_KEY);
1716    }
1717   
1718    /**
1719    * Gets the permalink provider in use.
1720    *
1721    * @return a PermalinkProvider instance
1722    */
 
1723  244 toggle public PermalinkProvider getPermalinkProvider() {
1724  244 return this.permalinkProvider;
1725    }
1726   
1727    /**
1728    * Sets the permalink provider in use.
1729    *
1730    * @param provider PermalinkProvider instance
1731    */
 
1732  1402 toggle public void setPermalinkProvider(PermalinkProvider provider) {
1733  1402 this.permalinkProvider = provider;
1734  1402 this.permalinkProvider.setBlog(this);
1735    }
1736   
 
1737  18 toggle public void reindex() {
1738  18 log.info("Reindexing blog with ID " + getId());
1739   
1740  18 reindexBlogEntries();
1741  18 reindexStaticPages();
1742    }
1743   
 
1744  18 toggle public void reindexBlogEntries() {
1745  18 blogEntryIndex.clear();
1746  18 responseIndex.clear();
1747  18 tagIndex.clear();
1748  18 categoryIndex.clear();
1749  18 authorIndex.clear();
1750  18 searchIndex.clear();
1751   
1752  18 try {
1753    // to reindex all blog entries, we need to load them via the DAO
1754  18 Collection<BlogEntry> blogEntries = DAOFactory.getConfiguredFactory().getBlogEntryDAO().loadBlogEntries(this);
1755  18 blogEntryIndex.index(blogEntries);
1756  18 responseIndex.index(blogEntries);
1757  18 tagIndex.index(blogEntries);
1758  18 categoryIndex.index(blogEntries);
1759  18 authorIndex.index(blogEntries);
1760  18 searchIndex.indexBlogEntries(blogEntries);
1761  18 info("Blog entries reindexed.");
1762    } catch (Exception e) {
1763  0 error(e.getClass().getName() + " reindexing blog entries - " + StringUtils.transformHTML(e.getMessage()));
1764  0 log.error("Error reindexing blog entries", e);
1765    }
1766    }
1767   
 
1768  18 toggle public void reindexStaticPages() {
1769  18 try {
1770    // to reindex all static pages, we need to load them via the DAO
1771  18 Collection<StaticPage> staticPages = DAOFactory.getConfiguredFactory().getStaticPageDAO().loadStaticPages(this);
1772  18 staticPageIndex.reindex(staticPages);
1773  18 searchIndex.indexStaticPages(staticPages);
1774  18 info("Static pages reindexed.");
1775    } catch (Exception e) {
1776  0 error(e.getClass().getName() + " reindexing static pages - " + StringUtils.transformHTML(e.getMessage()));
1777  0 log.error("Error reindexing static pages", e);
1778    }
1779    }
1780   
1781    /**
1782    * Indicates whether some other object is "equal to" this one.
1783    *
1784    * @param o the reference object with which to compare.
1785    * @return <code>true</code> if this object is the same as the obj
1786    * argument; <code>false</code> otherwise.
1787    * @see #hashCode()
1788    * @see java.util.Hashtable
1789    */
 
1790  30 toggle public boolean equals(Object o) {
1791  30 if (this == o) {
1792  30 return true;
1793    }
1794   
1795  0 if (!(o instanceof Blog)) {
1796  0 return false;
1797    }
1798   
1799  0 Blog blog = (Blog)o;
1800  0 return getId().equals(blog.getId());
1801    }
1802   
1803   
 
1804  1334 toggle public String getCommentConfirmationStrategyName() {
1805  1334 return getProperty(COMMENT_CONFIRMATION_STRATEGY_KEY);
1806    }
1807   
 
1808  8 toggle public CommentConfirmationStrategy getCommentConfirmationStrategy() {
1809  8 return commentConfirmationStrategy;
1810    }
1811   
 
1812  1334 toggle public String getTrackBackConfirmationStrategyName() {
1813  1334 return getProperty(TRACKBACK_CONFIRMATION_STRATEGY_KEY);
1814    }
1815   
 
1816  0 toggle public TrackBackConfirmationStrategy getTrackBackConfirmationStrategy() {
1817  0 return trackBackConfirmationStrategy;
1818    }
1819   
 
1820  0 toggle public boolean isRichTextEditorForCommentsEnabled() {
1821  0 String s = getProperty(RICH_TEXT_EDITOR_FOR_COMMENTS_ENABLED_KEY);
1822  0 return s != null && s.equalsIgnoreCase("true");
1823    }
1824   
 
1825  14 toggle public EmailSubscriptionList getEmailSubscriptionList() {
1826  14 return emailSubscriptionList;
1827    }
1828   
 
1829  0 toggle public List<NewsFeedEntry> getNewsFeedEntries() {
1830  0 return NewsFeedCache.getInstance().getNewsFeedEntries(this);
1831    }
1832   
 
1833  0 toggle public List<NewsFeedEntry> getRecentNewsFeedEntries() {
1834  0 List<NewsFeedEntry> entries = getNewsFeedEntries();
1835  0 if (entries.size() > getRecentBlogEntriesOnHomePage()) {
1836  0 entries = entries.subList(0, getRecentBlogEntriesOnHomePage());
1837    }
1838  0 return entries;
1839    }
1840   
 
1841  6670 toggle private List<String> getStringsFromProperty(String key) {
1842  6670 List<String> strings = new ArrayList<String>();
1843  6670 String value = getProperty(key);
1844  6670 if (value != null && value.length() > 0) {
1845  5336 String values[] = value.split("\\s+");
1846  33350 for (int i = 0; i < values.length; i++) {
1847  28014 if (!values[i].startsWith("#")) {
1848  22678 strings.add(values[i].trim());
1849    }
1850    }
1851    }
1852  6670 return strings;
1853    }
1854    }