Package net.sourceforge.pebble.domain

Source Code of net.sourceforge.pebble.domain.Blog

/*
* Copyright (c) 2003-2011, Simon Brown
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
*   - Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*
*   - Redistributions in binary form must reproduce the above copyright
*     notice, this list of conditions and the following disclaimer in
*     the documentation and/or other materials provided with the
*     distribution.
*
*   - Neither the name of Pebble nor the names of its contributors may
*     be used to endorse or promote products derived from this software
*     without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/

package net.sourceforge.pebble.domain;

import java.io.File;
import java.lang.reflect.Constructor;
import java.security.SecureRandom;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CopyOnWriteArrayList;

import javax.servlet.http.HttpServletRequest;

import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import net.sourceforge.pebble.*;
import net.sourceforge.pebble.aggregator.NewsFeedCache;
import net.sourceforge.pebble.aggregator.NewsFeedEntry;
import net.sourceforge.pebble.api.confirmation.CommentConfirmationStrategy;
import net.sourceforge.pebble.api.confirmation.TrackBackConfirmationStrategy;
import net.sourceforge.pebble.api.decorator.ContentDecorator;
import net.sourceforge.pebble.api.decorator.FeedDecorator;
import net.sourceforge.pebble.api.decorator.PageDecorator;
import net.sourceforge.pebble.api.event.EventDispatcher;
import net.sourceforge.pebble.api.event.blog.BlogEvent;
import net.sourceforge.pebble.api.event.blog.BlogListener;
import net.sourceforge.pebble.api.event.blogentry.BlogEntryListener;
import net.sourceforge.pebble.api.event.comment.CommentListener;
import net.sourceforge.pebble.api.event.trackback.TrackBackListener;
import net.sourceforge.pebble.api.openid.OpenIdCommentAuthorProvider;
import net.sourceforge.pebble.api.permalink.PermalinkProvider;
import net.sourceforge.pebble.confirmation.DefaultConfirmationStrategy;
import net.sourceforge.pebble.dao.CategoryDAO;
import net.sourceforge.pebble.dao.DAOFactory;
import net.sourceforge.pebble.dao.PersistenceException;
import net.sourceforge.pebble.decorator.ContentDecoratorChain;
import net.sourceforge.pebble.decorator.HideUnapprovedResponsesDecorator;
import net.sourceforge.pebble.event.AuditListener;
import net.sourceforge.pebble.event.DefaultEventDispatcher;
import net.sourceforge.pebble.event.EventListenerList;
import net.sourceforge.pebble.event.blogentry.EmailSubscriptionListener;
import net.sourceforge.pebble.index.AuthorIndex;
import net.sourceforge.pebble.index.AuthorIndexListener;
import net.sourceforge.pebble.index.BlogEntryIndex;
import net.sourceforge.pebble.index.BlogEntryIndexListener;
import net.sourceforge.pebble.index.CategoryIndex;
import net.sourceforge.pebble.index.CategoryIndexListener;
import net.sourceforge.pebble.index.EmailSubscriptionList;
import net.sourceforge.pebble.index.ResponseIndex;
import net.sourceforge.pebble.index.ResponseIndexListener;
import net.sourceforge.pebble.index.SearchIndex;
import net.sourceforge.pebble.index.SearchIndexListener;
import net.sourceforge.pebble.index.StaticPageIndex;
import net.sourceforge.pebble.index.TagIndex;
import net.sourceforge.pebble.index.TagIndexListener;
import net.sourceforge.pebble.logging.AbstractLogger;
import net.sourceforge.pebble.logging.CombinedLogFormatLogger;
import net.sourceforge.pebble.permalink.DefaultPermalinkProvider;
import net.sourceforge.pebble.util.StringUtils;

import org.apache.commons.codec.binary.Hex;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;

/**
* Represents a blog.
*
* @author    Simon Brown
*/
public class Blog extends AbstractBlog {

  private static final Log log = LogFactory.getLog(Blog.class);

  public static final String ABOUT_KEY = "about";
  public static final String EMAIL_KEY = "email";
  public static final String BLOG_OWNERS_KEY = "blogOwners";
  public static final String BLOG_PUBLISHERS_KEY = "blogPublishers";
  public static final String BLOG_CONTRIBUTORS_KEY = "blogContributors";
  public static final String BLOG_READERS_KEY = "blogReaders";
  public static final String PRIVATE_KEY = "private";
  public static final String LUCENE_ANALYZER_KEY = "luceneAnalyzer";
  public static final String CONTENT_DECORATORS_KEY = "decorators";
  public static final String BLOG_LISTENERS_KEY = "blogListeners";
  public static final String BLOG_ENTRY_LISTENERS_KEY = "blogEntryListeners";
  public static final String COMMENT_LISTENERS_KEY = "commentListeners";
  public static final String TRACKBACK_LISTENERS_KEY = "trackBackListeners";
  public static final String EVENT_DISPATCHER_KEY = "eventDispatcher";
  public static final String LOGGER_KEY = "logger";
  public static final String PERMALINK_PROVIDER_KEY = "permalinkProviderName";
  public static final String COMMENT_CONFIRMATION_STRATEGY_KEY = "commentConfirmationStrategy";
  public static final String TRACKBACK_CONFIRMATION_STRATEGY_KEY = "trackBackConfirmationStrategy";
  public static final String RICH_TEXT_EDITOR_FOR_COMMENTS_ENABLED_KEY = "richTextEditorForCommentsEnabled";
  public static final String GRAVATAR_SUPPORT_FOR_COMMENTS_ENABLED_KEY = "gravatarSupportForCommentsEnabled";
  public static final String HOME_PAGE_KEY = "homePage";
  public static final String PAGE_DECORATORS_KEY = "pageDecorators";
  public static final String FEED_DECORATORS_KEY = "feedDecorators";
  public static final String OPEN_ID_COMMENT_AUTHOR_PROVIDERS_KEY = "openIdCommentAuthorProviders";
  public static final String XSRF_SIGNING_SALT_KEY = "signingSalt";

  /** the ID of this blog */
  private String id = "default";

  /** the collection of Year instance that this root blog is managing */
  private List<Year> years;

  /** the root category associated with this blog */
  private Category rootCategory;

  /** the referer filter associated with this blog */
  private RefererFilterManager refererFilterManager;

  /** the editable theme belonging to this blog */
  private Theme editableTheme;

  /** the permalink provider in use */
  private PermalinkProvider permalinkProvider;

  /** the log used to log referers, requests, etc */
  private AbstractLogger logger;

  /** the decorator chain associated with this blog */
  private ContentDecoratorChain decoratorChain;

  private CommentConfirmationStrategy commentConfirmationStrategy;
  private TrackBackConfirmationStrategy trackBackConfirmationStrategy;

  /** the event dispatcher */
  private EventDispatcher eventDispatcher;

  /** the event listener list */
  private EventListenerList eventListenerList;

  /** the plugin properties */
  private PluginProperties pluginProperties;

  /** the blog companion */
  private BlogCompanion blogCompanion;

  private SearchIndex searchIndex;
  private BlogEntryIndex blogEntryIndex;
  private ResponseIndex responseIndex;
  private TagIndex tagIndex;
  private CategoryIndex categoryIndex;
  private AuthorIndex authorIndex;
  private StaticPageIndex staticPageIndex;

  private final List<PageDecorator> pageDecorators = new CopyOnWriteArrayList<PageDecorator>();
  private final List<OpenIdCommentAuthorProvider> openIdCommentAuthorProviders = new CopyOnWriteArrayList<OpenIdCommentAuthorProvider>();
  private final List<FeedDecorator> feedDecorators = new CopyOnWriteArrayList<FeedDecorator>();

  private EmailSubscriptionList emailSubscriptionList;

  /** the ApplicationContext to instantiate plugins with */
  private final AutowireCapableBeanFactory beanFactory;

