/* * Copyright (c) JForum Team * All rights reserved. * * Redistribution and use in source and binary forms, * with or without modification, are permitted provided * that the following conditions are met: * * 1) Redistributions of source code must retain the above * copyright notice, this list of conditions and the * following disclaimer. * 2) 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. * 3) Neither the name of "Rafael Steil" 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 * * Created on 17/10/2004 23:54:47 * The JForum Project * http://www.jforum.net */ package net.jforum.view.forum.common; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Map; import net.jforum.JForumExecutionContext; import net.jforum.SessionFacade; import net.jforum.dao.DataAccessDriver; import net.jforum.dao.ForumDAO; import net.jforum.dao.TopicDAO; import net.jforum.entities.Forum; import net.jforum.entities.Post; import net.jforum.entities.Topic; import net.jforum.entities.UserSession; import net.jforum.repository.ForumRepository; import net.jforum.repository.SecurityRepository; import net.jforum.repository.TopicRepository; import net.jforum.security.PermissionControl; import net.jforum.security.SecurityConstants; import net.jforum.util.I18n; import net.jforum.util.concurrent.Executor; import net.jforum.util.mail.EmailSenderTask; import net.jforum.util.mail.TopicReplySpammer; import net.jforum.util.preferences.ConfigKeys; import net.jforum.util.preferences.SystemGlobals; import net.jforum.view.forum.ModerationHelper; import freemarker.template.SimpleHash; /** * General utilities methods for topic manipulation. * * @author Rafael Steil * @version $Id: TopicsCommon.java,v 1.47 2007/09/10 23:07:00 rafaelsteil Exp $ */ public class TopicsCommon { private static final Object MUTEXT = new Object(); /** * List all first 'n' topics of a given forum. * This method returns no more than <code>ConfigKeys.TOPICS_PER_PAGE</code> * topics for the forum. * * @param forumId The forum id to which the topics belongs to * @param start The start fetching index * @return <code>java.util.List</code> containing the topics found. */ public static List topicsByForum(int forumId, int start) { TopicDAO tm = DataAccessDriver.getInstance().newTopicDAO(); int topicsPerPage = SystemGlobals.getIntValue(ConfigKeys.TOPICS_PER_PAGE); List topics; // Try to get the first's page of topics from the cache if (start == 0 && SystemGlobals.getBoolValue(ConfigKeys.TOPIC_CACHE_ENABLED)) { topics = TopicRepository.getTopics(forumId); if (topics.size() == 0 || !TopicRepository.isLoaded(forumId)) { synchronized (MUTEXT) { if (topics.size() == 0 || !TopicRepository.isLoaded(forumId)) { topics = tm.selectAllByForumByLimit(forumId, start, topicsPerPage); TopicRepository.addAll(forumId, topics); } } } } else { topics = tm.selectAllByForumByLimit(forumId, start, topicsPerPage); } return topics; } /** * Prepare the topics for listing. * This method does some preparation for a set ot <code>net.jforum.entities.Topic</code> * instances for the current user, like verification if the user already * read the topic, if pagination is a need and so on. * * @param topics The topics to process * @return The post-processed topics. */ public static List prepareTopics(List topics) { UserSession userSession = SessionFacade.getUserSession(); long lastVisit = userSession.getLastVisit().getTime(); int hotBegin = SystemGlobals.getIntValue(ConfigKeys.HOT_TOPIC_BEGIN); int postsPerPage = SystemGlobals.getIntValue(ConfigKeys.POSTS_PER_PAGE); List newTopics = new ArrayList(topics.size()); Map topicsReadTime = SessionFacade.getTopicsReadTime(); Map topicReadTimeByForum = SessionFacade.getTopicsReadTimeByForum(); boolean checkUnread = (userSession.getUserId() != SystemGlobals.getIntValue(ConfigKeys.ANONYMOUS_USER_ID)); for (Iterator iter = topics.iterator(); iter.hasNext(); ) { Topic t = (Topic)iter.next(); boolean read = false; boolean isReadByForum = false; long lastPostTime = t.getLastPostDate().getTime(); if (topicReadTimeByForum != null) { Long currentForumTime = (Long)topicReadTimeByForum.get(new Integer(t.getForumId())); isReadByForum = currentForumTime != null && lastPostTime < currentForumTime.longValue(); } boolean isTopicTimeOlder = !isReadByForum && lastPostTime <= lastVisit; if (!checkUnread || isReadByForum || isTopicTimeOlder) { read = true; } else { Integer topicId = new Integer(t.getId()); Long currentTopicTime = (Long)topicsReadTime.get(topicId); if (currentTopicTime != null) { read = currentTopicTime.longValue() > lastPostTime; } } if (t.getTotalReplies() + 1 > postsPerPage) { t.setPaginate(true); t.setTotalPages(new Double(Math.floor(t.getTotalReplies() / postsPerPage))); } else { t.setPaginate(false); t.setTotalPages(new Double(0)); } // Check if this is a hot topic t.setHot(t.getTotalReplies() >= hotBegin); t.setRead(read); newTopics.add(t); } return newTopics; } /** * Common properties to be used when showing topic data */ public static void topicListingBase() { SimpleHash context = JForumExecutionContext.getTemplateContext(); // Topic Types context.put("TOPIC_ANNOUNCE", new Integer(Topic.TYPE_ANNOUNCE)); context.put("TOPIC_STICKY", new Integer(Topic.TYPE_STICKY)); context.put("TOPIC_NORMAL", new Integer(Topic.TYPE_NORMAL)); // Topic Status context.put("STATUS_LOCKED", new Integer(Topic.STATUS_LOCKED)); context.put("STATUS_UNLOCKED", new Integer(Topic.STATUS_UNLOCKED)); // Moderation PermissionControl pc = SecurityRepository.get(SessionFacade.getUserSession().getUserId()); context.put("moderator", pc.canAccess(SecurityConstants.PERM_MODERATION)); context.put("can_remove_posts", pc.canAccess(SecurityConstants.PERM_MODERATION_POST_REMOVE)); context.put("can_move_topics", pc.canAccess(SecurityConstants.PERM_MODERATION_TOPIC_MOVE)); context.put("can_lockUnlock_topics", pc.canAccess(SecurityConstants.PERM_MODERATION_TOPIC_LOCK_UNLOCK)); context.put("rssEnabled", SystemGlobals.getBoolValue(ConfigKeys.RSS_ENABLED)); } /** * Checks if the user is allowed to view the topic. * If there currently logged user does not have access * to the forum, the template context will be set to show * an error message to the user, by calling * <blockquote>new ModerationHelper().denied(I18n.getMessage("PostShow.denied"))</blockquote> * @param forumId The forum id to which the topics belongs to * @return <code>true</code> if the topic is accessible, <code>false</code> otherwise */ public static boolean isTopicAccessible(int forumId) { Forum f = ForumRepository.getForum(forumId); if (f == null || !ForumRepository.isCategoryAccessible(f.getCategoryId())) { new ModerationHelper().denied(I18n.getMessage("PostShow.denied")); return false; } return true; } /** * Sends a "new post" notification message to all users watching the topic. * * @param t The changed topic * @param p The new message */ public static void notifyUsers(Topic t, Post p) { if (SystemGlobals.getBoolValue(ConfigKeys.MAIL_NOTIFY_ANSWERS)) { TopicDAO dao = DataAccessDriver.getInstance().newTopicDAO(); List usersToNotify = dao.notifyUsers(t); // We only have to send an email if there are users // subscribed to the topic if (usersToNotify != null && usersToNotify.size() > 0) { Executor.execute(new EmailSenderTask(new TopicReplySpammer(t, p, usersToNotify))); } } } /** * Updates the board status after a new post is inserted. * This method is used in conjunct with moderation manipulation. * It will increase by 1 the number of replies of the tpoic, set the * last post id for the topic and the forum and refresh the cache. * * @param topic Topic The topic to update * @param lastPostId int The id of the last post * @param topicDao TopicDAO A TopicModel instance * @param forumDao ForumDAO A ForumModel instance * @param firstPost boolean */ public static synchronized void updateBoardStatus(Topic topic, int lastPostId, boolean firstPost, TopicDAO topicDao, ForumDAO forumDao) { topic.setLastPostId(lastPostId); topicDao.update(topic); forumDao.setLastPost(topic.getForumId(), lastPostId); if (firstPost) { forumDao.incrementTotalTopics(topic.getForumId(), 1); } else { topicDao.incrementTotalReplies(topic.getId()); } topicDao.incrementTotalViews(topic.getId()); TopicRepository.addTopic(topic); TopicRepository.pushTopic(topic); ForumRepository.incrementTotalMessages(); } /** * Deletes a topic. * This method will remove the topic from the database, * clear the entry frm the cache and update the last * post info for the associated forum. * @param topicId The topic id to remove * @param fromModeration boolean * @param forumId int */ public static synchronized void deleteTopic(int topicId, int forumId, boolean fromModeration) { TopicDAO topicDao = DataAccessDriver.getInstance().newTopicDAO(); Topic topic = new Topic(); topic.setId(topicId); topic.setForumId(forumId); topicDao.delete(topic, fromModeration); if (!fromModeration) { // Updates the Recent Topics if it contains this topic TopicRepository.loadMostRecentTopics(); // Updates the Hottest Topics if it contains this topic TopicRepository.loadHottestTopics(); TopicRepository.clearCache(forumId); topicDao.removeSubscriptionByTopic(topicId); } } }