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