  /** the Cache that can be used by services to cache arbitrary config */
  private final ConcurrentMap<String, Supplier<?>> serviceCache = new ConcurrentHashMap<String, Supplier<?>>();

  /**
   * Creates a new Blog instance, based at the specified location.
   *
   * @param root    an absolute path pointing to the root directory of the blog
   */
  public Blog(String root) {
    super(root);

    beanFactory = PebbleContext.getInstance().getApplicationContext().getAutowireCapableBeanFactory();

    // probably Blog should be made a final class if init is called from here -
    // see javadoc comment on AbstractBlog.init() for reasons
    init();
  }

  /**
   * Initialize this blog - prepare it for use.
   * Note: As this blog instance is passed to the various participants while
   * it is being initialized, this method is dependent on the correct order
   * of calls: Keep in mind that 'this' is only partly initialized until the
   * end of this method...
   */

  protected void init() {
    super.init();

    try {
      Class<?> c = Class.forName(getPermalinkProviderName());
      setPermalinkProvider(instantiate(c.asSubclass(PermalinkProvider.class)));
    } catch (Exception e) {
      error("Could not load permalink provider \"" + getPermalinkProviderName() + "\"");
      e.printStackTrace();
      setPermalinkProvider(new DefaultPermalinkProvider());
    }

    // load categories
    try {
      DAOFactory factory = DAOFactory.getConfiguredFactory();
      CategoryDAO dao = factory.getCategoryDAO();
      rootCategory = dao.getCategories(this);
    } catch (PersistenceException pe) {
      pe.printStackTrace();
    }

    refererFilterManager = new RefererFilterManager(this);
    pluginProperties = new PluginProperties(this);
    blogCompanion = new BlogCompanion(this);
    years = new ArrayList<Year>();

    // create the various indexes for this blog
    searchIndex = new SearchIndex(this);
    blogEntryIndex = new BlogEntryIndex(this);
    responseIndex = new ResponseIndex(this);
    tagIndex = new TagIndex(this);
    categoryIndex = new CategoryIndex(this);
    authorIndex = new AuthorIndex(this);
    staticPageIndex = new StaticPageIndex(this);

    decoratorChain = new ContentDecoratorChain(this);

    try {
      Class<?> c = Class.forName(getCommentConfirmationStrategyName());
      commentConfirmationStrategy = instantiate(c.asSubclass(CommentConfirmationStrategy.class));
    } catch (Exception e) {
      error("Could not load comment confirmation strategy \"" + getCommentConfirmationStrategyName() + "\"");
      e.printStackTrace();
      commentConfirmationStrategy = new DefaultConfirmationStrategy();
    }

    try {
      Class<?> c = Class.forName(getTrackBackConfirmationStrategyName());
      trackBackConfirmationStrategy = instantiate(c.asSubclass(TrackBackConfirmationStrategy.class));
    } catch (Exception e) {
      error("Could not load TrackBack confirmation strategy \"" + getTrackBackConfirmationStrategyName() + "\"");
      e.printStackTrace();
      trackBackConfirmationStrategy = new DefaultConfirmationStrategy();
    }

    emailSubscriptionList = new EmailSubscriptionList(this);

    initLogger();
    initEventDispatcher();
    initBlogListeners();
    initBlogEntryListeners();
    initCommentListeners();
    initTrackBackListeners();
    initDecorators();
    initPluginList(getPageDecoratorNames(), PageDecorator.class, getPageDecorators(), "Page Decorator");
    initPluginList(getOpenIdCommentAuthorProviderNames(), OpenIdCommentAuthorProvider.class,
        getOpenIdCommentAuthorProviders(), "OpenID Comment Author Provider");
    initPluginList(getFeedDecoratorNames(), FeedDecorator.class, getFeedDecorators(), "Feed Decorator");
  }

  /**
   * Initialises the logger for this blog.
   */
  private void initLogger() {
    log.debug("Initializing logger");

    try {
      Class c = Class.forName(getLoggerName());
      Constructor cons = c.getConstructor(Blog.class);
      this.logger = (AbstractLogger)cons.newInstance(this);
    } catch (Exception e) {
      error("Could not start logger \"" + getLoggerName() + "\"");
      e.printStackTrace();
      this.logger = new CombinedLogFormatLogger(this);
    }
  }

  /**
   * Initialises the event dispatcher for this blog.
   */
  private void initEventDispatcher() {
    log.debug("Initializing event dispatcher");
    eventListenerList = new EventListenerList();

    try {
      Class<?> c = Class.forName(getEventDispatcherName());
      this.eventDispatcher = instantiate(c.asSubclass(EventDispatcher.class));
    } catch (Exception e) {
      e.printStackTrace();
      this.eventDispatcher = new DefaultEventDispatcher();
    }

    eventDispatcher.setEventListenerList(eventListenerList);
  }

  /**
   * Initialises any blog listeners configured for this blog.
   */
  private void initBlogListeners() {
    log.debug("Registering blog listeners");

    for (String className : getBlogListeners()) {
      try {
        Class<?> c = Class.forName(className.trim());
        BlogListener listener = instantiate(c.asSubclass(BlogListener.class));
        eventListenerList.addBlogListener(listener);
      } catch (Exception e) {
        error("Could not start blog listener \"" + className + "\" - check the class name is correct on the <a href=\"viewPlugins.secureaction#blogListeners\">plugins page</a>.");
        log.error("Blog listener " + className + " could not be registered", e);
      }
    }
  }

  /**
   * Initialises any blog entry listeners configured for this blog.
   */
  private void initBlogEntryListeners() {
    log.debug("Registering blog entry listeners");

    for (String className : getBlogEntryListeners()) {
      try {
        Class<?> c = Class.forName(className.trim());
        BlogEntryListener listener = instantiate(c.asSubclass(BlogEntryListener.class));
        eventListenerList.addBlogEntryListener(listener);
      } catch (Exception e) {
        error("Could not start blog entry listener \"" + className + "\" - check the class name is correct on the <a href=\"viewPlugins.secureaction#blogEntryListeners\">plugins page</a>.");
        log.error("Blog entry listener " + className + " could not be registered", e);
      }
    }

    // these are required to keep the various indexes up to date
    eventListenerList.addBlogEntryListener(new BlogEntryIndexListener());
    eventListenerList.addBlogEntryListener(new TagIndexListener());
    eventListenerList.addBlogEntryListener(new CategoryIndexListener());
    eventListenerList.addBlogEntryListener(new AuthorIndexListener());
    eventListenerList.addBlogEntryListener(new SearchIndexListener());
    eventListenerList.addBlogEntryListener(new AuditListener());
    try {
      eventListenerList.addBlogEntryListener(new EmailSubscriptionListener());
    } catch (Throwable t) {
      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.";
      warn(text);
      if(t instanceof NoClassDefFoundError &&
         t.getMessage() != null &&
         t.getMessage().indexOf("javax/mail/Session") > -1) {
        log.warn(text); // consider exception already handled well...
      } else {
        log.warn(text, t);
      }
    }
  }

  /**
   * Initialises any comment listeners configured for this blog.
   */
  private void initCommentListeners() {
    log.debug("Registering comment listeners");

    for (String className : getCommentListeners()) {
      try {
        Class<?> c = Class.forName(className.trim());
        CommentListener listener = instantiate(c.asSubclass(CommentListener.class));
        eventListenerList.addCommentListener(listener);
      } catch (Exception e) {
        error("Could not start comment listener \"" + className + "\" - check the class name is correct on the <a href=\"viewPlugins.secureaction#commentListeners\">plugins page</a>.");
        log.error("Comment listener " + className + " could not be registered", e);
      }
    }

    eventListenerList.addCommentListener(new ResponseIndexListener());
    eventListenerList.addCommentListener(new AuditListener());
  }

  /**
   * Initialises any TrackBack listeners configured for this blog.
   */
  private void initTrackBackListeners() {
    log.debug("Registering TrackBack listeners");

    for (String className : getTrackBackListeners()) {
      try {
        Class<?> c = Class.forName(className.trim());
        TrackBackListener listener = instantiate(c.asSubclass(TrackBackListener.class));
        eventListenerList.addTrackBackListener(listener);
      } catch (Exception e) {
        error("Could not start TrackBack listener \"" + className + "\" - check the class name is correct on the <a href=\"viewPlugins.secureaction#trackbackListeners\">plugins page</a>.");
        log.error("TrackBack listener " + className + " could not be registered", e);
      }
    }

    eventListenerList.addTrackBackListener(new ResponseIndexListener());
    eventListenerList.addTrackBackListener(new AuditListener());
  }

  /**
   * Initialises any content decorators configufred for this blog.
   */
  private void initDecorators() {
    log.debug("Registering decorators");

    decoratorChain.add(new HideUnapprovedResponsesDecorator());

    for (String className : getContentDecorators()) {
      try {
        Class<?> c = Class.forName(className.trim());
        ContentDecorator decorator = instantiate(c.asSubclass(ContentDecorator.class));
        decorator.setBlog(this);
        decoratorChain.add(decorator);
      } catch (Exception e) {
        error("Could not start decorator \"" + className + "\" - check the class name is correct on the <a href=\"viewPlugins.secureaction#contentDecorators\">plugins page</a>.");
        e.printStackTrace();
        log.error(className + " could not be started", e);
      }
    }
  }

  /**
   * Initialises the list of string plugins into the specified list of plugin instances
   *
   * @param pluginNameList  The list of plugin class names
   * @param pluginClass     The type of the plugin
   * @param pluginList      The list of plugins to put the instantiated plugins into
   * @param description   A description of the plugin point for logging
   */
  @SuppressWarnings("unchecked")
  private <P> void initPluginList(Collection<String> pluginNameList, Class<P> pluginClass, List<P> pluginList, String description) {
    log.debug("Registering " + description + "s");

    for (String className : pluginNameList) {
      try {
        Class c = Class.forName(className.trim());
        Class<? extends P> concreteClass = c.asSubclass(pluginClass);
        P plugin = instantiate(concreteClass);
        pluginList.add(plugin);
      } catch (Exception e) {
        error("Could not start " + description + " \"" + className + "\".");
        log.error(description + " " + className + " could not be registered", e);
      }
    }
  }

  /**
   * Gets the default properties for a Blog.
   *
   * @return    a Properties instance
   */
  protected Properties getDefaultProperties() {
    Properties defaultProperties = new Properties();
    defaultProperties.setProperty(NAME_KEY, "My blog");
    defaultProperties.setProperty(DESCRIPTION_KEY, "");
    defaultProperties.setProperty(IMAGE_KEY, "");
    defaultProperties.setProperty(AUTHOR_KEY, "Blog Owner");
    defaultProperties.setProperty(EMAIL_KEY, "blog@yourdomain.com");
    defaultProperties.setProperty(TIMEZONE_KEY, "Europe/London");
    defaultProperties.setProperty(LANGUAGE_KEY, "en");
    defaultProperties.setProperty(COUNTRY_KEY, "GB");
    defaultProperties.setProperty(CHARACTER_ENCODING_KEY, "UTF-8");
    defaultProperties.setProperty(RECENT_BLOG_ENTRIES_ON_HOME_PAGE_KEY, "3");
    defaultProperties.setProperty(RECENT_RESPONSES_ON_HOME_PAGE_KEY, "3");
    defaultProperties.setProperty(THEME_KEY, "default");
    defaultProperties.setProperty(PRIVATE_KEY, FALSE);
    defaultProperties.setProperty(LUCENE_ANALYZER_KEY, "org.apache.lucene.analysis.SimpleAnalyzer");
    defaultProperties.setProperty(CONTENT_DECORATORS_KEY,
        "net.sourceforge.pebble.decorator.RadeoxDecorator\n" +
        "net.sourceforge.pebble.decorator.HtmlDecorator\n" +
        "net.sourceforge.pebble.decorator.EscapeMarkupDecorator\n" +
        "net.sourceforge.pebble.decorator.RelativeUriDecorator\n" +
        "net.sourceforge.pebble.decorator.ReadMoreDecorator\n" +
        "net.sourceforge.pebble.decorator.BlogTagsDecorator");
    defaultProperties.setProperty(BLOG_ENTRY_LISTENERS_KEY,
        "net.sourceforge.pebble.event.blogentry.XmlRpcNotificationListener");
    defaultProperties.setProperty(COMMENT_LISTENERS_KEY,
        "net.sourceforge.pebble.event.response.IpAddressListener\r\n" +
        "net.sourceforge.pebble.event.response.LinkSpamListener\r\n" +
        "net.sourceforge.pebble.event.response.ContentSpamListener\r\n" +
        "net.sourceforge.pebble.event.response.SpamScoreListener\r\n" +
        "net.sourceforge.pebble.event.response.MarkApprovedWhenAuthenticatedListener\r\n" +
        "#net.sourceforge.pebble.event.response.DeleteRejectedListener\r\n" +
        "#net.sourceforge.pebble.event.comment.EmailAuthorNotificationListener");
    defaultProperties.setProperty(TRACKBACK_LISTENERS_KEY,
        "net.sourceforge.pebble.event.response.IpAddressListener\r\n" +
        "net.sourceforge.pebble.event.response.LinkSpamListener\r\n" +
        "net.sourceforge.pebble.event.response.ContentSpamListener\r\n" +
        "net.sourceforge.pebble.event.response.SpamScoreListener\r\n" +
        "net.sourceforge.pebble.event.response.MarkApprovedWhenAuthenticatedListener\r\n" +
        "#net.sourceforge.pebble.event.response.DeleteRejectedListener\r\n" +
        "#net.sourceforge.pebble.event.trackback.EmailAuthorNotificationListener");
    defaultProperties.setProperty(PERMALINK_PROVIDER_KEY, "net.sourceforge.pebble.permalink.DefaultPermalinkProvider");
    defaultProperties.setProperty(EVENT_DISPATCHER_KEY, "net.sourceforge.pebble.event.DefaultEventDispatcher");
    defaultProperties.setProperty(LOGGER_KEY, "net.sourceforge.pebble.logging.CombinedLogFormatLogger");
    defaultProperties.setProperty(COMMENT_CONFIRMATION_STRATEGY_KEY, "net.sourceforge.pebble.confirmation.DefaultConfirmationStrategy");
    defaultProperties.setProperty(TRACKBACK_CONFIRMATION_STRATEGY_KEY, "net.sourceforge.pebble.confirmation.DefaultConfirmationStrategy");
    defaultProperties.setProperty(RICH_TEXT_EDITOR_FOR_COMMENTS_ENABLED_KEY, "true");
    defaultProperties.setProperty(GRAVATAR_SUPPORT_FOR_COMMENTS_ENABLED_KEY, "true");

    return defaultProperties;
  }

  /**
   * Gets the ID of this blog.
   *
   * @return  the ID as a String
   */
  public String getId() {
    return this.id;
  }

  /**
   * Sets the ID of this blog.
   *
   * @param id    the ID as a String
   */
  public void setId(String id) {
    this.id = id;
  }

  /**
   * Gets the URL where this blog is deployed.
   *
   * @return  a URL as a String
   */
  public String getUrl() {
    Configuration config = PebbleContext.getInstance().getConfiguration();
    String url = config.getUrl();

    if (url == null || url.length() == 0) {
      return "";
    } else if (BlogManager.getInstance().isMultiBlog()) {
      if (config.isVirtualHostingEnabled()) {
        return url.substring(0, url.indexOf("://")+3) + getId() + "." + url.substring(url.indexOf("://")+3);
      } else {
        return url + getId() + "/";
      }
    } else {
      return url;
    }
  }

  /**
   * Gets the relative URL where this blog is deployed.
   *
   * @return  a URL as a String
   */
  public String getRelativeUrl() {
    if (BlogManager.getInstance().isMultiBlog()) {
      return "/" + getId() + "/";
    } else {
      return "/";
    }
  }

  /**
   * Gets the about description of this blog.
   *
   * @return    a String
   */
  public String getAbout() {
    return properties.getProperty(ABOUT_KEY);
  }

  /**
   * Gets the home page to be used for this blog.
   *
   * @return    a String
   */
  public String getHomePage() {
    return properties.getProperty(HOME_PAGE_KEY);
  }

  /**
   * Gets the e-mail address of the blog owner.
   *
   * @return    the e-mail address
   */
  public String getEmail() {
    return properties.getProperty(EMAIL_KEY);
  }

  /**
   * Gets a Collection of e-mail addresses.
   *
   * @return  a Collection of String instances
   */
  public Collection getEmailAddresses() {
    return Arrays.asList(getEmail().split(","));
  }

  /**
   * Gets the first of multiple e-mail addresses.
   *
   * @return  the firt e-mail address as a String
   */
  public String getFirstEmailAddress() {
    Collection emailAddresses = getEmailAddresses();
    if (emailAddresses != null && !emailAddresses.isEmpty()) {
      return (String)emailAddresses.iterator().next();
    } else {
      return "";
    }
  }

  /**
   * Gets a comma separated list of the users that are blog owners
   * for this blog.
   *
   * @return  a String containng a comma separated list of user names
   */
  public String getBlogOwnersAsString() {
    return properties.getProperty(BLOG_OWNERS_KEY);
  }

  /**
   * Gets a list of the users that are blog owners for this blog.
   *
   * @return  a String containng a comma separated list of user names
   */
  public List<String> getBlogOwners() {
    String commaSeparatedUsers = getBlogOwnersAsString();
    List<String> users = new LinkedList<String>();
    if (commaSeparatedUsers != null) {
      StringTokenizer tok = new StringTokenizer(commaSeparatedUsers, ",");
      while (tok.hasMoreTokens()) {
        users.add(tok.nextToken().trim());
      }
    }

    return users;
  }

  /**
   * Gets a comma separated list of the users that are blog publishers
   * for this blog.
   *
   * @return  a String containng a comma separated list of user names
   */
  public String getBlogPublishersAsString() {
    return properties.getProperty(BLOG_PUBLISHERS_KEY);
  }

  /**
   * Gets a list of the users that are blog publishers for this blog.
   *
   * @return  a String containng a comma separated list of user names
   */
  public List<String> getBlogPublishers() {
    String commaSeparatedUsers = getBlogPublishersAsString();
    List<String> users = new LinkedList<String>();
    if (commaSeparatedUsers != null) {
      StringTokenizer tok = new StringTokenizer(commaSeparatedUsers, ",");
      while (tok.hasMoreTokens()) {
        users.add(tok.nextToken().trim());
      }
    }

    return users;
  }

  /**
   * Gets a comma separated list of the users that are blog contributors
   * for this blog.
   *
   * @return  a String containng a comma separated list of user names
   */
  public String getBlogContributorsAsString() {
    return properties.getProperty(BLOG_CONTRIBUTORS_KEY);
  }

  /**
   * Gets a list of the users that are blog contributors for this blog.
   *
   * @return  a String containng a comma separated list of user names
   */
  public List<String> getBlogContributors() {
    String commaSeparatedUsers = getBlogContributorsAsString();
    List<String> users = new LinkedList<String>();
    if (commaSeparatedUsers != null) {
      StringTokenizer tok = new StringTokenizer(commaSeparatedUsers, ",");
      while (tok.hasMoreTokens()) {
        users.add(tok.nextToken().trim());
      }
    }

    return users;
  }

  /**
   * Gets a comma separated list of the users that are blog readers
   * for this blog.
   *
   * @return  a String containng a comma separated list of user names
   */
  public String getBlogReadersAsString() {
    return properties.getProperty(BLOG_READERS_KEY);
  }

  /**
   * Gets a list of the users that are blog readers for this blog.
   *
   * @return  a String containng a comma separated list of user names
   */
  public List<String> getBlogReaders() {
    String commaSeparatedUsers = getBlogReadersAsString();
    List<String> users = new LinkedList<String>();
    if (commaSeparatedUsers != null) {
      StringTokenizer tok = new StringTokenizer(commaSeparatedUsers, ",");
      while (tok.hasMoreTokens()) {
        users.add(tok.nextToken().trim());
      }
    }

    return users;
  }

  /**
   * Gets the name of the Lucene analyzer to use.
   *
   * @return  a fully qualified class name
   */
  public String getLuceneAnalyzer() {
    return properties.getProperty(LUCENE_ANALYZER_KEY);
  }

  /**
   * Gets the name of the logger in use.
   *
   * @return  a fully qualified class name
   */
  public String getLoggerName() {
    return properties.getProperty(LOGGER_KEY);
  }

  /**
   * Gets a Collection containing the names of users that are blog owners
   * for this blog.
   *
   * @param roleName The role to get users for
   * @return  a Collection containng user names as Strings
   */
  public Collection<String> getUsersInRole(String roleName) {
    List<String> users = new LinkedList<String>();

    if (roleName.equals(Constants.BLOG_OWNER_ROLE)) {
      users = getBlogOwners();
    } else if (roleName.equals(Constants.BLOG_PUBLISHER_ROLE)) {
      users = getBlogPublishers();
    } else if (roleName.equals(Constants.BLOG_CONTRIBUTOR_ROLE)) {
      users = getBlogContributors();
    } else if (roleName.equals(Constants.BLOG_READER_ROLE)) {
      users = getBlogReaders();
    }

    return users;
  }

  /**
   * Determines whether the specified user is in the specified role.
   *
   * @param roleName    the name of the role
   * @param user        the name of the user
   * @return  true if the user is a member of the role or the list of users
   *          is empty, false otherwise
   */
  public boolean isUserInRole(String roleName, String user) {
    Collection users = getUsersInRole(roleName);
    return users.isEmpty() || users.contains(user);
  }

  /**
   * Gets the Year instance for the specified year.
   *
   * @param year    the year as an int (e.g. 2003)
   * @return    a Year instance
   */
  public Year getBlogForYear(int year) {
    Iterator it = years.iterator();
    Year y;
    while (it.hasNext()) {
      y = (Year)it.next();
      if (y.getYear() == year) {
        return y;
      }
    }

    y = new Year(this, year);
    years.add(y);
    Collections.sort(years);

    return y;
  }

  /**
   * Gets the Year instance representing this year.
   *
   * @return  a Year instance for this year
   */
  public Year getBlogForThisYear() {
    Calendar cal = getCalendar();
    return getBlogForYear(cal.get(Calendar.YEAR));
  }

  /**
   * Gets all Years managed by this root blog.
   *
   * @return  a Collection of Year instances
   */
  public List<Year> getYears() {
    return years;
  }

  /**
   * Gets all Years managed by this root blog, in reverse order.
   *
   * @return  a Collection of Year instances
   */
  public List<Year> getArchives() {
    List<Year> list = new LinkedList<Year>();
    int firstYear = getBlogForFirstMonth().getYear().getYear();
    int thisYear = getBlogForThisYear().getYear();
    // only add years that are in range
    for (Year year : years) {
      if (year.getYear() >= firstYear && year.getYear() <= thisYear) {
        list.add(year);
      }
    }
    Collections.reverse(list);
    return list;
  }

  /**
   * Gets the Month instance representing the first month that
   * contains blog entries.
   *
   * @return  a Month instance
   */
  public Month getBlogForFirstMonth() {
    if (getBlogEntryIndex() == null) {
      return getBlogForThisMonth();
    }

    List<String> blogEntryIds = getBlogEntryIndex().getBlogEntries();
    if (blogEntryIds == null || blogEntryIds.isEmpty()) {
      return getBlogForThisMonth();
    }

    String firstBlogEntryId = blogEntryIds.get(blogEntryIds.size()-1);
    if (firstBlogEntryId == null) {
      return getBlogForThisMonth();
    }

    long dateInMillis = Long.parseLong(firstBlogEntryId);
    Date date = new Date(dateInMillis);
    return getBlogForDay(date).getMonth();
  }

  /**
   * Gets a Day intance for the specified Date.
   *
   * @param date    a java.util.Date instance
   * @return    a Day instance representing the specified Date
   */
  public Day getBlogForDay(Date date) {
    Calendar cal = getCalendar();
    cal.setTime(date);

    int year = cal.get(Calendar.YEAR);
    int month = (cal.get(Calendar.MONTH) + 1);
    int day = cal.get(Calendar.DAY_OF_MONTH);

    return getBlogForDay(year, month, day);
  }

  /**
   * Gets the Day instance for today.
   *
   * @return    a Day instance
   */
  public Day getBlogForToday() {
    return this.getBlogForDay(getCalendar().getTime());
  }

  /**
   * Gets a Day intance for the specified year, month and day.
   *
   * @param year    the year as an int
   * @param month   the month as an int
   * @param day     the day as an int
   * @return    a Day instance representing the specified year, month and day
   */
  public Day getBlogForDay(int year, int month, int day) {
    return getBlogForMonth(year, month).getBlogForDay(day);
  }

  /**
   * Gets a Month intance for the specified year and month.
   *
   * @param year    the year as an int
   * @param month   the month as an int
   * @return    a Month instance representing the specified year and month
   */
  public Month getBlogForMonth(int year, int month) {
    return getBlogForYear(year).getBlogForMonth(month);
  }

  /**
   * Gets the Month instance representing this month.
   *
   * @return  a Month instance for this month
   */
  public Month getBlogForThisMonth() {
    Calendar cal = getCalendar();
    return getBlogForMonth(cal.get(Calendar.YEAR), (cal.get(Calendar.MONTH) + 1));
  }

  /**
   * Given a Year, this method returns the Year instance
   * representing the previous year.
   *
   * @param year    a Year instance
   * @return    a Year representing the previous year
   */
  public Year getBlogForPreviousYear(Year year) {
    return getBlogForYear(year.getYear() - 1);
  }

  /**
   * Given a Year, this method returns the Year instance
   * representing the next year.
   *
   * @param year    a Year instance
   * @return    a Year representing the next year
   */
  public Year getBlogForNextYear(Year year) {
    return getBlogForYear(year.getYear() + 1);
  }

  /**
   * Gets all blog entries for this blog.
   *
   * @return  a List of BlogEntry objects
   */
  public List<BlogEntry> getBlogEntries() {
    List<BlogEntry> blogEntries = new ArrayList<BlogEntry>();
    BlogService service = new BlogService();

    for (int year = years.size()-1; year >= 0; year--) {
      Year y = years.get(year);
      Month[] months = y.getMonths();
      for (int month = 11; month >= 0; month--) {
        try {
          blogEntries.addAll(service.getBlogEntries(this, y.getYear(), months[month].getMonth()));
        } catch (BlogServiceException e) {
          log.error("Exception encountered", e);
        }
      }
    }

    return blogEntries;
  }

  /**
   * Gets all unpublished blog entries for this blog.
   *
   * @return  a List of BlogEntry objects
   */
  public List<BlogEntry> getUnpublishedBlogEntries() {
    List<BlogEntry> blogEntries = new ArrayList<BlogEntry>();
    BlogService service = new BlogService();

    List<String> blogEntryIds = blogEntryIndex.getUnpublishedBlogEntries();
    for (String blogEntryId : blogEntryIds) {
      try {
        blogEntries.add(service.getBlogEntry(this, blogEntryId));
      } catch (BlogServiceException e) {
        log.error("Exception encountered", e);
      }
    }

    return blogEntries;
  }

  /**
   * Gets the number of blog entries for this blog.
   *
   * @return  an int
   */
  public int getNumberOfBlogEntries() {
    return blogEntryIndex.getNumberOfBlogEntries();
  }

  /**
   * Gets the number of published blog entries for this blog.
   *
   * @return  an int
   */
  public int getNumberOfPublishedBlogEntries() {
    return blogEntryIndex.getNumberOfPublishedBlogEntries();
  }

  /**
   * Gets the number of unpublished blog entries for this blog.
   *
   * @return  an int
   */
  public int getNumberOfUnpublishedBlogEntries() {
    return blogEntryIndex.getNumberOfUnpublishedBlogEntries();
  }

  /**
   * Gets the number of static pages for this blog.
   *
   * @return  an int
   */
  public int getNumberOfStaticPages() {
    return staticPageIndex.getNumberOfStaticPages();
  }

  /**
   * Gets the most recent blog entries, the number
   * of which is specified.
   *
   * @param numberOfEntries the number of entries to get
   * @return a List containing the most recent blog entries
   */
  public List<BlogEntry> getRecentBlogEntries(int numberOfEntries) {
    BlogService service = new BlogService();
    List<String> blogEntryIds = blogEntryIndex.getBlogEntries();
    List<BlogEntry> blogEntries = new ArrayList<BlogEntry>();
    for (String blogEntryId : blogEntryIds) {
      try {
        BlogEntry blogEntry = service.getBlogEntry(this, blogEntryId);
        blogEntries.add(blogEntry);
      } catch (BlogServiceException e) {
        log.error("Exception encountered", e);
      }

      if (blogEntries.size() == numberOfEntries) {
        break;
      }
    }

    return blogEntries;
  }

  /**
   * Gets the most recent published blog entries, the number of which
   * is taken from the recentBlogEntriesOnHomePage property.
   *
   * @return a List containing the most recent blog entries
   */
  public List<BlogEntry> getRecentPublishedBlogEntries() {
    return getRecentPublishedBlogEntries(getRecentBlogEntriesOnHomePage());
  }

  /**
   * Gets the most recent published blog entries, the number of which
   * is specified
   *
   * @param number    the number of blog entries to get
   * @return a List containing the most recent blog entries
   */
  public List<BlogEntry> getRecentPublishedBlogEntries(int number) {
    BlogService service = new BlogService();
    List<String> blogEntryIds = blogEntryIndex.getPublishedBlogEntries();
    List<BlogEntry> blogEntries = new ArrayList<BlogEntry>();
    for (String blogEntryId : blogEntryIds) {
      if (blogEntries.size() == number) {
        break;
      }

      try {
        BlogEntry blogEntry = service.getBlogEntry(this, blogEntryId);
        if (blogEntry != null) {
          blogEntries.add(blogEntry);
        }
      } catch (BlogServiceException e) {
        log.error("Exception encountered", e);
      }
    }

    return blogEntries;
  }

  /**
   * Gets blog entries for a given list of IDs.
   *
   * @param blogEntryIds    the list of blog entry IDs
   * @return a List containing the blog entries
   */
  public List<BlogEntry> getBlogEntries(List<String> blogEntryIds) {
    BlogService service = new BlogService();
    List<BlogEntry> blogEntries = new LinkedList<BlogEntry>();
    for (String blogEntryId : blogEntryIds) {
      try {
        BlogEntry blogEntry = service.getBlogEntry(this, blogEntryId);
        if (blogEntry != null) {
          blogEntries.add(blogEntry);
        }
      } catch (BlogServiceException e) {
        log.error("Exception encountered", e);
      }
    }

    return blogEntries;
  }

  /**
   * Gets the most recent published blog entries for a given category, the
   * number of which is taken from the recentBlogEntriesOnHomePage property.
   *
   * @param   category          a category
   * @return  a List containing the most recent blog entries
   */
  public List<BlogEntry> getRecentPublishedBlogEntries(Category category) {
    BlogService service = new BlogService();
    List<String> blogEntryIds = categoryIndex.getRecentBlogEntries(category);
    List<BlogEntry> blogEntries = new ArrayList<BlogEntry>();
    for (String blogEntryId : blogEntryIds) {
      try {
        BlogEntry blogEntry = service.getBlogEntry(this, blogEntryId);
        if (blogEntry != null && blogEntry.isPublished()) {
          blogEntries.add(blogEntry);
        }
      } catch (BlogServiceException e) {
        log.error("Exception encountered", e);
      }

      if (blogEntries.size() == getRecentBlogEntriesOnHomePage()) {
        break;
      }
    }

    return blogEntries;
  }

  /**
   * Gets the most recent published blog entries for a given category, the
   * number of which is taken from the recentBlogEntriesOnHomePage property.
   *
   * @param   author    the author's username
   * @return  a List containing the most recent blog entries
   */
  public List<BlogEntry> getRecentPublishedBlogEntries(String author) {
    BlogService service = new BlogService();
    List<String> blogEntryIds = authorIndex.getRecentBlogEntries(author);
    List<BlogEntry> blogEntries = new ArrayList<BlogEntry>();
    for (String blogEntryId : blogEntryIds) {
      try {
        BlogEntry blogEntry = service.getBlogEntry(this, blogEntryId);
        if (blogEntry != null && blogEntry.isPublished()) {
          blogEntries.add(blogEntry);
        }
      } catch (BlogServiceException e) {
        log.error("Exception encountered", e);
      }

      if (blogEntries.size() == getRecentBlogEntriesOnHomePage()) {
        break;
      }
    }

    return blogEntries;
  }

  /**
   * Gets the most recent published blog entries for a given tag, the
   * number of which is taken from the recentBlogEntriesOnHomePage property.
   *
   * @param tag             a tag
   * @return a List containing the most recent blog entries
   */
  public List<BlogEntry> getRecentPublishedBlogEntries(Tag tag) {
    BlogService service = new BlogService();
    List<String> blogEntryIds = tagIndex.getRecentBlogEntries(tag);
    List<BlogEntry> blogEntries = new ArrayList<BlogEntry>();
    for (String blogEntryId : blogEntryIds) {
      try {
        BlogEntry blogEntry = service.getBlogEntry(this, blogEntryId);
        if (blogEntry != null && blogEntry.isPublished()) {
          blogEntries.add(blogEntry);
        }
      } catch (BlogServiceException e) {
        log.error("Exception encountered", e);
      }

      if (blogEntries.size() == getRecentBlogEntriesOnHomePage()) {
        break;
      }
    }

    return blogEntries;
  }

  /**
   * Gets the most recent responses.
   *
   * @return a List containing the most recent blog entries
   */
  public List<Response> getRecentApprovedResponses() {
    BlogService service = new BlogService();
    List<String> responseIds = responseIndex.getApprovedResponses();
    List<Response> responses = new ArrayList<Response>();
    for (String responseId : responseIds) {
      try {
        Response response = service.getResponse(this, responseId);
        if (response != null && response.getBlogEntry().isPublished()) {
          responses.add(response);
        }
      } catch (BlogServiceException e) {
        log.error("Exception encountered", e);
      }

      if (responses.size() == getRecentResponsesOnHomePage()) {
        break;
      }
    }

    return responses;
  }

  /**
   * Gets the list of approved responses.
   *
   * @return  a List of response IDs
   */
  public List<String> getApprovedResponses() {
    return responseIndex.getApprovedResponses();
  }

  /**
   * Gets the list of pending responses.
   *
   * @return  a List of response IDs
   */
  public List<String> getPendingResponses() {
    return responseIndex.getPendingResponses();
  }

  /**
   * Gets the list of rejected responses.
   *
   * @return  a List of response IDs
   */
  public List<String> getRejectedResponses() {
    return responseIndex.getRejectedResponses();
  }

  /**
   * Gets the number of responses.
   *
   * @return the number of responses
   */
  public int getNumberOfResponses() {
    return responseIndex.getNumberOfResponses();
  }

  /**
   * Gets the number of approved responses.
   *
   * @return the number of approved responses
   */
  public int getNumberOfApprovedResponses() {
    return responseIndex.getNumberOfApprovedResponses();
  }

  /**
   * Gets the number of pending responses.
   *
   * @return the number of pending responses
   */
  public int getNumberOfPendingResponses() {
    return responseIndex.getNumberOfPendingResponses();
  }

  /**
   * Gets the number of rejected responses.
   *
   * @return the number of rejected responses
   */
  public int getNumberOfRejectedResponses() {
    return responseIndex.getNumberOfRejectedResponses();
  }

  /**
   * Gets the date that this blog was last updated through the addition
   * of a blog entry.
   *
   * @return  a Date instance representing the time of the most recent entry
   */
  public Date getLastModified() {
    Date date = new Date(0);
    List blogEntries = getRecentPublishedBlogEntries(1);
    if (blogEntries.size() == 1) {
      date = ((BlogEntry)blogEntries.get(0)).getDate();
    }

    return date;
  }

  /**
   * Gets the date of the most recent response.
   *
   * @return  a Date instance representing the time of the most recent entry
   */
  public Date getDateOfLastResponse() {
    List<Response> responses = this.getRecentApprovedResponses();
    if (responses != null && responses.size() > 0) {
      return responses.get(0).getDate();
    } else {
      return new Date(0);
    }
  }

  public BlogEntry getPreviousBlogEntry(BlogEntry blogEntry) {
    Day firstDay = getBlogForFirstMonth().getBlogForFirstDay();
    Day day = getBlogForDay(blogEntry.getDate());

    String blogEntryId = day.getPreviousBlogEntry(blogEntry.getId());
    while (day != firstDay && blogEntryId == null) {
      day = day.getPreviousDay();
      blogEntryId = day.getLastBlogEntry();
    }

    if (blogEntryId != null) {
      BlogService service = new BlogService();
      try {
        return service.getBlogEntry(this, blogEntryId);
      } catch (BlogServiceException e) {
        // do nothing
      }
    }

    return null;
  }

  public BlogEntry getNextBlogEntry(BlogEntry blogEntry) {
    Day lastDay = getBlogForToday();
    Day day = getBlogForDay(blogEntry.getDate());

    String blogEntryId = day.getNextBlogEntry(blogEntry.getId());
    while (day != lastDay && blogEntryId == null) {
      day = day.getNextDay();
      blogEntryId = day.getFirstBlogEntry();
    }

    if (blogEntryId != null) {
      BlogService service = new BlogService();
      try {
        return service.getBlogEntry(this, blogEntryId);
      } catch (BlogServiceException e) {
        // do nothing
      }
    }

    return null;
  }

  /**
   * Gets the categories associated with this blog.
   *
   * @return  a List of Category instances
   */
  public List<Category> getCategories() {
    CategoryBuilder builder = new CategoryBuilder(this, rootCategory);
    return builder.getCategories();
  }

  /**
   * Gets a specific category.
   *
   * @param id The id of the category
   * @return  a Category instance
   */
  public Category getCategory(String id) {
    CategoryBuilder builder = new CategoryBuilder(this, rootCategory);
    return builder.getCategory(id);
  }

  /**
   * Gets the root category for this blog.
   *
   * @return  a Category instance
   */
  public Category getRootCategory() {
    return this.rootCategory;
  }

  /**
   * Sets the root category for this blog.
   *
   * @param category    a Category instance
   */
  public void setRootCategory(Category category) {
    this.rootCategory = category;
  }

  /**
   * Adds a category.
   *
   * @param category    the Category to be added
   */
  public synchronized void addCategory(Category category) {
    if (getCategory(category.getId()) == null) {
      CategoryBuilder builder = new CategoryBuilder(this, rootCategory);
      builder.addCategory(category);
    }
  }

  /**
   * Removes a category.
   *
   * @param category    the Category to be removed
   */
  public synchronized void removeCategory(Category category) {
    if (getCategory(category.getId()) != null) {
      CategoryBuilder builder = new CategoryBuilder(this, rootCategory);
      builder.removeCategory(category);
    }
  }

  /**
   * Gets the list of tags associated with this blog.
   *
   * @return The list of tags
   */
  public List<Tag> getTags() {
    return tagIndex.getTags();
  }

  /**
   * Gets the tag with the specified name.
   *
   * @param name    the name as a String
   * @return  a Tag instance
   */
  public Tag getTag(String name) {
    return new Tag(name, this);
  }

  /**
   * Gets the object managing referer filters.
   *
   * @return  a RefererFilterManager instance
   */
  public RefererFilterManager getRefererFilterManager() {
    return this.refererFilterManager;
  }

  /**
   * Gets the search index.
   *
   * @return  a BlogEntryIndex instance
   */
  public SearchIndex getSearchIndex() {
    return this.searchIndex;
  }

  /**
   * Gets the blog entry index.
   *
   * @return  a BlogEntryIndex instance
   */
  public BlogEntryIndex getBlogEntryIndex() {
    return this.blogEntryIndex;
  }

  /**
   * Gets the response index.
   *
   * @return  a ResponseIndex instance
   */
  public ResponseIndex getResponseIndex() {
    return this.responseIndex;
  }

  /**
   * Gets the tag index.
   *
   * @return  a TagIndex instance
   */
  public TagIndex getTagIndex() {
    return this.tagIndex;
  }

  /**
   * Gets the category index.
   *
   * @return  a CategoryIndex instance
   */
  public CategoryIndex getCategoryIndex() {
    return this.categoryIndex;
  }

  /**
   * Gets the author index.
   *
   * @return  a AuthorIndex instance
   */
  public AuthorIndex getAuthorIndex() {
    return this.authorIndex;
  }

  /**
   * Gets the story index.
   *
   * @return  a StaticPageIndex instance
   */
  public StaticPageIndex getStaticPageIndex() {
    return this.staticPageIndex;
  }

  /**
   * Logs this request for blog.
   *
   * @param request   the HttpServletRequest instance for this request
   */
  public synchronized void log(HttpServletRequest request, int status) {
    String externalUri = (String)request.getAttribute(Constants.EXTERNAL_URI);
    if (externalUri.matches("/images/.+")) {
      // do nothing, we don't want to log the following types of requests
      // - a blog's images
    } else {
      // log the request
      logger.log(request, status);
    }
  }

  /**
   * Gets an object representing the editable theme.
   *
   * @return    an EditableTheme instance
   */
  public Theme getEditableTheme() {
    return editableTheme;
  }

  /**
   * Sets an object representing the editable theme.
   *
   * @param editableTheme    an EditableTheme instance
   */
  public void setEditableTheme(Theme editableTheme) {
    this.editableTheme = editableTheme;
  }

  /**
   * Gets the location where the blog files are stored.
   *
   * @return    an absolute, local path on the filing system
   */
  public String getFilesDirectory() {
    return getRoot() + File.separator + "files";
  }

  /**
   * Gets the location where the blog theme is stored.
   *
   * @return    an absolute, local path on the filing system
   */
  public String getThemeDirectory() {
    return getRoot() + File.separator + "theme";
  }

  /**
   * Gets the location where the plugin properties file is stored.
   *
   * @return    an absolute, local path on the filing system
   */
  public String getPluginPropertiesFile() {
    return getRoot() + File.separator + "plugin.properties";
  }

  /**
   * Gets the location where the plugin properties file is stored.
   *
   * @return    an absolute, local path on the filing system
   */
  public String getCompanionFile() {
    return getRoot() + File.separator + "companion.txt";
  }

  /**
   * Determines whether this blog is public.
   *
   * @return  true if public, false otherwise
   */
  public boolean isPublic() {
    return properties.getProperty(PRIVATE_KEY).equalsIgnoreCase(FALSE);
  }

  /**
   * Determines whether this blog is private.
   *
   * @return  true if public, false otherwise
   */
  public boolean isPrivate() {
    return properties.getProperty(PRIVATE_KEY).equalsIgnoreCase(TRUE);
  }

  /**
   * Called to start (i.e. activate/initialise, restore the theme, etc) this
   * blog.
   */
  void start() {
    log.debug("Starting blog with ID " + getId());

    // reindex the blog if the indexes don't exist
    File indexes = new File(getIndexesDirectory());
    if (!indexes.exists()) {
      indexes.mkdir();
      reindex();
    }

    File imagesDirectory = new File(getImagesDirectory());
    if (!imagesDirectory.exists()) {
      imagesDirectory.mkdir();
    }

    File filesDirectory = new File(getFilesDirectory());
    if (!filesDirectory.exists()) {
      filesDirectory.mkdir();
    }

    File logDirectory = new File(getLogsDirectory());
    if (!logDirectory.exists()) {
      logDirectory.mkdir();
    }

    logger.start();
    editableTheme.restore();

    // call blog listeners
    eventDispatcher.fireBlogEvent(new BlogEvent(this, BlogEvent.BLOG_STARTED));
    log.info("Started blog with ID " + getId());
  }

  /**
   * Called to shutdown this blog.
   */
  void stop() {
    log.debug("Stopping blog with ID " + getId());

    logger.stop();
    editableTheme.backup();

    // call blog listeners
    eventDispatcher.fireBlogEvent(new BlogEvent(this, BlogEvent.BLOG_STOPPED));
    log.info("Stopped blog with ID " + getId());
  }

  /**
   * Gets the logger associated with this blog.
   *
   * @return    an AbstractLogger implementation
   */
  public AbstractLogger getLogger() {
    return this.logger;
  }

  /**
   * Gets the list of plugins.
   *
   * @return  a comma separated list of class names
   */
  public List<String> getContentDecorators() {
    return getStringsFromProperty(CONTENT_DECORATORS_KEY);
  }

  /**
   * Gets the decorator manager associated with this blog.
   *
   * @return  a BlogEntryDecoratorManager instance
   */
  public ContentDecoratorChain getContentDecoratorChain() {
    return this.decoratorChain;
  }

  /**
   * Gets the list of blog listeners as strings.
   *
   * @return  The list of class names
   */
  public List<String> getBlogListeners() {
    return getStringsFromProperty(BLOG_LISTENERS_KEY);
  }

  /**
   * Gets the list of blog entry listeners as strings.
   *
   * @return  The list of class names
   */
  public List<String> getBlogEntryListeners() {
    return getStringsFromProperty(BLOG_ENTRY_LISTENERS_KEY);
  }

  /**
   * Gets the list of comment listeners as strings.
   *
   * @return  The list of class names
   */
  public List<String> getCommentListeners() {
    return getStringsFromProperty(COMMENT_LISTENERS_KEY);
  }

  /**
   * Gets the list of TrackBack listeners as strings.
   *
   * @return  The list of class names
   */
  public List<String> getTrackBackListeners() {
    return getStringsFromProperty(TRACKBACK_LISTENERS_KEY);
  }

  /**
   * Gets the list of page decorators as Strings
   *
   * @return  The list of class names
   */
  public List<String> getPageDecoratorNames() {
    return getStringsFromProperty(PAGE_DECORATORS_KEY);
  }

  /**
   * Gets the list of feed decorators as Strings
   *
   * @return  The list of class names
   */
  public List<String> getFeedDecoratorNames() {
    return getStringsFromProperty(FEED_DECORATORS_KEY);
  }

  /**
   * Gets the list of OpenID comment author providers as Strings
   *
   * @return  The list of class names
   */
  public List<String> getOpenIdCommentAuthorProviderNames() {
    return getStringsFromProperty(OPEN_ID_COMMENT_AUTHOR_PROVIDERS_KEY);
  }

  /**
   * Gets the name the event dispatcher.
   *
   * @return  a String
   */
  public String getEventDispatcherName() {
    return getProperty(EVENT_DISPATCHER_KEY);
  }

  /**
   * Gets the event dispatcher in use.
   *
   * @return  an EventDispatcher implementation
   */
  public EventDispatcher getEventDispatcher() {
    return this.eventDispatcher;
  }

  /**
   * Gets the event listsner list.
   *
   * @return The event listener list
   */
  public EventListenerList getEventListenerList() {
    return this.eventListenerList;
  }

  public PluginProperties getPluginProperties() {
    return this.pluginProperties;
  }
  public BlogCompanion getBlogCompanion() {
    return this.blogCompanion;
  }

  /**
   * Gets the name of the permalink provider.
   *
   * @return    the fully qualified class name of the permalink provider
   */
  public String getPermalinkProviderName() {
    return properties.getProperty(PERMALINK_PROVIDER_KEY);
  }

  /**
   * Gets the permalink provider in use.
   *
   * @return  a PermalinkProvider instance
   */
  public PermalinkProvider getPermalinkProvider() {
    return this.permalinkProvider;
  }

  /**
   * Sets the permalink provider in use.
   *
   * @param provider    PermalinkProvider instance
   */
  public void setPermalinkProvider(PermalinkProvider provider) {
    this.permalinkProvider = provider;
    this.permalinkProvider.setBlog(this);
  }

  public List<PageDecorator> getPageDecorators() {
    return pageDecorators;
  }

  public List<OpenIdCommentAuthorProvider> getOpenIdCommentAuthorProviders() {
    return openIdCommentAuthorProviders;
  }

  public List<FeedDecorator> getFeedDecorators() {
    return feedDecorators;
  }

  public void reindex() {
    log.info("Reindexing blog with ID " + getId());

    reindexBlogEntries();
    reindexStaticPages();
  }

  public void reindexBlogEntries() {
    blogEntryIndex.clear();
    responseIndex.clear();
    tagIndex.clear();
    categoryIndex.clear();
    authorIndex.clear();
    searchIndex.clear();

    try {
      // to reindex all blog entries, we need to load them via the DAO
      Collection<BlogEntry> blogEntries = DAOFactory.getConfiguredFactory().getBlogEntryDAO().loadBlogEntries(this);
      blogEntryIndex.index(blogEntries);
      responseIndex.index(blogEntries);
      tagIndex.index(blogEntries);
      categoryIndex.index(blogEntries);
      authorIndex.index(blogEntries);
      searchIndex.indexBlogEntries(blogEntries);
      info("Blog entries reindexed.");
    } catch (Exception e) {
      error(e.getClass().getName() + " reindexing blog entries - " + StringUtils.transformHTML(e.getMessage()));
      log.error("Error reindexing blog entries", e);
    }
  }

  public void reindexStaticPages() {
    try {
      // to reindex all static pages, we need to load them via the DAO
      Collection<StaticPage> staticPages = DAOFactory.getConfiguredFactory().getStaticPageDAO().loadStaticPages(this);
      staticPageIndex.reindex(staticPages);
      searchIndex.indexStaticPages(staticPages);
      info("Static pages reindexed.");
    } catch (Exception e) {
      error(e.getClass().getName() + " reindexing static pages - " + StringUtils.transformHTML(e.getMessage()));
      log.error("Error reindexing static pages", e);
    }
  }

  /**
   * Indicates whether some other object is "equal to" this one.
   *
   * @param o   the reference object with which to compare.
   * @return <code>true</code> if this object is the same as the obj
   *         argument; <code>false</code> otherwise.
   * @see #hashCode()
   * @see java.util.Hashtable
   */
  public boolean equals(Object o) {
    if (this == o) {
      return true;
    }

    if (!(o instanceof Blog)) {
      return false;
    }

    Blog blog = (Blog)o;
    return getId().equals(blog.getId());
  }


  public String getCommentConfirmationStrategyName() {
    return getProperty(COMMENT_CONFIRMATION_STRATEGY_KEY);
  }

  public CommentConfirmationStrategy getCommentConfirmationStrategy() {
    return commentConfirmationStrategy;
  }

  public String getTrackBackConfirmationStrategyName() {
    return getProperty(TRACKBACK_CONFIRMATION_STRATEGY_KEY);
  }

  public TrackBackConfirmationStrategy getTrackBackConfirmationStrategy() {
    return trackBackConfirmationStrategy;
  }

  public boolean isRichTextEditorForCommentsEnabled() {
    return Boolean.parseBoolean(getProperty(RICH_TEXT_EDITOR_FOR_COMMENTS_ENABLED_KEY));
  }

  public boolean isGravatarSupportForCommentsEnabled() {
    return Boolean.parseBoolean(getProperty(GRAVATAR_SUPPORT_FOR_COMMENTS_ENABLED_KEY));
  }

  public EmailSubscriptionList getEmailSubscriptionList() {
    return emailSubscriptionList;
  }

  public List<NewsFeedEntry> getNewsFeedEntries() {
    return NewsFeedCache.getInstance().getNewsFeedEntries(this);
  }

  public List<NewsFeedEntry> getRecentNewsFeedEntries() {
    List<NewsFeedEntry> entries = getNewsFeedEntries();
    if (entries.size() > getRecentBlogEntriesOnHomePage()) {
      entries = entries.subList(0, getRecentBlogEntriesOnHomePage());
    }
    return entries;
  }

  public String getXsrfSigningSalt() {
    String salt = getProperty(XSRF_SIGNING_SALT_KEY);
    if (salt == null) {
      // Generate salt
      byte[] saltBytes = new byte[8];
      new SecureRandom().nextBytes(saltBytes);
      salt = new String(Hex.encodeHex(saltBytes));
      setProperty(XSRF_SIGNING_SALT_KEY, salt);
      try {
        storeProperties();
      } catch (BlogServiceException e) {
        log.error("Error saving XSRF salt", e);
      }
    }
    return salt;
  }

  /**
   * Get an item from the cache
   *
   * @param key The key in the cache
   * @param supplier The supplier of the item.  This would usually be a memoized supplier, created using Suppliers.memoize()
   * @param <T> The type of the time
   *
   * @return The item
   */
  public <T> T getServiceCacheItem(String key, Supplier<T> supplier) {
    Supplier<T> memoizedSupplier = Suppliers.memoize(supplier);
    Supplier<T> cachedSupplier = (Supplier<T>) serviceCache.putIfAbsent(key, memoizedSupplier);
    if (cachedSupplier == null) {
      return memoizedSupplier.get();
    } else {
      return cachedSupplier.get();
    }
  }

  /**
   * Reset an item in the cache
   *
   * @param key The item to reset
   */
  public void resetServiceCacheItem(String key) {
    serviceCache.remove(key);
  }

  private List<String> getStringsFromProperty(String key) {
    List<String> strings = new ArrayList<String>();
    String value = getProperty(key);
    if (value != null && value.length() > 0) {
      String values[] = value.split("\\s+");
      for (String val :  values) {
        if (!val.startsWith("#")) {
          strings.add(val.trim());
        }
      }
    }
    return strings;
  }

  private <T> T instantiate(Class<T> clazz) {
    return (T) beanFactory.autowire(clazz, AutowireCapableBeanFactory.AUTOWIRE_NO, false);
  }
}
TOP

Related Classes of net.sourceforge.pebble.domain.Blog

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.