org.etudes.component.app.jforum.JForumPostServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.etudes.component.app.jforum.JForumPostServiceImpl.java

Source

/**********************************************************************************
 * $URL: https://source.sakaiproject.org/contrib/etudes/sakai-jforum/tags/2.9.11/jforum-impl/impl/src/java/org/etudes/component/app/jforum/JForumPostServiceImpl.java $ 
 * $Id: JForumPostServiceImpl.java 83559 2013-04-30 19:03:29Z murthy@etudes.org $ 
 *********************************************************************************** 
 * 
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Etudes, Inc. 
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); 
 * you may not use this file except in compliance with the License. 
 * You may obtain a copy of the License at 
 * 
 * http://www.apache.org/licenses/LICENSE-2.0 
 * 
 * Unless required by applicable law or agreed to in writing, software 
 * distributed under the License is distributed on an "AS IS" BASIS, 
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
 * See the License for the specific language governing permissions and 
 * limitations under the License. 
 ***********************************************************************************/
package org.etudes.component.app.jforum;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;

import org.apache.commons.lang.StringEscapeUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.etudes.api.app.jforum.Attachment;
import org.etudes.api.app.jforum.Category;
import org.etudes.api.app.jforum.Evaluation;
import org.etudes.api.app.jforum.Forum;
import org.etudes.api.app.jforum.Grade;
import org.etudes.api.app.jforum.JForumAccessException;
import org.etudes.api.app.jforum.JForumAttachmentBadExtensionException;
import org.etudes.api.app.jforum.JForumAttachmentOverQuotaException;
import org.etudes.api.app.jforum.JForumAttachmentService;
import org.etudes.api.app.jforum.JForumCategoryService;
import org.etudes.api.app.jforum.JForumEmailExecutorService;
import org.etudes.api.app.jforum.JForumForumService;
import org.etudes.api.app.jforum.JForumGBService;
import org.etudes.api.app.jforum.JForumGradeService;
import org.etudes.api.app.jforum.JForumGradesModificationException;
import org.etudes.api.app.jforum.JForumItemEvaluatedException;
import org.etudes.api.app.jforum.JForumItemNotFoundException;
import org.etudes.api.app.jforum.JForumPostService;
import org.etudes.api.app.jforum.JForumSearchIndexingExecutorService;
import org.etudes.api.app.jforum.JForumSecurityService;
import org.etudes.api.app.jforum.JForumService;
import org.etudes.api.app.jforum.JForumSpecialAccessService;
import org.etudes.api.app.jforum.JForumUserService;
import org.etudes.api.app.jforum.LastPostInfo;
import org.etudes.api.app.jforum.Post;
import org.etudes.api.app.jforum.SpecialAccess;
import org.etudes.api.app.jforum.Topic;
import org.etudes.api.app.jforum.User;
import org.etudes.api.app.jforum.dao.AttachmentDao;
import org.etudes.api.app.jforum.dao.CategoryDao;
import org.etudes.api.app.jforum.dao.ForumDao;
import org.etudes.api.app.jforum.dao.TopicDao;
import org.etudes.component.app.jforum.util.post.EmailUtil;
import org.etudes.component.app.jforum.util.post.PostUtil;
import org.etudes.util.HtmlHelper;
import org.etudes.util.XrefHelper;
import org.etudes.util.api.AccessAdvisor;
import org.sakaiproject.component.cover.ComponentManager;
import org.sakaiproject.component.cover.ServerConfigurationService;
import org.sakaiproject.db.api.SqlReader;
import org.sakaiproject.db.api.SqlService;
import org.sakaiproject.exception.IdUnusedException;
import org.sakaiproject.site.api.Group;
import org.sakaiproject.site.api.Site;
import org.sakaiproject.site.api.SiteService;
import org.sakaiproject.site.api.ToolConfiguration;
import org.sakaiproject.thread_local.api.ThreadLocalManager;
import org.sakaiproject.user.api.UserNotDefinedException;
import org.sakaiproject.user.cover.UserDirectoryService;
import org.sakaiproject.util.Web;

public class JForumPostServiceImpl implements JForumPostService {
    private static Log logger = LogFactory.getLog(JForumPostServiceImpl.class);

    /** Dependency (optional, self-injected): AccessAdvisor. */
    protected transient AccessAdvisor accessAdvisor = null;

    /** Dependency: AttachmentDao */
    protected AttachmentDao attachmentDao = null;

    /** Dependency: CategoryDao. */
    protected CategoryDao categoryDao = null;

    /** Dependency: ForumDao. */
    protected ForumDao forumDao = null;

    /** Dependency: JForumAttachmentService */
    protected JForumAttachmentService jforumAttachmentService = null;

    /** Dependency: JForumCategoryService */
    protected JForumCategoryService jforumCategoryService = null;

    /** Dependency: JForumEmailExecutorService. */
    protected JForumEmailExecutorService jforumEmailExecutorService = null;

    /** Dependency: JForumForumService */
    protected JForumForumService jforumForumService = null;

    /** Dependency:JForumGBService */
    protected JForumGBService jforumGBService = null;

    /** Dependency: JForumGradeService */
    protected JForumGradeService jforumGradeService = null;

    /** Dependency: JForumSearchIndexingExecutorService. */
    protected JForumSearchIndexingExecutorService jforumSearchIndexingExecutorService = null;

    /** Dependency: JForumSecurityService. */
    protected JForumSecurityService jforumSecurityService = null;

    /** Dependency: JForumSpecialAccessService */
    protected JForumSpecialAccessService jforumSpecialAccessService = null;

    /** Dependency: JForumUserService */
    protected JForumUserService jforumUserService = null;

    /** Dependency: SiteService. */
    protected SiteService siteService = null;

    /** Dependency: SqlService */
    protected SqlService sqlService = null;

    /** Dependency: ThreadLocalManager. */
    protected ThreadLocalManager threadLocalManager = null;

    /** Dependency: TopicDao */
    protected TopicDao topicDao = null;

    /**
     * {@inheritDoc}
     */
    public int createPost(Topic topic) throws JForumAccessException, JForumAttachmentOverQuotaException,
            JForumAttachmentBadExtensionException {
        if (topic == null || topic.getId() <= 0) {
            throw new IllegalArgumentException("Topic is null or invalid");
        }

        if (topic.getPosts().size() != 1) {
            throw new IllegalArgumentException("Post data is required to create topic post");
        }

        Post post = topic.getPosts().get(0);
        if (post.getPostedBy() == null || post.getPostedBy().getId() <= 0) {
            throw new IllegalArgumentException("Post data has no proper postedby information");
        }

        // validate attachments
        if (post.hasAttachments()) {
            jforumAttachmentService.validatePostAttachments(post);
        }

        return createTopicPost(topic);
    }

    /**
     * {@inheritDoc}
     */
    public int createTopic(Topic topic) throws JForumAccessException {
        if (topic == null) {
            throw new IllegalArgumentException();
        }

        if (topic.getPosts().size() != 1) {
            throw new IllegalArgumentException("Post data is required to create the topic");
        }

        if (topic.getForumId() == 0) {
            throw new IllegalArgumentException("Topic's forum information is required");
        }

        Forum forum = forumDao.selectById(topic.getForumId());

        if (forum == null) {
            throw new IllegalArgumentException("Topic forum is not a valid forum or not existing");
        }

        Category category = categoryDao.selectById(forum.getCategoryId());

        if (category == null) {
            throw new IllegalArgumentException("Topic forum category is not a valid category or not existing");
        }

        if (topic.getPostedBy() == null || topic.getPostedBy().getSakaiUserId() == null) {
            throw new IllegalArgumentException("Topic data has no proper postedby information");
        }

        String sakaiUserId = topic.getPostedBy().getSakaiUserId();

        // access check
        boolean facilitator = jforumSecurityService.isJForumFacilitator(category.getContext(),
                topic.getPostedBy().getSakaiUserId());

        boolean participant = false;
        if (!facilitator) {
            participant = jforumSecurityService.isJForumParticipant(category.getContext(),
                    topic.getPostedBy().getSakaiUserId());
        }

        if (!(facilitator || participant)) {
            throw new JForumAccessException(sakaiUserId);
        }

        Site site = null;
        if (participant) {
            // participants cannot create new topics if the forum has deny access etc
            if (forum.getAccessType() == Forum.ForumAccess.DENY.getAccessType()) {
                throw new JForumAccessException(sakaiUserId);
            }

            // participants cannot create new topics if the forum type is reply only or readonly
            if ((forum.getType() == Forum.ForumType.READ_ONLY.getType())
                    || (forum.getAccessType() == Forum.ForumType.REPLY_ONLY.getType())) {
                throw new JForumAccessException(sakaiUserId);
            }

            String userSakaiId = topic.getPostedBy().getSakaiUserId();

            try {
                site = siteService.getSite(category.getContext());
            } catch (IdUnusedException e) {
                if (logger.isWarnEnabled()) {
                    logger.warn("createTopic: missing site: " + category.getContext());
                }
            }

            // if forum access type is GROUPS. Check the user groups.
            if (forum.getAccessType() == Forum.ForumAccess.GROUPS.getAccessType()) {
                boolean userInGroup = false;
                Collection<org.sakaiproject.site.api.Group> userGroups = null;

                // fetch user groups only once
                if (userGroups == null && site != null) {
                    userGroups = site.getGroupsWithMember(userSakaiId);
                }

                if (userGroups != null) {
                    for (org.sakaiproject.site.api.Group grp : userGroups) {
                        if ((forum.getGroups() != null) && (forum.getGroups().contains(grp.getId()))) {
                            userInGroup = true;
                            break;
                        }
                    }
                }

                // user not in any group
                if (!userInGroup) {
                    throw new JForumAccessException(sakaiUserId);
                }
            }

            Date now = new Date();

            // participants cannot create new topics if the category or forum is not open.
            if ((category.getAccessDates() != null) && ((category.getAccessDates().getOpenDate() != null)
                    || (category.getAccessDates().getDueDate() != null))) {
                if (((category.getAccessDates().getOpenDate() != null))
                        && (category.getAccessDates().getOpenDate().after(now))) {
                    throw new JForumAccessException(sakaiUserId);
                }

                //if ((category.getAccessDates().getDueDate() != null) && (category.getAccessDates().getDueDate().before(now)) && (category.getAccessDates().isLocked()))
                if (category.getAccessDates().getAllowUntilDate() != null) {
                    if (category.getAccessDates().getAllowUntilDate().before(now)) {
                        throw new JForumAccessException(sakaiUserId);
                    }
                } else if (category.getAccessDates().getDueDate() != null) {
                    if (category.getAccessDates().getDueDate().before(now)) {
                        throw new JForumAccessException(sakaiUserId);
                    }
                }
            } else if ((forum.getAccessDates() != null) && ((forum.getAccessDates().getOpenDate() != null)
                    || (forum.getAccessDates().getDueDate() != null)
                    || (forum.getAccessDates().getAllowUntilDate() != null))) {
                // forum special access
                List<SpecialAccess> specialAccessList = forum.getSpecialAccess();
                boolean specialAccessUser = false;
                boolean specialAccessUserAccess = false;

                for (SpecialAccess sa : specialAccessList) {
                    if (sa.getUserIds().contains(new Integer(topic.getPostedBy().getId()))) {
                        specialAccessUser = true;

                        if (!sa.isOverrideStartDate()) {
                            sa.getAccessDates().setOpenDate(forum.getAccessDates().getOpenDate());
                        }

                        if (!sa.isOverrideHideUntilOpen()) {
                            sa.getAccessDates().setHideUntilOpen(forum.getAccessDates().isHideUntilOpen());
                        }

                        if (!sa.isOverrideEndDate()) {
                            sa.getAccessDates().setDueDate(forum.getAccessDates().getDueDate());
                        }

                        /*if (!sa.isOverrideLockEndDate())
                        {
                           sa.getAccessDates().setLocked(forum.getAccessDates().isLocked());
                        }*/

                        if (!sa.isOverrideAllowUntilDate()) {

                            sa.getAccessDates().setAllowUntilDate(forum.getAccessDates().getAllowUntilDate());
                        }

                        if (sa.getAccessDates().getOpenDate() == null) {
                            specialAccessUserAccess = true;
                        } else {
                            if (sa.getAccessDates().getOpenDate().after(now)) {
                                if (!sa.getAccessDates().isHideUntilOpen()) {
                                    specialAccessUserAccess = true;
                                }
                            } else {
                                specialAccessUserAccess = true;
                            }
                        }
                        break;
                    }
                }

                if (specialAccessUser) {
                    if (specialAccessUserAccess) {

                    } else {
                        throw new JForumAccessException(sakaiUserId);
                    }
                }

                // forum dates
                if (!specialAccessUserAccess) {
                    if (forum.getAccessDates().getOpenDate() != null
                            && now.before(forum.getAccessDates().getOpenDate())) {
                        throw new JForumAccessException(sakaiUserId);
                    } else if (forum.getAccessDates().getAllowUntilDate() != null) {
                        if (now.after(forum.getAccessDates().getAllowUntilDate())) {
                            throw new JForumAccessException(sakaiUserId);
                        }
                    } else if (forum.getAccessDates().getDueDate() != null) {
                        if (now.after(forum.getAccessDates().getDueDate())) {
                            throw new JForumAccessException(sakaiUserId);
                        }
                    }
                }
            }

            // CourseMap blocker check for gradable category/forum/topic
            if (topic.isGradeTopic()) {
                Boolean checkAccess = checkItemAccess(category.getContext(),
                        JForumService.CMItemIdPrefix.TOPIC.getCMItemIdPrefix() + String.valueOf(topic.getId()),
                        sakaiUserId);

                if (!checkAccess) {
                    throw new JForumAccessException(sakaiUserId);
                }
            } else if (forum.getGradeType() == Grade.GradeType.FORUM.getType()) {
                Boolean checkAccess = checkItemAccess(category.getContext(),
                        JForumService.CMItemIdPrefix.FORUM.getCMItemIdPrefix() + String.valueOf(forum.getId()),
                        sakaiUserId);

                if (!checkAccess) {
                    throw new JForumAccessException(sakaiUserId);
                }
            } else if (category.isGradable()) {
                Boolean checkAccess = checkItemAccess(category.getContext(),
                        JForumService.CMItemIdPrefix.CAT.getCMItemIdPrefix() + String.valueOf(category.getId()),
                        sakaiUserId);

                if (!checkAccess) {
                    throw new JForumAccessException(sakaiUserId);
                }
            }
        }

        // if forum/category have dates then topic should not have dates
        if ((category.getAccessDates() != null) && ((category.getAccessDates().getOpenDate() != null)
                || (category.getAccessDates().getDueDate() != null)
                || (category.getAccessDates().getAllowUntilDate() != null))) {
            topic.setAccessDates(null);
        } else if ((forum.getAccessDates() != null)
                && ((forum.getAccessDates().getOpenDate() != null) || (forum.getAccessDates().getDueDate() != null)
                        || (category.getAccessDates().getAllowUntilDate() != null))) {
            topic.setAccessDates(null);
        }

        // participants cannot add dates and grades
        if (participant) {
            topic.setAccessDates(null);
            topic.setGradeTopic(Boolean.FALSE);
            topic.setType(Topic.TopicType.NORMAL.getType());
            topic.setExportTopic(Boolean.FALSE);
        }

        //if forum/category have grades then topic is not gradable.
        if (forum.getGradeType() == Grade.GradeType.FORUM.getType() || category.isGradable()) {
            if (topic.isGradeTopic()) {
                topic.setGradeTopic(Boolean.FALSE);
            }
        } else if (forum.getGradeType() != Grade.GradeType.TOPIC.getType()) {
            topic.setGradeTopic(Boolean.FALSE);
        }

        if (topic.isGradeTopic()) {
            if ((topic.getGrade() != null) && (topic.getGrade().getPoints() != null)) {
                topic.getGrade().setContext(category.getContext());
            } else {
                topic.setGradeTopic(Boolean.FALSE);
            }
        }

        Post post = topic.getPosts().get(0);

        // clean html
        String cleanedPostText = HtmlHelper.clean(post.getText(), true);
        post.setText(cleanedPostText);

        int topicId = topicDao.addNew(topic);

        // post attachments
        Post topicPost = topic.getPosts().get(0);

        if (topicPost.hasAttachments()) {
            try {
                jforumAttachmentService.processPostAttachments(topicPost);
            } catch (JForumAttachmentOverQuotaException e) {
                if (logger.isWarnEnabled()) {
                    logger.warn(" Post '" + topicPost.getSubject() + "' and with id " + topicPost.getId()
                            + " attachment(s) are not saved. " + e.toString());
                }
            } catch (JForumAttachmentBadExtensionException e1) {
                if (logger.isWarnEnabled()) {
                    logger.warn(" Post '" + topicPost.getSubject() + "' and with id " + topicPost.getId()
                            + " attachment(s) are not saved. " + e1.toString());
                }
            }
        }

        // gradable topic add to gradebook
        if (facilitator) {
            ((ForumImpl) forum).setCategory(category);
            ((TopicImpl) topic).setForum(forum);

            if (topic.isGradeTopic()) {
                updateGradeBook(topic);
            }
        }

        // new topic email notification
        /* if site is published and category and forum are open notify the users who opted to receive new topic notifications*/
        boolean notifyTopicToUsers = true;
        if (forum.getAccessType() == Forum.ForumAccess.DENY.getAccessType()) {
            notifyTopicToUsers = false;
        }

        // check for open and due dates
        if (notifyTopicToUsers) {
            Date startDate = null;
            Date endDate = null;

            Date nowDate = Calendar.getInstance().getTime();

            if ((topic.getAccessDates() != null) && ((topic.getAccessDates().getOpenDate() != null)
                    || (topic.getAccessDates().getDueDate() != null))) {
                startDate = topic.getAccessDates().getOpenDate();
                endDate = topic.getAccessDates().getDueDate();
            } else if ((forum.getAccessDates() != null) && ((forum.getAccessDates().getOpenDate() != null)
                    || (forum.getAccessDates().getDueDate() != null))) {
                startDate = forum.getAccessDates().getOpenDate();
                endDate = forum.getAccessDates().getDueDate();
            } else if ((category.getAccessDates() != null) && ((category.getAccessDates().getOpenDate() != null)
                    || (category.getAccessDates().getDueDate() != null))) {
                startDate = category.getAccessDates().getOpenDate();
                endDate = category.getAccessDates().getDueDate();
            }

            if (startDate != null) {
                if (nowDate.getTime() < startDate.getTime()) {
                    notifyTopicToUsers = false;
                }
            }

            if (endDate != null && notifyTopicToUsers) {
                if (nowDate.getTime() > endDate.getTime()) {
                    notifyTopicToUsers = false;
                }
            }
        }

        try {
            if (site == null) {
                site = siteService.getSite(category.getContext());
            }

            if (site != null && site.isPublished() && notifyTopicToUsers) {
                forum.getTopics().add(topic);
                category.getForums().add(forum);

                // notify new topic to users
                notifyNewTopicToUsers(category);
            }
        } catch (IdUnusedException e) {
            if (logger.isWarnEnabled()) {
                logger.warn("createTopic: missing site: " + category.getContext());
            }
        }

        // Index topic post for search
        post = topic.getPosts().get(0);
        jforumSearchIndexingExecutorService.indexPost(post);

        return topicId;
    }

    /**
     * {@inheritDoc}
     */
    public int createTopicPost(Topic topic) throws JForumAccessException {
        if (topic == null || topic.getId() <= 0) {
            throw new IllegalArgumentException("Topic is null or invalid");
        }

        if (topic.getForumId() <= 0) {
            throw new IllegalArgumentException("Topic's forum information is required");
        }

        Forum forum = forumDao.selectById(topic.getForumId());

        if (forum == null) {
            throw new IllegalArgumentException("Topic forum is not a valid forum or not existing");
        }

        Category category = categoryDao.selectById(forum.getCategoryId());

        if (category == null) {
            throw new IllegalArgumentException("Topic forum category is not a valid category or not existing");
        }

        if (topic.getPosts().size() != 1) {
            throw new IllegalArgumentException("Post data is required to create topic post");
        }

        Post post = topic.getPosts().get(0);
        if (post.getPostedBy() == null || post.getPostedBy().getId() <= 0) {
            throw new IllegalArgumentException("Post data has no proper postedby information");
        }

        ((PostImpl) post).setTopicId(topic.getId());
        ((PostImpl) post).setForumId(topic.getForumId());

        String sakaiUserId = post.getPostedBy().getSakaiUserId();

        // access check
        boolean facilitator = jforumSecurityService.isJForumFacilitator(category.getContext(),
                post.getPostedBy().getSakaiUserId());

        boolean participant = false;
        if (!facilitator) {
            participant = jforumSecurityService.isJForumParticipant(category.getContext(),
                    post.getPostedBy().getSakaiUserId());
        }

        if (!(facilitator || participant)) {
            throw new JForumAccessException(sakaiUserId);
        }

        // participants cannot reply to topic if the category/ forum/ topic is not open. check the user special access.
        Site site = null;
        if (participant) {
            // participants cannot reply to topic if the forum has deny access
            if (forum.getAccessType() == Forum.ForumAccess.DENY.getAccessType()) {
                throw new JForumAccessException(sakaiUserId);
            }

            String userSakaiId = post.getPostedBy().getSakaiUserId();

            try {
                site = siteService.getSite(category.getContext());
            } catch (IdUnusedException e) {
                if (logger.isWarnEnabled()) {
                    logger.warn("createTopicPost: missing site: " + category.getContext());
                }
            }

            // if forum access type is GROUPS. Check the user groups.
            if (forum.getAccessType() == Forum.ForumAccess.GROUPS.getAccessType()) {
                boolean userInGroup = false;
                Collection<org.sakaiproject.site.api.Group> userGroups = null;

                // fetch user groups only once
                if (userGroups == null && site != null) {
                    userGroups = site.getGroupsWithMember(userSakaiId);
                }

                if (userGroups != null) {
                    for (org.sakaiproject.site.api.Group grp : userGroups) {
                        if ((forum.getGroups() != null) && (forum.getGroups().contains(grp.getId()))) {
                            userInGroup = true;
                            break;
                        }
                    }
                }

                // user not in any group
                if (!userInGroup) {
                    throw new JForumAccessException(sakaiUserId);
                }
            }

            Date now = Calendar.getInstance().getTime();

            // participants cannot create new topics if the category or forum or topic is not open.
            if ((category.getAccessDates() != null) && ((category.getAccessDates().getOpenDate() != null)
                    || (category.getAccessDates().getDueDate() != null)
                    || (category.getAccessDates().getAllowUntilDate() != null))) {
                if (((category.getAccessDates().getOpenDate() != null))
                        && (category.getAccessDates().getOpenDate().after(now))) {
                    throw new JForumAccessException(sakaiUserId);
                }

                //if ((category.getAccessDates().getDueDate() != null) && (category.getAccessDates().getDueDate().before(now)) && (category.getAccessDates().isLocked()))
                if (category.getAccessDates().getAllowUntilDate() != null) {
                    if (category.getAccessDates().getAllowUntilDate().before(now)) {
                        throw new JForumAccessException(sakaiUserId);
                    }
                } else if (category.getAccessDates().getDueDate() != null) {
                    if (category.getAccessDates().getDueDate().before(now)) {
                        throw new JForumAccessException(sakaiUserId);
                    }
                }
            } else if ((forum.getAccessDates() != null) && ((forum.getAccessDates().getOpenDate() != null)
                    || (forum.getAccessDates().getDueDate() != null)
                    || (forum.getAccessDates().getAllowUntilDate() != null))) {
                // forum special access
                List<SpecialAccess> specialAccessList = forum.getSpecialAccess();
                boolean specialAccessUser = false;
                boolean specialAccessUserAccess = false;

                for (SpecialAccess sa : specialAccessList) {
                    if (sa.getUserIds().contains(new Integer(topic.getPostedBy().getId()))) {
                        specialAccessUser = true;

                        if (!sa.isOverrideStartDate()) {
                            sa.getAccessDates().setOpenDate(forum.getAccessDates().getOpenDate());
                        }

                        if (!sa.isOverrideHideUntilOpen()) {
                            sa.getAccessDates().setHideUntilOpen(forum.getAccessDates().isHideUntilOpen());
                        }

                        if (!sa.isOverrideEndDate()) {
                            sa.getAccessDates().setDueDate(forum.getAccessDates().getDueDate());
                        }

                        if (!sa.isOverrideAllowUntilDate()) {

                            sa.getAccessDates().setAllowUntilDate(forum.getAccessDates().getAllowUntilDate());
                        }

                        if (sa.getAccessDates().getOpenDate() == null) {
                            specialAccessUserAccess = true;
                        } else {
                            if (sa.getAccessDates().getOpenDate().after(now)) {
                                if (!sa.getAccessDates().isHideUntilOpen()) {
                                    specialAccessUserAccess = true;
                                }
                            } else {
                                specialAccessUserAccess = true;
                            }
                        }
                        break;
                    }
                }

                if (specialAccessUser) {
                    if (specialAccessUserAccess) {

                    } else {
                        throw new JForumAccessException(sakaiUserId);
                    }
                }

                // forum dates
                if (!specialAccessUserAccess) {
                    if ((forum.getAccessDates() != null) && (forum.getAccessDates().getOpenDate() != null)) {
                        if (forum.getAccessDates().getOpenDate().after(now)) {
                            throw new JForumAccessException(sakaiUserId);
                        }
                    }

                    //if ((forum.getAccessDates().getDueDate() != null) && (forum.getAccessDates().getDueDate().before(now)) && (forum.getAccessDates().isLocked()))
                    if (forum.getAccessDates().getAllowUntilDate() != null) {
                        if (forum.getAccessDates().getAllowUntilDate().before(now)) {
                            throw new JForumAccessException(sakaiUserId);
                        }
                    } else if (forum.getAccessDates().getDueDate() != null) {
                        if (forum.getAccessDates().getDueDate().before(now)) {
                            throw new JForumAccessException(sakaiUserId);
                        }
                    }
                }
            } else if ((topic.getAccessDates() != null) && ((topic.getAccessDates().getOpenDate() != null)
                    || (topic.getAccessDates().getDueDate() != null)
                    || (topic.getAccessDates().getAllowUntilDate() != null))) {
                // topic special access
                List<SpecialAccess> specialAccessList = topic.getSpecialAccess();
                boolean specialAccessUser = false;
                boolean specialAccessUserAccess = false;

                for (SpecialAccess sa : specialAccessList) {
                    if (sa.getUserIds().contains(new Integer(topic.getPostedBy().getId()))) {
                        specialAccessUser = true;

                        if (!sa.isOverrideStartDate()) {
                            sa.getAccessDates().setOpenDate(topic.getAccessDates().getOpenDate());
                        }

                        if (!sa.isOverrideHideUntilOpen()) {
                            sa.getAccessDates().setHideUntilOpen(topic.getAccessDates().isHideUntilOpen());
                        }

                        if (!sa.isOverrideEndDate()) {
                            sa.getAccessDates().setDueDate(topic.getAccessDates().getDueDate());
                        }

                        if (!sa.isOverrideAllowUntilDate()) {

                            sa.getAccessDates().setAllowUntilDate(topic.getAccessDates().getAllowUntilDate());
                        }

                        if (sa.getAccessDates().getOpenDate() == null) {
                            specialAccessUserAccess = true;
                        } else {
                            if (sa.getAccessDates().getOpenDate().after(now)) {
                                if (!sa.getAccessDates().isHideUntilOpen()) {
                                    specialAccessUserAccess = true;
                                }
                            } else {
                                specialAccessUserAccess = true;
                            }
                        }
                        break;
                    }
                }

                if (specialAccessUser) {
                    if (specialAccessUserAccess) {

                    } else {
                        throw new JForumAccessException(sakaiUserId);
                    }
                }

                // topic dates
                if (!specialAccessUserAccess) {
                    if ((topic.getAccessDates() != null) && (topic.getAccessDates().getOpenDate() != null)) {
                        if (topic.getAccessDates().getOpenDate().after(now)) {
                            throw new JForumAccessException(sakaiUserId);
                        }
                    }

                    //if ((topic.getAccessDates().getDueDate() != null) && (topic.getAccessDates().getDueDate().before(now)) && (topic.getAccessDates().isLocked()))
                    if (topic.getAccessDates().getAllowUntilDate() != null) {
                        if (topic.getAccessDates().getAllowUntilDate().before(now)) {
                            throw new JForumAccessException(sakaiUserId);
                        }
                    } else if (topic.getAccessDates().getDueDate() != null) {
                        if (topic.getAccessDates().getDueDate().before(now)) {
                            throw new JForumAccessException(sakaiUserId);
                        }
                    }
                }
            }

            // CourseMap blocker check for gradable category/forum/topic
            if (topic.isGradeTopic()) {
                Boolean checkAccess = checkItemAccess(category.getContext(),
                        JForumService.CMItemIdPrefix.TOPIC.getCMItemIdPrefix() + String.valueOf(topic.getId()),
                        sakaiUserId);

                if (!checkAccess) {
                    throw new JForumAccessException(sakaiUserId);
                }
            } else if (forum.getGradeType() == Grade.GradeType.FORUM.getType()) {
                Boolean checkAccess = checkItemAccess(category.getContext(),
                        JForumService.CMItemIdPrefix.FORUM.getCMItemIdPrefix() + String.valueOf(forum.getId()),
                        sakaiUserId);

                if (!checkAccess) {
                    throw new JForumAccessException(sakaiUserId);
                }
            } else if (category.isGradable()) {
                Boolean checkAccess = checkItemAccess(category.getContext(),
                        JForumService.CMItemIdPrefix.CAT.getCMItemIdPrefix() + String.valueOf(category.getId()),
                        sakaiUserId);

                if (!checkAccess) {
                    throw new JForumAccessException(sakaiUserId);
                }
            }
        }

        // clean html
        String cleanedPostText = HtmlHelper.clean(post.getText(), true);
        post.setText(cleanedPostText);

        int postId = topicDao.addNewTopicPost(topic);

        // post attachments
        if (post.hasAttachments()) {
            try {
                jforumAttachmentService.processPostAttachments(post);
            } catch (JForumAttachmentOverQuotaException e) {
                if (logger.isWarnEnabled()) {
                    logger.warn(" Post '" + post.getSubject() + "' and with id " + post.getId()
                            + " attachment(s) are not saved. " + e.toString());
                }
            } catch (JForumAttachmentBadExtensionException e1) {
                if (logger.isWarnEnabled()) {
                    logger.warn(" Post '" + post.getSubject() + "' and with id " + post.getId()
                            + " attachment(s) are not saved. " + e1.toString());
                }
            }
        }

        //topic reply email notification for the user who are watching the topic
        /* if site is published and category, forum and topic are open notify the users who opted to watch the topic*/
        boolean notifyTopicToUsers = true;
        if (forum.getAccessType() == Forum.ForumAccess.DENY.getAccessType()) {
            notifyTopicToUsers = false;
        }

        // check for open and due dates
        if (notifyTopicToUsers) {
            Date startDate = null;
            Date endDate = null;

            Date nowDate = Calendar.getInstance().getTime();

            if ((topic.getAccessDates() != null) && ((topic.getAccessDates().getOpenDate() != null)
                    || (topic.getAccessDates().getDueDate() != null))) {
                startDate = topic.getAccessDates().getOpenDate();
                endDate = topic.getAccessDates().getDueDate();
            } else if ((forum.getAccessDates() != null) && ((forum.getAccessDates().getOpenDate() != null)
                    || (forum.getAccessDates().getDueDate() != null))) {
                startDate = forum.getAccessDates().getOpenDate();
                endDate = forum.getAccessDates().getDueDate();
            } else if ((category.getAccessDates() != null) && ((category.getAccessDates().getOpenDate() != null)
                    || (category.getAccessDates().getDueDate() != null))) {
                startDate = category.getAccessDates().getOpenDate();
                endDate = category.getAccessDates().getDueDate();
            }

            if (startDate != null) {
                if (nowDate.getTime() < startDate.getTime()) {
                    notifyTopicToUsers = false;
                }
            }

            if (endDate != null && notifyTopicToUsers) {
                if (nowDate.getTime() > endDate.getTime()) {
                    notifyTopicToUsers = false;
                }
            }
        }

        try {
            if (site == null) {
                site = siteService.getSite(category.getContext());
            }

            if (site != null && site.isPublished() && notifyTopicToUsers) {
                forum.getTopics().add(topic);
                category.getForums().add(forum);

                // notify post reply
                notifyTopicReplyToUsers(category);

            }
        } catch (IdUnusedException e) {
            if (logger.isWarnEnabled()) {
                logger.warn("createTopic: missing site: " + category.getContext());
            }
        } catch (Exception e) {
            if (logger.isErrorEnabled()) {
                logger.error(e.toString(), e);
            }
        }

        try {
            // index post for search
            jforumSearchIndexingExecutorService.indexPost(post);
        } catch (Exception e) {
            if (logger.isErrorEnabled()) {
                logger.error(e.toString(), e);
            }
        }

        return postId;
    }

    /**
     * {@inheritDoc}
     */
    public int createTopicWithAttachments(Topic topic) throws JForumAccessException,
            JForumAttachmentOverQuotaException, JForumAttachmentBadExtensionException {
        if (topic == null) {
            throw new IllegalArgumentException();
        }

        if (topic.getPosts().size() != 1) {
            throw new IllegalArgumentException("Post data in required to create the topic");
        }

        if (topic.getForumId() == 0) {
            throw new IllegalArgumentException("Topic's forum information is required");
        }

        // validate attachments
        Post topicPost = topic.getPosts().get(0);
        if (topicPost.hasAttachments()) {
            try {
                jforumAttachmentService.validatePostAttachments(topicPost);
            } catch (JForumAttachmentOverQuotaException e) {
                throw e;
            } catch (JForumAttachmentBadExtensionException e) {
                throw e;
            }
        }

        return createTopic(topic);
    }

    public void destroy() {
        if (logger.isInfoEnabled())
            logger.info("destroy....");
    }

    /**
     * {@inheritDoc}
     */
    public void evaluateTopic(Topic topic) throws JForumAccessException {

        if (topic == null || !topic.isGradeTopic()) {
            return;
        }

        Grade grade = topic.getGrade();

        if (grade == null) {
            return;
        }

        jforumGradeService.addModifyTopicEvaluations(grade, topic.getEvaluations());
    }

    /**
     * {@inheritDoc}
     */
    public List<Topic> getForumExportTopics(int forumId) {
        if (forumId <= 0)
            throw new IllegalArgumentException();

        return topicDao.selectForumExportTopics(forumId);
    }

    /**
     * {@inheritDoc}
     */
    public LastPostInfo getForumLatestPostInfo(String siteId, int forumId, String sakaiUserId) {
        boolean facilitator = jforumSecurityService.isJForumFacilitator(siteId, sakaiUserId);
        boolean participant = jforumSecurityService.isJForumParticipant(siteId, sakaiUserId);

        User user = jforumUserService.getBySakaiUserId(sakaiUserId);

        LastPostInfo lastPostInfo = null;
        if (facilitator) {
            lastPostInfo = topicDao.selectForumLastPostInfo(forumId, true);
        } else if (participant) {
            lastPostInfo = topicDao.selectForumLastPostInfo(forumId, false);

            if (user != null && (lastPostInfo != null)) {
                // check user special access for topics with dates
                List<SpecialAccess> topicsSpecialAccessList = jforumSpecialAccessService
                        .getTopicsByForumId(forumId);

                Date curDate = new Date(System.currentTimeMillis());

                if (topicsSpecialAccessList.size() > 0) {
                    for (SpecialAccess sa : topicsSpecialAccessList) {
                        if (sa.getUserIds().contains(user.getId())) {
                            if (sa.getAccessDates().getOpenDate() == null
                                    || curDate.after(sa.getAccessDates().getOpenDate())) {
                                LastPostInfo topicLpi = topicDao.selectTopicLastPostInfo(sa.getTopicId());

                                if (topicLpi.getPostTimeMillis() > lastPostInfo.getPostTimeMillis()) {
                                    lastPostInfo = new LastPostInfoImpl((LastPostInfoImpl) topicLpi);
                                }
                            }
                        }
                    }

                }
            }
        }

        return lastPostInfo;
    }

    /**
     * {@inheritDoc}
     */
    public List<Topic> getForumTopicLatestPosttimes(int forumId, int userId, Date after, boolean topicDatesNeeded) {
        return topicDao.selectTopicLatestPosttimesByForum(forumId, userId, after, topicDatesNeeded);
    }

    /**
     * {@inheritDoc}
     */
    public List<Topic> getForumTopics(int forumId) {
        if (forumId <= 0)
            throw new IllegalArgumentException();

        return topicDao.selectForumTopics(forumId);
    }

    /**
     * {@inheritDoc}
     */
    public List<Topic> getForumTopics(int forumId, int startFrom, int count) {
        if (forumId <= 0)
            throw new IllegalArgumentException();

        return topicDao.selectForumTopics(forumId, startFrom, count);
    }

    /**
     * {@inheritDoc}
     */
    public int getForumTopicsAccessibleCount(int forumId) {

        return getAccessibleTopicsCountByForum(forumId);
    }

    /**
     * {@inheritDoc}
     */
    public int getForumTopicsAccessibleMessagesCount(int forumId) {
        return getAccessibleTopicsMessagesCountByForum(forumId);
    }

    /**
     * {@inheritDoc}
     */
    public int getForumTopicsCount(int forumId) {
        if (forumId <= 0)
            throw new IllegalArgumentException();

        return topicDao.selectTopicsCountByForum(forumId);
    }

    /**
     * {@inheritDoc}
     */
    public int getMarkedTopicsCountByForum(int forumId, int userId) {
        return topicDao.selectMarkedTopicsCountByForum(forumId, userId);
    }

    /**
     * {@inheritDoc}
     */
    public Post getPost(int postId) {
        Post post = topicDao.selectPostById(postId);

        if (post != null) {
            PostUtil.preparePostForDisplay(post);
        }

        return post;
    }

    /**
     * {@inheritDoc}
     */
    public Attachment getPostAttachment(int attachmentId) {
        if (attachmentId <= 0)
            throw new IllegalArgumentException("Attachment id should be greater than 0");

        return attachmentDao.selectAttachmentById(attachmentId);
    }

    /**
     * {@inheritDoc}
     */
    public List<Topic> getRecentTopics(String context, int limit) {
        if ((context == null) || (limit < 0))
            throw new IllegalArgumentException("Limit can not be less than 0");

        if (limit > RECENT_TOPICS_LIMIT) {
            limit = RECENT_TOPICS_LIMIT;
        }

        return topicDao.selectRecentTopics(context, limit);
    }

    /**
     * {@inheritDoc}
     */
    public List<Topic> getRecentTopics(String context, int limit, String sakaiUserId) throws JForumAccessException {
        if (context == null || context.trim().length() == 0) {
            throw new IllegalArgumentException("Not a valid site id.");
        }

        if (sakaiUserId == null || sakaiUserId.trim().length() == 0) {
            throw new IllegalArgumentException("Sakai user Id is required.");
        }

        // user access check
        boolean facilitator = jforumSecurityService.isJForumFacilitator(context, sakaiUserId);

        boolean participant = false;
        if (!facilitator) {
            participant = jforumSecurityService.isJForumParticipant(context, sakaiUserId);
        }

        if (!(facilitator || participant)) {
            throw new JForumAccessException(sakaiUserId);
        }

        if (limit < 0)
            throw new IllegalArgumentException("Limit can not be less than 0");

        if (limit > RECENT_TOPICS_LIMIT) {
            limit = RECENT_TOPICS_LIMIT;
        }

        List<Topic> topics = topicDao.selectRecentTopics(context, limit);

        if (participant) {
            // filter topics based on user topic, forum, and categories accessibility
            filterUserRecentTopics(topics, sakaiUserId);
        }

        // mark read or unread
        User user = jforumUserService.getBySakaiUserId(sakaiUserId);
        checkUnreadTopics(context, topics, user);

        return topics;

    }

    /**
     * {@inheritDoc}
     */
    public Topic getTopic(int topicId) {
        if (topicId == 0)
            throw new IllegalArgumentException();

        // for thread-local caching
        String key = cacheKey(String.valueOf(topicId));
        TopicImpl cachedTopic = (TopicImpl) this.threadLocalManager.get(key);
        if (cachedTopic != null) {
            return this.clone(cachedTopic);
        }
        Topic topic = topicDao.selectById(topicId);

        // thread-local cache (copy)
        if (topic != null)
            this.threadLocalManager.set(key, this.clone((TopicImpl) topic));

        return topic;
    }

    /**
     * {@inheritDoc}
     */
    public Topic getTopic(int topicId, String sakaiUserId) throws JForumAccessException {
        Topic topic = getTopic(topicId);

        if (topic == null) {
            return null;
        }

        Forum forum = topic.getForum();

        if (forum == null) {
            return null;
        }

        Category category = forum.getCategory();

        if (category == null) {
            return null;
        }

        boolean facilitator = jforumSecurityService.isJForumFacilitator(category.getContext(), sakaiUserId);

        boolean participant = false;
        if (!facilitator) {
            participant = jforumSecurityService.isJForumParticipant(category.getContext(), sakaiUserId);
        }

        if (!(facilitator || participant)) {
            throw new JForumAccessException(sakaiUserId);
        }

        if (facilitator) {
            ((TopicImpl) topic).setMayPost(Boolean.TRUE);
        } else {
            checkUserTopicAccess(topic, sakaiUserId);
        }

        return topic;
    }

    /**
     * {@inheritDoc}
     */
    public int getTopicDatesCountByCategory(int categoryId) {
        return topicDao.selectTopicDatesCountByCategory(categoryId);
    }

    /**
     * {@inheritDoc}
     */
    public int getTopicDatesCountByForum(int forumId) {
        return topicDao.selectTopicDatesCountByForum(forumId);
    }

    /**
     * {@inheritDoc}
     */
    public Topic getTopicLatestPosttime(int topicId, int userId, Date after, boolean topicDatesNeeded) {
        return topicDao.selectTopicLatestPosttime(topicId, userId, after, topicDatesNeeded);
    }

    /**
     * {@inheritDoc}
     */
    public List<Post> getTopicPosts(int topicId, int startFrom, int count) {
        List<Post> posts = topicDao.selectTopicPostsByLimit(topicId, startFrom, count);

        for (Post post : posts) {
            PostUtil.preparePostForDisplay(post);
        }

        return posts;

    }

    /**
     * {@inheritDoc}
     */
    public List<Post> getTopicPosts(int topicId, int startFrom, int count, String sakaiUserId)
            throws JForumAccessException {
        if (sakaiUserId == null || sakaiUserId.trim().length() == 0) {
            throw new IllegalArgumentException("Sakai user Id is required.");
        }

        if (topicId <= 0)
            throw new IllegalArgumentException("Not a valid topic id.");

        Topic topic = getTopic(topicId, sakaiUserId);

        if (topic == null) {
            return new ArrayList<Post>();
        }

        Forum forum = jforumForumService.getForum(topic.getForumId());
        if (forum == null) {
            return new ArrayList<Post>();
        }
        ((TopicImpl) topic).setForum(forum);

        Category category = jforumCategoryService.getCategory(forum.getCategoryId());
        if (category == null) {
            return new ArrayList<Post>();
        }
        ((ForumImpl) forum).setCategory(category);

        // access check
        boolean facilitator = jforumSecurityService.isJForumFacilitator(category.getContext(), sakaiUserId);

        boolean participant = false;
        if (!facilitator) {
            participant = jforumSecurityService.isJForumParticipant(category.getContext(), sakaiUserId);
        }

        if (!(facilitator || participant)) {
            throw new JForumAccessException(sakaiUserId);
        }

        List<Post> posts = topicDao.selectTopicPostsByLimit(topicId, startFrom, count);

        for (Post post : posts) {
            ((PostImpl) post).setTopic(topic);

            PostUtil.preparePostForDisplay(post);

            // edit permission
            if (facilitator) {
                ((PostImpl) post).setCanEdit(true);
            }

            // posted by user posts count
            int userSitePostsCount = jforumUserService.getUserSitePostsCount(post.getPostedBy().getId(),
                    category.getContext());

            ((UserImpl) post.getPostedBy()).setTotalSitePosts(userSitePostsCount);
        }

        topic.getPosts().clear();
        topic.getPosts().addAll(posts);

        /*add blcokedBy details to topic, forum, category if they are gradable based on sakai user accessing posts
        check category, forum, topic dates and forum, topic special access*/
        if (participant && !posts.isEmpty()) {
            filterUserTopicPosts(topic, sakaiUserId);

        }

        if (!posts.isEmpty()) {
            //update user topic read/unread
            markTopicForumReadStatus(topic, sakaiUserId);

            if (!forum.isUnread()) {
                checkForumUnreadTopics(forum, sakaiUserId);
            }
        }

        return posts;
    }

    /**
     * {@inheritDoc}
     */
    public List<Topic> getTopics(int forumId, int startFrom, int count, String sakaiUserId)
            throws JForumAccessException {
        if (forumId <= 0)
            throw new IllegalArgumentException("Not a valid forum id.");

        if (sakaiUserId == null || sakaiUserId.trim().length() == 0) {
            throw new IllegalArgumentException("Sakai user Id is required.");
        }

        // get forum including user forum unread status
        Forum forum = jforumForumService.getForum(forumId, sakaiUserId);

        if (forum == null) {
            return new ArrayList<Topic>();
        }

        Category category = forum.getCategory();

        if (category == null) {
            return new ArrayList<Topic>();
        }

        ((CategoryImpl) category).setCurrentSakaiUserId(sakaiUserId);

        // access check
        boolean facilitator = jforumSecurityService.isJForumFacilitator(category.getContext(), sakaiUserId);

        boolean participant = false;
        if (!facilitator) {
            participant = jforumSecurityService.isJForumParticipant(category.getContext(), sakaiUserId);
        }

        if (!(facilitator || participant)) {
            throw new JForumAccessException(sakaiUserId);
        }

        List<Topic> topics = topicDao.selectForumTopics(forumId, startFrom, count);

        Date now = new Date();
        for (Topic topic : topics) {
            ((TopicImpl) topic).setForum(forum);

            // publish topic with future open date and hidden until open grade to gradebook after the open date
            if (topic.isGradeTopic()) {
                Grade grade = topic.getGrade();
                if (grade != null && grade.getItemOpenDate() != null && grade.getItemOpenDate().before(now)) {
                    jforumGradeService.updateGradebook(grade, topic);
                }
            }
        }

        forum.getTopics().clear();
        forum.getTopics().addAll(topics);

        if (facilitator) {
            User user = jforumUserService.getBySakaiUserId(sakaiUserId);

            // topics read status
            checkUnreadTopics(forum, user, category.getContext());
        }

        if (participant) {
            // filter user topics
            filterUserForumTopics(forum, sakaiUserId);
        }

        return forum.getTopics();
    }

    /**
     * {@inheritDoc}
     */
    public int getTotalPosts(int topicId) {
        if (topicId <= 0) {
            throw new IllegalArgumentException("Topic id is needed.");
        }

        return topicDao.selectPostsCountByTopic(topicId);
    }

    /**
     * {@inheritDoc}
     */
    public List<Post> getUserCategoryPosts(int categoryId, String sakaiUserId) throws JForumAccessException {
        if (sakaiUserId == null || sakaiUserId.trim().length() == 0) {
            throw new IllegalArgumentException("Sakai user Information is missing or not valid.");
        }

        if (categoryId <= 0)
            throw new IllegalArgumentException("Category information is missing or not valid.");

        Category category = jforumCategoryService.getCategory(categoryId);
        if (category == null) {
            return new ArrayList<Post>();
        }

        // access check
        boolean facilitator = jforumSecurityService.isJForumFacilitator(category.getContext(), sakaiUserId);

        boolean participant = false;
        if (!facilitator) {
            participant = jforumSecurityService.isJForumParticipant(category.getContext(), sakaiUserId);
        }

        if (!(facilitator || participant)) {
            throw new JForumAccessException(sakaiUserId);
        }

        User user = jforumUserService.getBySakaiUserId(sakaiUserId);

        List<Post> posts = topicDao.selectPostsByCategoryByUser(categoryId, user.getId());

        // posted by user posts count
        int userSitePostsCount = jforumUserService.getUserSitePostsCount(user.getId(), category.getContext());

        for (Post post : posts) {
            PostUtil.preparePostForDisplay(post);
            ((UserImpl) post.getPostedBy()).setTotalSitePosts(userSitePostsCount);
        }

        return posts;
    }

    /**
     * {@inheritDoc}
     */
    public List<Post> getUserForumPosts(int forumId, String sakaiUserId) throws JForumAccessException {
        if (sakaiUserId == null || sakaiUserId.trim().length() == 0) {
            throw new IllegalArgumentException("Sakai user Information is missing or not valid.");
        }

        if (forumId <= 0)
            throw new IllegalArgumentException("Forum information is missing or not valid.");

        Forum forum = jforumForumService.getForum(forumId);
        if (forum == null) {
            return new ArrayList<Post>();
        }

        Category category = forum.getCategory();

        // access check
        boolean facilitator = jforumSecurityService.isJForumFacilitator(category.getContext(), sakaiUserId);

        boolean participant = false;
        if (!facilitator) {
            participant = jforumSecurityService.isJForumParticipant(category.getContext(), sakaiUserId);
        }

        if (!(facilitator || participant)) {
            throw new JForumAccessException(sakaiUserId);
        }

        User user = jforumUserService.getBySakaiUserId(sakaiUserId);

        List<Post> posts = topicDao.selectPostsByForumByUser(forumId, user.getId());

        // posted by user posts count
        int userSitePostsCount = jforumUserService.getUserSitePostsCount(user.getId(), category.getContext());

        for (Post post : posts) {
            PostUtil.preparePostForDisplay(post);
            ((UserImpl) post.getPostedBy()).setTotalSitePosts(userSitePostsCount);
        }

        return posts;
    }

    /**
     * {@inheritDoc}
     */
    public List<Topic> getUserForumTopicVisittimes(int forumId, int userId) {
        return topicDao.selectUserTopicVisitTimesByForum(forumId, userId);
    }

    /**
     * {@inheritDoc}
     */
    public Topic getUserTopicMarkTime(int topicId, String sakaiUserId) {
        if ((topicId == 0) || (sakaiUserId == null)) {
            new IllegalArgumentException();
        }

        User user = jforumUserService.getBySakaiUserId(sakaiUserId);

        if (user == null) {
            return null;
        }

        return topicDao.selectMarkTime(topicId, user.getId());
    }

    /**
     * {@inheritDoc}
     */
    public List<Post> getUserTopicPosts(int topicId, String sakaiUserId) throws JForumAccessException {
        if (sakaiUserId == null || sakaiUserId.trim().length() == 0) {
            throw new IllegalArgumentException("Sakai user Information is missing or not valid.");
        }

        if (topicId <= 0)
            throw new IllegalArgumentException("Topic information is missing or not valid.");

        Topic topic = getTopic(topicId);

        Forum forum = topic.getForum();
        if (forum == null) {
            return new ArrayList<Post>();
        }

        Category category = forum.getCategory();

        // access check
        boolean facilitator = jforumSecurityService.isJForumFacilitator(category.getContext(), sakaiUserId);

        boolean participant = false;
        if (!facilitator) {
            participant = jforumSecurityService.isJForumParticipant(category.getContext(), sakaiUserId);
        }

        if (!(facilitator || participant)) {
            throw new JForumAccessException(sakaiUserId);
        }

        User user = jforumUserService.getBySakaiUserId(sakaiUserId);

        List<Post> posts = topicDao.selectPostsByTopicByUser(topicId, user.getId());

        // posted by user posts count
        int userSitePostsCount = jforumUserService.getUserSitePostsCount(user.getId(), category.getContext());

        for (Post post : posts) {
            PostUtil.preparePostForDisplay(post);
            ((UserImpl) post.getPostedBy()).setTotalSitePosts(userSitePostsCount);
        }

        return posts;
    }

    public void init() {
        if (logger.isInfoEnabled())
            logger.info("init....");

        // check if there is an access advisor - if not, that's ok.
        this.accessAdvisor = (AccessAdvisor) ComponentManager.get(AccessAdvisor.class);
    }

    /**
     * {@inheritDoc}
     */
    public boolean isUserSubscribedToTopic(int topicId, String sakaiUserId) {
        if ((topicId <= 0) || (sakaiUserId == null || sakaiUserId.trim().length() == 0)) {
            new IllegalArgumentException("Not a valid topic id or user id.");
        }

        User user = jforumUserService.getBySakaiUserId(sakaiUserId);

        if (user != null) {
            return topicDao.isUserSubscribed(topicId, user.getId());
        }

        return false;
    }

    /**
     * {@inheritDoc}
     */
    public void lockUnlock(int[] topicId, Topic.TopicStatus status) {
        if (topicId.length == 0) {
            return;
        }

        for (int count = 0; count < topicId.length; count++) {
            topicDao.lockUnlock(topicId[count], status);
        }
    }

    /**
     * {@inheritDoc}
     */
    public void markTopicRead(int topicId, String sakaiUserId, Date markTime, boolean isRead) {
        if ((topicId == 0) || (sakaiUserId == null) || (markTime == null)) {
            new IllegalArgumentException();
        }

        User user = jforumUserService.getBySakaiUserId(sakaiUserId);

        if (user == null) {
            return;
        }

        topicDao.updateTopicMarkTime(topicId, user.getId(), markTime, isRead);
    }

    /**
     * {@inheritDoc}
     */
    public void modifyPost(Post post) throws JForumAccessException, JForumAttachmentOverQuotaException,
            JForumAttachmentBadExtensionException {
        if (post == null) {
            throw new IllegalArgumentException("Post information is missing");
        }

        if (post.getId() <= 0) {
            throw new IllegalArgumentException("Post id is missing");
        }

        if (post.getTopicId() <= 0) {
            throw new IllegalArgumentException("Post's topic is required");
        }

        if (post.getForumId() <= 0) {
            throw new IllegalArgumentException("Topic's forum id is required");
        }

        Post exisPost = topicDao.selectPostById(post.getId());

        if (exisPost == null) {
            throw new IllegalArgumentException("Post not existing");
        }

        Topic exisTopic = topicDao.selectById(post.getTopicId());

        if (exisTopic == null) {
            throw new IllegalArgumentException("Topic is not existing");
        }

        if (exisTopic.getForumId() != post.getForumId()) {
            throw new IllegalArgumentException("Post forum information cannot be verified");
        }

        Forum forum = forumDao.selectById(exisTopic.getForumId());

        if (forum == null) {
            throw new IllegalArgumentException("Forum is not existing");
        }

        Category category = categoryDao.selectById(forum.getCategoryId());

        if (category == null) {
            throw new IllegalArgumentException("Category is not existing");
        }

        if (post.getPostedBy() == null || post.getPostedBy().getId() <= 0) {
            throw new IllegalArgumentException("Post data has no proper postedby information");
        }

        Post existingPost = topicDao.selectPostById(post.getId());

        //if ((existingPost.getTopicId() != post.getTopicId()) || (existingPost.getForumId() != post.getForumId()) || (!post.getPostedBy().getSakaiUserId().trim().equalsIgnoreCase(existingPost.getPostedBy().getSakaiUserId().trim())))
        if ((existingPost.getTopicId() != post.getTopicId()) || (existingPost.getForumId() != post.getForumId())) {
            throw new IllegalArgumentException("Post's information is not matched with the existing post");
        }

        String sakaiUserId = post.getPostedBy().getSakaiUserId();

        // access check
        boolean facilitator = jforumSecurityService.isJForumFacilitator(category.getContext(),
                post.getPostedBy().getSakaiUserId());

        boolean participant = false;
        if (!facilitator) {
            participant = jforumSecurityService.isJForumParticipant(category.getContext(),
                    post.getPostedBy().getSakaiUserId());
        }

        if (!(facilitator || participant)) {
            throw new JForumAccessException(sakaiUserId);
        }

        // participant post edit permission. participants cannot edit the post if topic is locked or forum is read only or category/forum/topic has dates and locked after the end date
        if (participant) {
            if (!post.getPostedBy().getSakaiUserId().trim()
                    .equalsIgnoreCase(existingPost.getPostedBy().getSakaiUserId().trim())) {
                throw new JForumAccessException(sakaiUserId);
            }

            // participants cannot update post if forum has deny access
            if (forum.getAccessType() == Forum.ForumAccess.DENY.getAccessType()) {
                throw new JForumAccessException(sakaiUserId);
            }

            if (forum.getType() == Forum.ForumType.READ_ONLY.getType()) {
                throw new JForumAccessException(sakaiUserId);
            }

            if (exisTopic.getStatus() == Topic.TopicStatus.LOCKED.getStatus()) {
                throw new JForumAccessException(sakaiUserId);
            }

            Date currentTime = Calendar.getInstance().getTime();

            // category due date passed and category locked
            if ((category.getAccessDates() != null) && ((category.getAccessDates().getOpenDate() != null)
                    || (category.getAccessDates().getDueDate() != null)
                    || (category.getAccessDates().getAllowUntilDate() != null))) {
                if (category.getAccessDates().getAllowUntilDate() != null) {
                    if (category.getAccessDates().getAllowUntilDate().before(currentTime)) {
                        throw new JForumAccessException(sakaiUserId);
                    }
                } else if (category.getAccessDates().getDueDate() != null) {
                    //if (category.getAccessDates().getDueDate().before(currentTime) && category.getAccessDates().isLocked())
                    if (category.getAccessDates().getDueDate().before(currentTime)) {
                        throw new JForumAccessException(sakaiUserId);
                    }
                }
            }

            //forum dates and special access
            List<SpecialAccess> forumSpecialAccessList = forum.getSpecialAccess();
            boolean forumSpecialAccessUser = false;
            boolean forumSpecialAccessUserAccess = false;
            SpecialAccess userSa = null;

            User user = jforumUserService.getBySakaiUserId(sakaiUserId);
            if (user != null && ((forum.getAccessDates() != null) && ((forum.getAccessDates().getOpenDate() != null)
                    || (forum.getAccessDates().getDueDate() != null)
                    || (forum.getAccessDates().getAllowUntilDate() != null)))) {
                for (SpecialAccess sa : forumSpecialAccessList) {
                    if (sa.getUserIds().contains(new Integer(user.getId()))) {
                        userSa = sa;

                        forumSpecialAccessUser = true;

                        if (!sa.isOverrideStartDate()) {
                            sa.getAccessDates().setOpenDate(forum.getAccessDates().getOpenDate());
                        }

                        if (!sa.isOverrideHideUntilOpen()) {
                            sa.getAccessDates().setHideUntilOpen(forum.getAccessDates().isHideUntilOpen());
                        }

                        if (!sa.isOverrideEndDate()) {
                            sa.getAccessDates().setDueDate(forum.getAccessDates().getDueDate());
                        }

                        if (!sa.isOverrideAllowUntilDate()) {

                            sa.getAccessDates().setAllowUntilDate(forum.getAccessDates().getAllowUntilDate());
                        }

                        if (sa.getAccessDates().getOpenDate() == null) {
                            forumSpecialAccessUserAccess = true;
                        } else {
                            if (sa.getAccessDates().getOpenDate().after(currentTime)) {
                                if (!sa.getAccessDates().isHideUntilOpen()) {
                                    forumSpecialAccessUserAccess = true;
                                }
                            } else {
                                forumSpecialAccessUserAccess = true;
                            }
                        }
                        break;
                    }
                }
            }

            forum.getSpecialAccess().clear();
            if (forumSpecialAccessUser) {
                forum.getSpecialAccess().add(userSa);
            }
            // forum dates
            else if ((forum.getAccessDates() != null) && (forum.getAccessDates().getOpenDate() != null)) {
                if (forum.getAccessDates().getOpenDate().after(currentTime)) {
                    throw new JForumAccessException(sakaiUserId);
                }
            }

            // forum special access user
            if (forumSpecialAccessUser) {
                if (forumSpecialAccessUserAccess && userSa != null) {
                    //if (userSa.getAccessDates().getDueDate() != null && userSa.getAccessDates().getDueDate().before(currentTime) && userSa.getAccessDates().isLocked())
                    if (userSa.getAccessDates().getAllowUntilDate() != null) {
                        if (userSa.getAccessDates().getAllowUntilDate().before(currentTime)) {
                            throw new JForumAccessException(sakaiUserId);
                        }
                    } else if (userSa.getAccessDates().getDueDate() != null) {
                        if (userSa.getAccessDates().getDueDate().before(currentTime)) {
                            throw new JForumAccessException(sakaiUserId);
                        }
                    }
                }
            }
            // forum due date passed and forum locked
            else if ((forum.getAccessDates() != null) && ((forum.getAccessDates().getDueDate() != null)
                    || (forum.getAccessDates().getAllowUntilDate() != null))) {
                //if (forum.getAccessDates().getDueDate().before(currentTime) && forum.getAccessDates().isLocked())
                if (forum.getAccessDates().getAllowUntilDate() != null) {
                    if (forum.getAccessDates().getAllowUntilDate().before(currentTime)) {
                        throw new JForumAccessException(sakaiUserId);
                    }
                } else if (forum.getAccessDates().getDueDate() != null) {
                    if (forum.getAccessDates().getDueDate().before(currentTime)) {
                        throw new JForumAccessException(sakaiUserId);
                    }
                }
            }

            //topic dates and special access
            List<SpecialAccess> topicSpecialAccessList = exisTopic.getSpecialAccess();
            boolean topicSpecialAccessUser = false;
            boolean topicSpecialAccessUserAccess = false;
            SpecialAccess userTopicSa = null;

            if ((user != null)
                    && ((exisTopic.getAccessDates() != null) && ((exisTopic.getAccessDates().getOpenDate() != null)
                            || (exisTopic.getAccessDates().getDueDate() != null)))) {
                for (SpecialAccess sa : topicSpecialAccessList) {
                    if (sa.getUserIds().contains(new Integer(user.getId()))) {
                        userTopicSa = sa;

                        topicSpecialAccessUser = true;

                        if (!sa.isOverrideStartDate()) {
                            sa.getAccessDates().setOpenDate(exisTopic.getAccessDates().getOpenDate());
                        }

                        if (!sa.isOverrideHideUntilOpen()) {
                            sa.getAccessDates().setHideUntilOpen(exisTopic.getAccessDates().isHideUntilOpen());
                        }

                        if (!sa.isOverrideEndDate()) {
                            sa.getAccessDates().setDueDate(exisTopic.getAccessDates().getDueDate());
                        }

                        if (!sa.isOverrideAllowUntilDate()) {

                            sa.getAccessDates().setAllowUntilDate(exisTopic.getAccessDates().getAllowUntilDate());
                        }

                        if (sa.getAccessDates().getOpenDate() == null) {
                            topicSpecialAccessUser = true;
                        } else {
                            if (sa.getAccessDates().getOpenDate().after(currentTime)) {
                                if (!sa.getAccessDates().isHideUntilOpen()) {
                                    topicSpecialAccessUser = true;
                                }
                            } else {
                                topicSpecialAccessUser = true;
                            }
                        }
                        break;
                    }
                }
            }

            exisTopic.getSpecialAccess().clear();
            if (topicSpecialAccessUser) {
                exisTopic.getSpecialAccess().add(userTopicSa);
            }
            // topic dates
            else if ((exisTopic.getAccessDates() != null) && (exisTopic.getAccessDates().getOpenDate() != null)) {
                if (exisTopic.getAccessDates().getOpenDate().after(currentTime)) {
                    throw new JForumAccessException(user.getSakaiUserId());
                }
            }

            if (topicSpecialAccessUser) {
                if (topicSpecialAccessUserAccess && userTopicSa != null) {
                    //if (userTopicSa.getAccessDates().getDueDate() != null && userTopicSa.getAccessDates().getDueDate().before(currentTime) && userTopicSa.getAccessDates().isLocked())
                    if (userTopicSa.getAccessDates().getAllowUntilDate() != null) {
                        if (userTopicSa.getAccessDates().getAllowUntilDate().before(currentTime)) {
                            throw new JForumAccessException(sakaiUserId);
                        }
                    } else if (userTopicSa.getAccessDates().getDueDate() != null) {
                        if (userTopicSa.getAccessDates().getDueDate().before(currentTime)) {
                            throw new JForumAccessException(sakaiUserId);
                        }
                    }
                }
            }
            // topic due date passed and topic locked
            else if ((exisTopic.getAccessDates() != null) && ((exisTopic.getAccessDates().getDueDate() != null)
                    || (exisTopic.getAccessDates().getAllowUntilDate() != null))) {
                //if (exisTopic.getAccessDates().getDueDate().before(currentTime) && exisTopic.getAccessDates().isLocked())
                if (exisTopic.getAccessDates().getAllowUntilDate() != null) {
                    if (exisTopic.getAccessDates().getAllowUntilDate().before(currentTime)) {
                        throw new JForumAccessException(sakaiUserId);
                    }
                } else if (exisTopic.getAccessDates().getDueDate() != null) {
                    if (exisTopic.getAccessDates().getDueDate().before(currentTime)) {
                        throw new JForumAccessException(sakaiUserId);
                    }
                }
            }
        }

        // validate attachments
        if (post.hasAttachments()) {
            jforumAttachmentService.validateEditPostAttachments(post);
        }

        // clean html
        String cleanedPostText = HtmlHelper.clean(post.getText(), true);
        post.setText(cleanedPostText);

        //save modified post
        topicDao.updateTopicPost(post);

        //if it's first post update topic title. if facilitator update topic type,  export topic, dates, grades as needed.
        if (exisTopic.getFirstPostId() == post.getId()) {
            Topic topic = post.getTopic();
            exisTopic.setTitle(post.getSubject());
            Grade exisGrade = null;

            if (facilitator) {
                if (exisTopic.isGradeTopic()) {
                    if (exisTopic.getGrade() != null) {
                        exisGrade = exisTopic.getGrade();
                    }
                }

                /*update topic type, export topic, dates, grade etc*/
                // topic type
                exisTopic.setType(topic.getType());

                // topic - mark for export
                exisTopic.setExportTopic(topic.isExportTopic());

                //topic dates - check forum and category dates
                boolean catForumDates = false;
                if ((forum.getAccessDates() != null) && (forum.getAccessDates().getOpenDate() != null
                        || forum.getAccessDates().getDueDate() != null
                        || forum.getAccessDates().getAllowUntilDate() != null)) {
                    catForumDates = true;
                } else if ((category.getAccessDates() != null) && (category.getAccessDates().getOpenDate() != null
                        || category.getAccessDates().getDueDate() != null
                        || category.getAccessDates().getAllowUntilDate() != null)) {
                    catForumDates = true;
                }

                if (catForumDates) {
                    exisTopic.setAccessDates(null);
                } else {
                    exisTopic.setAccessDates(topic.getAccessDates());
                }

                //topic grades - check forum and category grades
                if (topic.isGradeTopic()) {
                    if (forum.getGradeType() == Grade.GradeType.FORUM.getType() || category.isGradable()) {
                        if (exisTopic.isGradeTopic()) {
                            exisTopic.setGradeTopic(Boolean.FALSE);
                        }
                    } else if (forum.getGradeType() != Grade.GradeType.TOPIC.getType()) {
                        exisTopic.setGradeTopic(Boolean.FALSE);
                    }

                    if (exisTopic.isGradeTopic()) {
                        exisTopic.getGrade().setPoints(topic.getGrade().getPoints());
                        exisTopic.getGrade().setMinimumPosts(topic.getGrade().getMinimumPosts());
                        exisTopic.getGrade().setMinimumPostsRequired(topic.getGrade().isMinimumPostsRequired());
                        exisTopic.getGrade().setAddToGradeBook(topic.getGrade().isAddToGradeBook());
                    } else {
                        if (forum.getGradeType() == Grade.GradeType.TOPIC.getType()) {
                            Grade grade = new GradeImpl();
                            grade.setPoints(topic.getGrade().getPoints());
                            grade.setMinimumPosts(topic.getGrade().getMinimumPosts());
                            grade.setMinimumPostsRequired(topic.getGrade().isMinimumPostsRequired());
                            grade.setContext(category.getContext());
                            grade.setAddToGradeBook(topic.getGrade().isAddToGradeBook());

                            exisTopic.setGradeTopic(Boolean.TRUE);
                            exisTopic.setGrade(grade);
                        }
                    }
                } else {
                    exisTopic.setGradeTopic(Boolean.FALSE);
                }
            }

            // save topic
            topicDao.updateTopic(exisTopic);

            if (facilitator) {
                //send or remove from grade book if gradable topic
                ((ForumImpl) forum).setCategory(category);
                ((TopicImpl) exisTopic).setForum(forum);

                if (exisTopic.isGradeTopic() && exisTopic.getGrade().isAddToGradeBook()) {
                    updateGradeBook(exisTopic);
                } else {
                    if (exisGrade != null) {
                        removeEntryFromGradeBook(exisGrade);
                    }
                }
            }

        }

        // post attachments
        jforumAttachmentService.processEditPostAttachments(post);

        // index post for search
        jforumSearchIndexingExecutorService.indexPost(post);

        // clear the cache
        clearCache(exisTopic);
    }

    /**
     * {@inheritDoc}
     */
    public void modifyTopicPost(Post post) throws JForumAccessException, JForumAttachmentOverQuotaException,
            JForumAttachmentBadExtensionException, JForumGradesModificationException {
        if (post.getId() <= 0) {
            throw new IllegalArgumentException("Post id is missing");
        }

        if (post.getTopicId() <= 0) {
            throw new IllegalArgumentException("Post's topic is required");
        }

        if (post.getForumId() <= 0) {
            throw new IllegalArgumentException("Topic's forum id is required");
        }

        Post exisPost = topicDao.selectPostById(post.getId());

        if (exisPost == null) {
            throw new IllegalArgumentException("Post not existing");
        }

        Topic exisTopic = topicDao.selectById(post.getTopicId());

        if (exisTopic == null) {
            throw new IllegalArgumentException("Topic is not existing");
        }

        if (exisTopic.getForumId() != post.getForumId()) {
            throw new IllegalArgumentException("Post forum information cannot be verified");
        }

        Forum forum = forumDao.selectById(exisTopic.getForumId());

        if (forum == null) {
            throw new IllegalArgumentException("Forum is not existing");
        }

        Category category = categoryDao.selectById(forum.getCategoryId());

        if (category == null) {
            throw new IllegalArgumentException("Category is not existing");
        }

        if (post.getPostedBy() == null || post.getPostedBy().getId() <= 0) {
            throw new IllegalArgumentException("Post data has no proper postedby information");
        }

        Post existingPost = topicDao.selectPostById(post.getId());

        //if ((existingPost.getTopicId() != post.getTopicId()) || (existingPost.getForumId() != post.getForumId()) || (!post.getPostedBy().getSakaiUserId().trim().equalsIgnoreCase(existingPost.getPostedBy().getSakaiUserId().trim())))
        if ((existingPost.getTopicId() != post.getTopicId()) || (existingPost.getForumId() != post.getForumId())) {
            throw new IllegalArgumentException("Post's information is not matched with the existing post");
        }

        String sakaiUserId = post.getPostedBy().getSakaiUserId();

        // access check
        boolean facilitator = jforumSecurityService.isJForumFacilitator(category.getContext(),
                post.getPostedBy().getSakaiUserId());

        boolean participant = false;
        if (!facilitator) {
            participant = jforumSecurityService.isJForumParticipant(category.getContext(),
                    post.getPostedBy().getSakaiUserId());
        }

        if (!(facilitator || participant)) {
            throw new JForumAccessException(sakaiUserId);
        }

        // participant post edit permission. participants cannot edit the post if topic is locked or forum is read only or category/forum/topic has dates and locked after the end date
        if (participant) {
            if (!post.getPostedBy().getSakaiUserId().trim()
                    .equalsIgnoreCase(existingPost.getPostedBy().getSakaiUserId().trim())) {
                throw new JForumAccessException(sakaiUserId);
            }

            // participants cannot update post if forum has deny access
            if (forum.getAccessType() == Forum.ForumAccess.DENY.getAccessType()) {
                throw new JForumAccessException(sakaiUserId);
            }

            if (forum.getType() == Forum.ForumType.READ_ONLY.getType()) {
                throw new JForumAccessException(sakaiUserId);
            }

            if (exisTopic.getStatus() == Topic.TopicStatus.LOCKED.getStatus()) {
                throw new JForumAccessException(sakaiUserId);
            }

            Date currentTime = Calendar.getInstance().getTime();

            // category due date passed and category locked
            if ((category.getAccessDates() != null) && ((category.getAccessDates().getOpenDate() != null)
                    || (category.getAccessDates().getDueDate() != null)
                    || (category.getAccessDates().getAllowUntilDate() != null))) {
                if (category.getAccessDates().getAllowUntilDate() != null) {
                    if (category.getAccessDates().getAllowUntilDate().before(currentTime)) {
                        throw new JForumAccessException(sakaiUserId);
                    }
                } else if (category.getAccessDates().getDueDate() != null) {
                    if (category.getAccessDates().getDueDate().before(currentTime)) {
                        throw new JForumAccessException(sakaiUserId);
                    }
                }
            }

            //forum dates and special access
            List<SpecialAccess> forumSpecialAccessList = forum.getSpecialAccess();
            boolean forumSpecialAccessUser = false;
            boolean forumSpecialAccessUserAccess = false;
            SpecialAccess userSa = null;

            User user = jforumUserService.getBySakaiUserId(sakaiUserId);
            if (user != null && ((forum.getAccessDates() != null) && ((forum.getAccessDates().getOpenDate() != null)
                    || (forum.getAccessDates().getDueDate() != null)
                    || (forum.getAccessDates().getAllowUntilDate() != null)))) {
                for (SpecialAccess sa : forumSpecialAccessList) {
                    if (sa.getUserIds().contains(new Integer(user.getId()))) {
                        userSa = sa;

                        forumSpecialAccessUser = true;

                        if (!sa.isOverrideStartDate()) {
                            sa.getAccessDates().setOpenDate(forum.getAccessDates().getOpenDate());
                        }

                        if (!sa.isOverrideHideUntilOpen()) {
                            sa.getAccessDates().setHideUntilOpen(forum.getAccessDates().isHideUntilOpen());
                        }

                        if (!sa.isOverrideEndDate()) {
                            sa.getAccessDates().setDueDate(forum.getAccessDates().getDueDate());
                        }

                        if (!sa.isOverrideAllowUntilDate()) {

                            sa.getAccessDates().setAllowUntilDate(forum.getAccessDates().getAllowUntilDate());
                        }

                        if (sa.getAccessDates().getOpenDate() == null) {
                            forumSpecialAccessUserAccess = true;
                        } else {
                            if (sa.getAccessDates().getOpenDate().after(currentTime)) {
                                if (!sa.getAccessDates().isHideUntilOpen()) {
                                    forumSpecialAccessUserAccess = true;
                                }
                            } else {
                                forumSpecialAccessUserAccess = true;
                            }
                        }
                        break;
                    }
                }
            }

            forum.getSpecialAccess().clear();
            if (forumSpecialAccessUser) {
                forum.getSpecialAccess().add(userSa);
            }
            // forum dates
            else if ((forum.getAccessDates() != null) && ((forum.getAccessDates().getOpenDate() != null)
                    || (forum.getAccessDates().getAllowUntilDate() != null))) {
                if (forum.getAccessDates().getAllowUntilDate() != null) {
                    if (forum.getAccessDates().getAllowUntilDate().before(currentTime)) {
                        throw new JForumAccessException(sakaiUserId);
                    }
                } else if (forum.getAccessDates().getDueDate() != null) {
                    if (forum.getAccessDates().getDueDate().before(currentTime)) {
                        throw new JForumAccessException(sakaiUserId);
                    }
                }
            }

            // forum special access user
            if (forumSpecialAccessUser) {
                if (forumSpecialAccessUserAccess && userSa != null) {
                    //if (userSa.getAccessDates().getDueDate() != null && userSa.getAccessDates().getDueDate().before(currentTime) && userSa.getAccessDates().isLocked())
                    if (userSa.getAccessDates().getAllowUntilDate() != null) {
                        if (userSa.getAccessDates().getAllowUntilDate().before(currentTime)) {
                            throw new JForumAccessException(sakaiUserId);
                        }
                    } else if (userSa.getAccessDates().getDueDate() != null) {
                        if (userSa.getAccessDates().getDueDate().before(currentTime)) {
                            throw new JForumAccessException(sakaiUserId);
                        }
                    }
                }
            }
            // forum due date passed and forum locked
            else if ((forum.getAccessDates() != null) && ((forum.getAccessDates().getDueDate() != null)
                    || (forum.getAccessDates().getAllowUntilDate() != null))) {
                //if (forum.getAccessDates().getDueDate().before(currentTime) && forum.getAccessDates().isLocked())
                if (forum.getAccessDates().getAllowUntilDate() != null) {
                    if (forum.getAccessDates().getAllowUntilDate().before(currentTime)) {
                        throw new JForumAccessException(sakaiUserId);
                    }
                } else if (forum.getAccessDates().getDueDate() != null) {
                    if (forum.getAccessDates().getDueDate().before(currentTime)) {
                        throw new JForumAccessException(sakaiUserId);
                    }
                }
            }

            //topic dates and special access
            List<SpecialAccess> topicSpecialAccessList = exisTopic.getSpecialAccess();
            boolean topicSpecialAccessUser = false;
            boolean topicSpecialAccessUserAccess = false;
            SpecialAccess userTopicSa = null;

            if ((user != null)
                    && ((exisTopic.getAccessDates() != null) && ((exisTopic.getAccessDates().getOpenDate() != null)
                            || (exisTopic.getAccessDates().getDueDate() != null)
                            || (exisTopic.getAccessDates().getAllowUntilDate() != null)))) {
                for (SpecialAccess sa : topicSpecialAccessList) {
                    if (sa.getUserIds().contains(new Integer(user.getId()))) {
                        userTopicSa = sa;

                        topicSpecialAccessUser = true;

                        if (!sa.isOverrideStartDate()) {
                            sa.getAccessDates().setOpenDate(exisTopic.getAccessDates().getOpenDate());
                        }

                        if (!sa.isOverrideHideUntilOpen()) {
                            sa.getAccessDates().setHideUntilOpen(exisTopic.getAccessDates().isHideUntilOpen());
                        }

                        if (!sa.isOverrideEndDate()) {
                            sa.getAccessDates().setDueDate(exisTopic.getAccessDates().getDueDate());
                        }

                        if (!sa.isOverrideAllowUntilDate()) {

                            sa.getAccessDates().setAllowUntilDate(exisTopic.getAccessDates().getAllowUntilDate());
                        }

                        if (sa.getAccessDates().getOpenDate() == null) {
                            topicSpecialAccessUserAccess = true;
                        } else {
                            if (sa.getAccessDates().getOpenDate().after(currentTime)) {
                                if (!sa.getAccessDates().isHideUntilOpen()) {
                                    topicSpecialAccessUserAccess = true;
                                }
                            } else {
                                topicSpecialAccessUserAccess = true;
                            }
                        }
                        break;
                    }
                }
            }

            exisTopic.getSpecialAccess().clear();
            if (topicSpecialAccessUser) {
                exisTopic.getSpecialAccess().add(userTopicSa);
            }
            // topic dates
            else if ((exisTopic.getAccessDates() != null) && (exisTopic.getAccessDates().getOpenDate() != null)) {
                if (exisTopic.getAccessDates().getOpenDate().after(currentTime)) {
                    throw new JForumAccessException(user.getSakaiUserId());
                }
            }

            if (topicSpecialAccessUser) {
                if (topicSpecialAccessUserAccess && userTopicSa != null) {
                    //if (userTopicSa.getAccessDates().getDueDate() != null && userTopicSa.getAccessDates().getDueDate().before(currentTime) && userTopicSa.getAccessDates().isLocked())
                    if (userTopicSa.getAccessDates().getAllowUntilDate() != null) {
                        if (userTopicSa.getAccessDates().getAllowUntilDate().before(currentTime)) {
                            throw new JForumAccessException(sakaiUserId);
                        }
                    } else if (userTopicSa.getAccessDates().getDueDate() != null) {
                        if (userTopicSa.getAccessDates().getDueDate().before(currentTime)) {
                            throw new JForumAccessException(sakaiUserId);
                        }
                    }
                }
            }
            // topic due date passed and topic locked
            else if ((exisTopic.getAccessDates() != null) && (exisTopic.getAccessDates().getDueDate() != null
                    || exisTopic.getAccessDates().getAllowUntilDate() != null)) {
                if (exisTopic.getAccessDates().getAllowUntilDate() != null) {
                    if (exisTopic.getAccessDates().getAllowUntilDate().before(currentTime)) {
                        throw new JForumAccessException(sakaiUserId);
                    }
                } else if (exisTopic.getAccessDates().getDueDate() != null) {
                    if (exisTopic.getAccessDates().getDueDate().before(currentTime)) {
                        throw new JForumAccessException(sakaiUserId);
                    }
                }
            }
        }

        // validate attachments
        if (post.hasAttachments()) {
            jforumAttachmentService.validateEditPostAttachments(post);
        }

        //if it's first post update topic title. if facilitator update topic type,  export topic, dates, grades as needed.
        if (exisTopic.getFirstPostId() == post.getId()) {
            Topic topic = post.getTopic();
            exisTopic.setTitle(post.getSubject());
            Grade exisGrade = null;

            if (facilitator) {
                if (exisTopic.isGradeTopic()) {
                    if (exisTopic.getGrade() != null) {
                        exisGrade = exisTopic.getGrade();
                    }
                }

                /*update topic type, export topic, dates, grade etc*/
                // topic type
                exisTopic.setType(topic.getType());

                // topic - mark for export
                exisTopic.setExportTopic(topic.isExportTopic());

                //topic dates - check forum and category dates
                boolean catForumDates = false;
                if ((forum.getAccessDates() != null) && (forum.getAccessDates().getOpenDate() != null
                        || forum.getAccessDates().getDueDate() != null
                        || forum.getAccessDates().getAllowUntilDate() != null)) {
                    catForumDates = true;
                } else if ((category.getAccessDates() != null) && (category.getAccessDates().getOpenDate() != null
                        || category.getAccessDates().getDueDate() != null
                        || category.getAccessDates().getAllowUntilDate() != null)) {
                    catForumDates = true;
                }

                if (catForumDates) {
                    exisTopic.setAccessDates(null);
                } else {
                    exisTopic.setAccessDates(topic.getAccessDates());
                }

                //topic grades - check forum and category grades
                if (topic.isGradeTopic()) {
                    if (forum.getGradeType() == Grade.GradeType.FORUM.getType() || category.isGradable()) {
                        if (exisTopic.isGradeTopic()) {
                            exisTopic.setGradeTopic(Boolean.FALSE);
                        }
                    } else if (forum.getGradeType() != Grade.GradeType.TOPIC.getType()) {
                        exisTopic.setGradeTopic(Boolean.FALSE);
                    }

                    if (exisTopic.isGradeTopic()) {
                        exisTopic.getGrade().setPoints(topic.getGrade().getPoints());
                        exisTopic.getGrade().setMinimumPosts(topic.getGrade().getMinimumPosts());
                        exisTopic.getGrade().setMinimumPostsRequired(topic.getGrade().isMinimumPostsRequired());
                        exisTopic.getGrade().setAddToGradeBook(topic.getGrade().isAddToGradeBook());
                    } else {
                        if (forum.getGradeType() == Grade.GradeType.TOPIC.getType()) {
                            Grade grade = new GradeImpl();
                            grade.setPoints(topic.getGrade().getPoints());
                            grade.setMinimumPosts(topic.getGrade().getMinimumPosts());
                            grade.setMinimumPostsRequired(topic.getGrade().isMinimumPostsRequired());
                            grade.setContext(category.getContext());
                            grade.setAddToGradeBook(topic.getGrade().isAddToGradeBook());

                            exisTopic.setGradeTopic(Boolean.TRUE);
                            exisTopic.setGrade(grade);
                        }
                    }
                } else {
                    if (exisTopic.isGradeTopic()) {
                        // if exiting gradable topic is graded don't allow to change the grade type
                        List<Evaluation> topicEvaluations = jforumGradeService
                                .getTopicEvaluations(topic.getForumId(), topic.getId());

                        if (!topicEvaluations.isEmpty()) {
                            throw new JForumGradesModificationException(
                                    "Item with title: " + topic.getTitle() + " has been graded");
                        }
                    }
                    exisTopic.setGradeTopic(Boolean.FALSE);
                }
            }

            // clean html
            String cleanedPostText = HtmlHelper.clean(post.getText(), true);
            post.setText(cleanedPostText);

            //save modified post
            topicDao.updateTopicPost(post);

            // save topic
            topicDao.updateTopic(exisTopic);

            if (facilitator) {
                //send or remove from grade book if gradable topic
                ((ForumImpl) forum).setCategory(category);
                ((TopicImpl) exisTopic).setForum(forum);

                if (exisTopic.isGradeTopic() && exisTopic.getGrade().isAddToGradeBook()) {
                    updateGradeBook(exisTopic);
                } else {
                    if (exisGrade != null) {
                        removeEntryFromGradeBook(exisGrade);
                    }
                }
            }

        } else {
            //save modified post
            topicDao.updateTopicPost(post);
        }

        // post attachments
        jforumAttachmentService.processEditPostAttachments(post);

        // index post for search
        jforumSearchIndexingExecutorService.indexPost(post);

        // clear the cache
        clearCache(exisTopic);
    }

    /**
     * {@inheritDoc}
     */
    public void moveTopic(int topicId, int toForumId, String movedBySakaiUserId)
            throws JForumItemNotFoundException, JForumAccessException, JForumGradesModificationException {
        Topic topic = getTopic(topicId);

        if (topic == null) {
            throw new JForumItemNotFoundException(topicId);
        }

        Forum curforum = forumDao.selectById(topic.getForumId());

        if (curforum == null) {
            throw new JForumItemNotFoundException(topic.getForumId());
        }

        Category category = categoryDao.selectById(curforum.getCategoryId());

        if (category == null) {
            throw new JForumItemNotFoundException(curforum.getCategoryId());
        }

        boolean facilitator = jforumSecurityService.isJForumFacilitator(category.getContext(), movedBySakaiUserId);

        if (!facilitator) {
            throw new JForumAccessException(topic.getPostedBy().getSakaiUserId());
        }

        /*
         * gradable topics and gradable forum topics cannot be moved
         */
        if (curforum.getGradeType() == Grade.GradeType.TOPIC.getType() && topic.isGradeTopic()) {
            throw new JForumGradesModificationException("Item with title: " + topic.getTitle() + " is gradable.");
        } else if (curforum.getGradeType() == Grade.GradeType.FORUM.getType()) {
            throw new JForumGradesModificationException("Item with title: " + topic.getTitle() + " is gradable.");
        }

        topicDao.moveTopic(topicId, toForumId);

        // clear the cache
        clearCache(topic);
    }

    /**
     * {@inheritDoc}
     */
    public Attachment newAttachment(String fileName, String contentType, String comments, byte[] fileContent) {
        if ((fileName == null || fileName.trim().length() == 0)
                || (fileContent == null || fileContent.length == 0)) {
            return null;
        }

        // Get only the filename, without the path
        String separator = "/";
        int index = fileName.indexOf(separator);

        if (index == -1) {
            separator = "\\";
            index = fileName.indexOf(separator);
        }

        if (index > -1) {
            if (separator.equals("\\")) {
                separator = "\\\\";
            }

            String[] p = fileName.split(separator);
            fileName = p[p.length - 1];
        }

        Attachment attachment = new AttachmentImpl();

        AttachmentInfoImpl attachmentInfo = new AttachmentInfoImpl();
        attachmentInfo.setMimetype(contentType);
        attachmentInfo.setRealFilename(fileName);
        attachmentInfo.setComment(comments);
        attachmentInfo.setFileContent(fileContent);

        attachment.setInfo(attachmentInfo);

        return attachment;
    }

    /**
     * {@inheritDoc}
     */
    public Post newPost() {
        // TODO: add access check

        Post post = new PostImpl();
        post.setPostedBy(new UserImpl());

        return post;
    }

    /**
     * {@inheritDoc}
     */
    public Topic newTopic() {
        // TODO: add access check

        Topic topic = new TopicImpl();
        topic.setAccessDates(new AccessDatesImpl());
        topic.setGrade(new GradeImpl());

        return topic;
    }

    /**
     * {@inheritDoc}
     */
    public Grade newTopicGrade(Topic topic) {
        // TODO: add access check

        if (topic == null) {
            return null;
        }

        Grade grade = new GradeImpl();
        topic.setGrade(grade);

        return grade;
    }

    /**
     * {@inheritDoc}
     */
    public void previewPostForDisplay(Post post) {
        if (post == null) {
            return;
        }

        // clean html
        String cleanedPostText = HtmlHelper.clean(post.getText(), true);
        post.setText(cleanedPostText);

        PostUtil.preparePostForDisplay(post);

        /*
         * Escapes the characters in a String using JavaScript String rules.
         * Example adds escape character to quotes - First name: <input name=\"firstname\" size=\"20\" type=\"text\" />
         * Else the display may not be correct
        */
        post.setSubject(StringEscapeUtils.escapeJavaScript(post.getSubject()));
        post.setText(StringEscapeUtils.escapeJavaScript(post.getText()));
    }

    /**
     * {@inheritDoc}
     */
    public void removePost(int postId, String deletedBySakaiUserId)
            throws JForumItemNotFoundException, JForumAccessException, JForumGradesModificationException {
        if (postId <= 0) {
            throw new IllegalArgumentException("Post id is missing");
        }

        Post post = topicDao.selectPostById(postId);

        if (post == null) {
            throw new JForumItemNotFoundException(postId);
        }

        Topic topic = topicDao.selectById(post.getTopicId());

        if (topic == null) {
            throw new JForumItemNotFoundException(post.getTopicId());
        }

        Forum forum = forumDao.selectById(topic.getForumId());

        if (forum == null) {
            throw new JForumItemNotFoundException(topic.getForumId());
        }

        Category category = categoryDao.selectById(forum.getCategoryId());

        if (category == null) {
            throw new JForumItemNotFoundException(forum.getCategoryId());
        }

        boolean facilitator = jforumSecurityService.isJForumFacilitator(category.getContext(),
                deletedBySakaiUserId);

        if (!facilitator) {
            throw new JForumAccessException(deletedBySakaiUserId);
        }

        /*
         * If the post is first post check the grade type of the topic. If the topic's grade type
         * is yes check for existing evaluations. If the topic's grade has evaluations, the topic cannot be deleted
         * If the gradable forum or category has evaluations the first post cannot be deleted
         */
        if (topic.getFirstPostId() == post.getId()) {
            if (forum.getGradeType() == Grade.GradeType.TOPIC.getType() && topic.isGradeTopic()) {
                /*List<Evaluation> topicEvaluations = jforumGradeService.getTopicEvaluations(topic.getForumId(), topic.getId());
                    
                if (!topicEvaluations.isEmpty())
                {
                   throw new JForumGradesModificationException("Item with title: "+ topic.getTitle() +" has been graded");
                }*/
                int topicEvaluationsCount = jforumGradeService.getTopicEvaluationsCount(topic.getForumId(),
                        topic.getId());

                if (topicEvaluationsCount > 0) {
                    throw new JForumGradesModificationException(
                            "Item with title: " + topic.getTitle() + " has been graded");
                }
            } else if (forum.getGradeType() == Grade.GradeType.FORUM.getType()) {
                /*List<Evaluation> forumEvaluations = jforumGradeService.getForumEvaluations(forum.getId());
                    
                if (!forumEvaluations.isEmpty())
                {
                   throw new JForumGradesModificationException("Item with title: "+ forum.getName() +" has been graded");
                }*/
                int forumEvaluationsCount = jforumGradeService.getForumEvaluationsCount(forum.getId());

                if (forumEvaluationsCount > 0) {
                    throw new JForumGradesModificationException(
                            "Item with title: " + forum.getName() + " has been graded");
                }
            }
            /*else if (category.isGradable())
            {
               List<Evaluation> catEvaluations = jforumGradeService.getCategoryEvaluations(category.getId());
                   
               if (!catEvaluations.isEmpty())
               {
                  throw new JForumGradesModificationException("Item with title: "+ category.getTitle() +" has been graded");
               }
            }*/
        }

        //delete post
        topicDao.deleteTopicPost(postId);

        //delete post attachments
        if (post.hasAttachments()) {
            jforumAttachmentService.deletePostAttachments(post);
        }

        //remove entry from gradebook if topic is also removed
        if (topic.getFirstPostId() == post.getId() && topic.isGradeTopic()) {
            Grade grade = topic.getGrade();

            //jforumGBService.removeExternalAssessment(grade.getContext(), JForumGradeService.JFORUM_GRADEBOOK_EXTERNAL_ID_CONCAT + String.valueOf(grade.getId()));
            jforumGradeService.removeGradebookEntry(grade);
        }

        //clean search index related to post
        jforumSearchIndexingExecutorService.cleanIndex(post);

        // clear the cache
        clearCache(topic);
    }

    /**
     * {@inheritDoc}
     */
    public void removeTopic(int topicId, String deletedBySakaiUserId)
            throws JForumItemNotFoundException, JForumAccessException, JForumItemEvaluatedException {
        Topic topic = topicDao.selectById(topicId);

        if (topic == null) {
            throw new JForumItemNotFoundException(topicId);
        }

        Forum forum = forumDao.selectById(topic.getForumId());

        if (forum == null) {
            throw new JForumItemNotFoundException(topic.getForumId());
        }

        Category category = categoryDao.selectById(forum.getCategoryId());

        if (category == null) {
            throw new JForumItemNotFoundException(forum.getCategoryId());
        }

        boolean facilitator = jforumSecurityService.isJForumFacilitator(category.getContext(),
                deletedBySakaiUserId);

        if (!facilitator) {
            throw new JForumAccessException(deletedBySakaiUserId);
        }

        /*
         * For gradable topic check for existing evaluations. If the topic's grade has evaluations, the topic cannot be deleted
         * If the gradable forum has evaluations the topic cannot be deleted
         */
        if (forum.getGradeType() == Grade.GradeType.TOPIC.getType() && topic.isGradeTopic()) {
            int topicEvaluationsCount = jforumGradeService.getTopicEvaluationsCount(topic.getForumId(),
                    topic.getId());

            if (topicEvaluationsCount > 0) {
                throw new JForumItemEvaluatedException(topic.getTitle());
            }
        } else if (forum.getGradeType() == Grade.GradeType.FORUM.getType()) {
            int forumEvaluationsCount = jforumGradeService.getForumEvaluationsCount(forum.getId());

            if (forumEvaluationsCount > 0) {
                throw new JForumItemEvaluatedException(forum.getName());
            }
        }

        List<Post> topicPosts = getTopicPosts(topicId, 0, 0);

        // delete topic
        topicDao.deleteTopic(topicId);

        // delete topic post attachments
        for (Post post : topicPosts) {
            if (post.hasAttachments()) {
                jforumAttachmentService.deletePostAttachments(post);
            }
        }

        // remove entry from gradebook if topic is also removed
        if (topic.isGradeTopic()) {
            Grade grade = topic.getGrade();

            jforumGBService.removeExternalAssessment(grade.getContext(),
                    JForumGradeService.JFORUM_GRADEBOOK_EXTERNAL_ID_CONCAT + String.valueOf(grade.getId()));
        }

        // clear the cache
        clearCache(topic);

        // clean search index related to post
        for (Post post : topicPosts) {
            jforumSearchIndexingExecutorService.cleanIndex(post);
        }
    }

    /**
     * {@inheritDoc}
     */
    public void removeTopicSubscription(int topicId) {
        if (topicId <= 0) {
            new IllegalArgumentException("Not a valid topic id.");
        }

        topicDao.removeSubscription(topicId);
    }

    /**
     * {@inheritDoc}
     */
    public void removeUserTopicSubscription(int topicId, String sakaiUserId) {
        if ((topicId <= 0) || (sakaiUserId == null || sakaiUserId.trim().length() == 0)) {
            new IllegalArgumentException("Not a valid topic id or user id.");
        }

        User user = jforumUserService.getBySakaiUserId(sakaiUserId);

        if (user != null) {
            topicDao.removeUserSubscription(topicId, user.getId());
        }
    }

    /**
     * @param attachmentDao the attachmentDao to set
     */
    public void setAttachmentDao(AttachmentDao attachmentDao) {
        this.attachmentDao = attachmentDao;
    }

    /**
     * @param categoryDao the categoryDao to set
     */
    public void setCategoryDao(CategoryDao categoryDao) {
        this.categoryDao = categoryDao;
    }

    /**
     * @param forumDao the forumDao to set
     */
    public void setForumDao(ForumDao forumDao) {
        this.forumDao = forumDao;
    }

    /**
     * @param jforumAttachmentService the jforumAttachmentService to set
     */
    public void setJforumAttachmentService(JForumAttachmentService jforumAttachmentService) {
        this.jforumAttachmentService = jforumAttachmentService;
    }

    /**
     * @param jforumCategoryService the jforumCategoryService to set
     */
    public void setJforumCategoryService(JForumCategoryService jforumCategoryService) {
        this.jforumCategoryService = jforumCategoryService;
    }

    /**
     * @param jforumEmailExecutorService the jforumEmailExecutorService to set
     */
    public void setJforumEmailExecutorService(JForumEmailExecutorService jforumEmailExecutorService) {
        this.jforumEmailExecutorService = jforumEmailExecutorService;
    }

    /**
     * @param jforumForumService the jforumForumService to set
     */
    public void setJforumForumService(JForumForumService jforumForumService) {
        this.jforumForumService = jforumForumService;
    }

    /**
     * @param jforumGBService the jforumGBService to set
     */
    public void setJforumGBService(JForumGBService jforumGBService) {
        this.jforumGBService = jforumGBService;
    }

    /**
     * @param jforumGradeService 
     *          The jforumGradeService to set
     */
    public void setJforumGradeService(JForumGradeService jforumGradeService) {
        this.jforumGradeService = jforumGradeService;
    }

    /**
     * @param jforumSearchIndexingExecutorService the jforumSearchIndexingExecutorService to set
     */
    public void setJforumSearchIndexingExecutorService(
            JForumSearchIndexingExecutorService jforumSearchIndexingExecutorService) {
        this.jforumSearchIndexingExecutorService = jforumSearchIndexingExecutorService;
    }

    /**
     * @param jforumSecurityService the jforumSecurityService to set
     */
    public void setJforumSecurityService(JForumSecurityService jforumSecurityService) {
        this.jforumSecurityService = jforumSecurityService;
    }

    /**
     * @param jforumSpecialAccessService 
     *          The jforumSpecialAccessService to set
     */
    public void setJforumSpecialAccessService(JForumSpecialAccessService jforumSpecialAccessService) {
        this.jforumSpecialAccessService = jforumSpecialAccessService;
    }

    /**
     * @param jforumUserService the jforumUserService to set
     */
    public void setJforumUserService(JForumUserService jforumUserService) {
        this.jforumUserService = jforumUserService;
    }

    /**
     * @param siteService the siteService to set
     */
    public void setSiteService(SiteService siteService) {
        this.siteService = siteService;
    }

    /**
     * @param sqlService 
     *          The sqlService to set
     */
    public void setSqlService(SqlService sqlService) {
        this.sqlService = sqlService;
    }

    /**
     * @param threadLocalManager 
     *          The threadLocalManager to set
     */
    public void setThreadLocalManager(ThreadLocalManager threadLocalManager) {
        this.threadLocalManager = threadLocalManager;
    }

    /**
     * @param topicDao the topicDao to set
     */
    public void setTopicDao(TopicDao topicDao) {
        this.topicDao = topicDao;
    }

    /**
     * {@inheritDoc}
     */
    public void subscribeUserToTopic(int topicId, String sakaiUserId) {
        if ((topicId <= 0) || (sakaiUserId == null || sakaiUserId.trim().length() == 0)) {
            new IllegalArgumentException("Not a valid topic id or user id.");
        }

        User user = jforumUserService.getBySakaiUserId(sakaiUserId);

        if (user != null) {
            topicDao.subscribeUser(topicId, user.getId());
        }
    }

    /**
     * {@inheritDoc}
     */
    public void updateDates(final Topic topic) {
        if (topic == null) {
            throw new IllegalArgumentException("updateDates: forum is null");
        }

        Topic existingTopic = getTopic(topic.getId());

        if (existingTopic == null) {
            if (logger.isWarnEnabled()) {
                logger.warn("updateDates: There is no existing topic with id " + topic.getId());
            }
            return;
        }

        // update if the date changes
        boolean datesChanged = false;

        datesChanged = checkDatesChanged(topic, existingTopic);

        if (datesChanged) {
            topic.setForumId(existingTopic.getForumId());

            boolean forumDates = false, categoryDates = false;

            Forum existingForum = existingTopic.getForum();
            if ((existingForum != null) && (existingForum.getAccessDates() != null)) {
                if ((existingForum.getAccessDates().getOpenDate() != null)
                        || (existingForum.getAccessDates().getDueDate() != null)
                        || (existingForum.getAccessDates().getAllowUntilDate() != null)) {
                    forumDates = true;
                }
            }

            Category existingcategory = existingForum.getCategory();

            if ((existingcategory != null) && (existingcategory.getAccessDates() != null)) {
                if ((existingcategory.getAccessDates().getOpenDate() != null)
                        || (existingcategory.getAccessDates().getDueDate() != null)
                        || (existingcategory.getAccessDates().getAllowUntilDate() != null)) {
                    categoryDates = true;
                }
            }

            if ((forumDates) || (categoryDates)) {
                topic.getAccessDates().setOpenDate(null);
                topic.getAccessDates().setHideUntilOpen(null);
                topic.getAccessDates().setDueDate(null);
                topic.getAccessDates().setAllowUntilDate(null);
            }

            // topic can have dates if category and forum have no dates         
            if ((topic.getAccessDates() != null) && (existingTopic.getAccessDates() != null)) {
                //topic.getAccessDates().setLocked(existingTopic.getAccessDates().isLocked());
            }

            // if no dates remove any existing special access. Gradable topics with dates can have special access.
            if ((topic.getAccessDates() == null) || ((topic.getAccessDates().getOpenDate() == null)
                    && (topic.getAccessDates().getDueDate() == null)
                    && (topic.getAccessDates().getAllowUntilDate() == null))) {
                List<SpecialAccess> forumSpecialAccessList = this.jforumSpecialAccessService
                        .getByTopic(topic.getForumId(), topic.getId());

                for (SpecialAccess specialAccess : forumSpecialAccessList) {
                    this.jforumSpecialAccessService.delete(specialAccess.getId());
                }
            }

            this.sqlService.transact(new Runnable() {
                public void run() {
                    updateDatesTXN(topic);
                }
            }, "updateTopicDates: " + topic.getId());

            //Date exisDueDate = null, modDueDate = null, modOpenDate = null, modAllowUntilDate = null;
            //boolean dueDateChanged = false;
            Date modDueDate = null, modOpenDate = null, modAllowUntilDate = null;

            // check the due date changes
            if (topic.getAccessDates() != null) {
                modDueDate = topic.getAccessDates().getDueDate();
                modOpenDate = topic.getAccessDates().getOpenDate();
                modAllowUntilDate = topic.getAccessDates().getAllowUntilDate();

                if ((modOpenDate != null) || (modDueDate != null) || (modAllowUntilDate != null)) {
                    if (!topic.getAccessDates().isDatesValid()) {
                        Grade grade = jforumGradeService.getByForumTopicId(topic.getForumId(), topic.getId());

                        if (grade != null && grade.isAddToGradeBook()) {
                            boolean result = jforumGradeService.removeGradebookEntry(grade);

                            /*if (result)
                            {
                               //update the grade for addToGradeBook
                               String sql = "UPDATE jforum_grade SET add_to_gradebook = ? WHERE grade_id = ?";
                                   
                               Object[] fields = new Object[2];
                               int i = 0;
                                   
                               fields[i++] = 0;
                               fields[i++] = grade.getId();
                                
                               if (!sqlService.dbWrite(sql.toString(), fields)) 
                               {
                                  throw new RuntimeException("updateGrade - add_to_gradebook : db write failed");
                               }
                            }*/
                        }
                        return;
                    }
                }

            }

            /*if (existingTopic.getAccessDates() != null)
            {
               exisDueDate = existingTopic.getAccessDates().getDueDate();
            }
                
            if (modDueDate == null && exisDueDate != null)
            {
               dueDateChanged = true;
            }
            else if (modDueDate != null && exisDueDate == null)
            {
               dueDateChanged = true;
            }
            else if (modDueDate == null && exisDueDate == null)
            {
               dueDateChanged = false;
            }
            else
            {
               if (!exisDueDate.equals(modDueDate))
               {
                  dueDateChanged = true;
               }
            }*/

            //if (dueDateChanged)
            //{
            topic.setTitle(existingTopic.getTitle());
            topic.setGradeTopic(existingTopic.isGradeTopic());

            Grade topicGrade = this.jforumGradeService.getByForumTopicId(topic.getForumId(), topic.getId());

            if (topicGrade != null && topicGrade.isAddToGradeBook()) {
                this.jforumGradeService.updateGradebook(topicGrade, topic);
            }
            //}

        }

        // clear the cache
        clearCache(topic);
    }

    /**
     * Key for caching a topic.
     * 
     * @param topicId   Topic id
     * 
     * @return   Cache key
     */
    protected String cacheKey(String topicId) {
        return "jforum:topic:" + topicId;
    }

    /**
     * Check for date changes
     * 
     * @param topic         Topic
     * 
     * @param existingTopic   Existing topic to compare
     * 
     * @return   true - if dates are changed
     *          false - if dates are not changed
     */
    protected boolean checkDatesChanged(Topic topic, Topic existingTopic) {
        boolean datesChanged = false;

        /*Date exisOpenDate = null, exisDueDate = null, modOpenDate = null, modDueDate = null;
            
        if (topic.getAccessDates() != null)
        {
           modOpenDate = topic.getAccessDates().getOpenDate();
           modDueDate = topic.getAccessDates().getDueDate();
        }
            
        if (existingTopic.getAccessDates() != null)
        {
           exisOpenDate = existingTopic.getAccessDates().getOpenDate();
           exisDueDate = existingTopic.getAccessDates().getDueDate();
        }
            
        if (exisOpenDate == null)
        {
           if (modOpenDate != null)
           {
        datesChanged = true;
           }
        }
        else
        {
           if (modOpenDate == null)
           {
        datesChanged = true;
           }
           else if (!modOpenDate.equals(exisOpenDate))
           {
        datesChanged = true;
           }
        }
            
        if (!datesChanged)
        {
           if (exisDueDate == null)
           {
        if (modDueDate != null)
        {
           datesChanged = true;
        }
           }
           else
           {
        if (modDueDate == null)
        {
           datesChanged = true;
        }
        else if (!modDueDate.equals(exisDueDate))
        {
           datesChanged = true;
        }
           }
        }*/

        Date exisOpenDate = null, exisDueDate = null, exisAllowUntilDate = null, modOpenDate = null,
                modDueDate = null, modAllowUntilDate = null;
        Boolean exisHideUntilOpen = null, modHideUntilOpen = null;

        if (topic.getAccessDates() != null) {
            modOpenDate = topic.getAccessDates().getOpenDate();
            modHideUntilOpen = topic.getAccessDates().isHideUntilOpen();
            modDueDate = topic.getAccessDates().getDueDate();
            modAllowUntilDate = topic.getAccessDates().getAllowUntilDate();
        }

        if (existingTopic.getAccessDates() != null) {
            exisOpenDate = existingTopic.getAccessDates().getOpenDate();
            exisHideUntilOpen = existingTopic.getAccessDates().isHideUntilOpen();
            exisDueDate = existingTopic.getAccessDates().getDueDate();
            exisAllowUntilDate = existingTopic.getAccessDates().getAllowUntilDate();
        }

        // open date
        if (exisOpenDate == null) {
            if (modOpenDate != null) {
                datesChanged = true;
            }
        } else {
            if (modOpenDate == null) {
                datesChanged = true;
            } else if (!modOpenDate.equals(exisOpenDate)) {
                datesChanged = true;
            } else if (modOpenDate.equals(exisOpenDate)) {
                if (!exisHideUntilOpen.equals(modHideUntilOpen)) {
                    datesChanged = true;
                }
            }
        }

        // due date
        if (!datesChanged) {
            if (exisDueDate == null) {
                if (modDueDate != null) {
                    datesChanged = true;
                }
            } else {
                if (modDueDate == null) {
                    datesChanged = true;
                } else if (!modDueDate.equals(exisDueDate)) {
                    datesChanged = true;
                }
            }
        }

        // allow until date
        if (!datesChanged) {
            if (exisAllowUntilDate == null) {
                if (modAllowUntilDate != null) {
                    datesChanged = true;
                }
            } else {
                if (modAllowUntilDate == null) {
                    datesChanged = true;
                } else if (!modAllowUntilDate.equals(exisAllowUntilDate)) {
                    datesChanged = true;
                }
            }
        }

        return datesChanged;
    }

    /**
     * Checks and marks user read status of the forum
     * 
     * @param forum      Forum
     * 
     * @param sakaiUserId   Sakai user id
     */
    protected void checkForumUnreadTopics(Forum forum, String sakaiUserId) {
        Date currentTime = new Date(System.currentTimeMillis());
        User user = jforumUserService.getBySakaiUserId(sakaiUserId);

        if (user == null) {
            forum.setUnread(true);
            return;
        }

        Category category = forum.getCategory();

        // check marked unread topics
        int markedUnreadTopicCount = getMarkedTopicsCountByForum(forum.getId(), user.getId());

        if (markedUnreadTopicCount > 0) {
            forum.setUnread(true);
            return;
        }

        // marked all read time
        Date markAllReadTime = jforumUserService.getMarkAllReadTime(category.getContext(), user.getId());
        boolean facilitator = jforumSecurityService.isJForumFacilitator(category.getContext(),
                user.getSakaiUserId());

        List<SpecialAccess> forumTopicsSpecialAccess = jforumSpecialAccessService.getTopicsByForumId(forum.getId());

        /*get all topics with latest post time and check with topic read time*/
        List<Topic> topicsLastPosttimes = null;

        if (markAllReadTime == null) {
            // check with all topics
            if (facilitator) {
                topicsLastPosttimes = getForumTopicLatestPosttimes(forum.getId(), user.getId(), null, false);
            } else {
                topicsLastPosttimes = getForumTopicLatestPosttimes(forum.getId(), user.getId(), null, true);
            }
        } else {
            /*check with topics whose latest post time is after mark all read time and not posted by the current user*/
            if (facilitator) {
                topicsLastPosttimes = getForumTopicLatestPosttimes(forum.getId(), user.getId(), markAllReadTime,
                        false);
            } else {
                topicsLastPosttimes = getForumTopicLatestPosttimes(forum.getId(), user.getId(), markAllReadTime,
                        true);
            }
        }

        List<Topic> lastPosttimeTopicsList = new ArrayList<Topic>();

        for (Topic t : topicsLastPosttimes) {
            if (!facilitator) {
                /* no special access topics and special access topics*/
                List<SpecialAccess> topicsSpecialAccessList = jforumSpecialAccessService
                        .getTopicsByForumId(forum.getId());

                HashMap<Integer, SpecialAccess> userSpecialAccessAccessMap = new HashMap<Integer, SpecialAccess>();
                HashMap<Integer, SpecialAccess> userSpecialAccessNoAccessMap = new HashMap<Integer, SpecialAccess>();

                if (topicsSpecialAccessList.size() > 0) {
                    for (SpecialAccess sa : topicsSpecialAccessList) {
                        if (sa.getUserIds().contains(new Integer(user.getId()))) {
                            if (sa.getAccessDates().getOpenDate() == null) {
                                userSpecialAccessAccessMap.put(new Integer(sa.getTopicId()), sa);
                            } else if (currentTime.after(sa.getAccessDates().getOpenDate())) {
                                userSpecialAccessAccessMap.put(new Integer(sa.getTopicId()), sa);
                            } else {
                                userSpecialAccessNoAccessMap.put(new Integer(sa.getTopicId()), sa);
                            }
                        }
                    }
                }

                if (t.getAccessDates() == null) {
                    lastPosttimeTopicsList.add(t);
                } else {
                    if ((t.getAccessDates().getOpenDate() != null) || (t.getAccessDates().getDueDate() != null)) {
                        if (userSpecialAccessNoAccessMap.containsKey(t.getId())) {

                        } else if (userSpecialAccessAccessMap.containsKey(t.getId())) {
                            lastPosttimeTopicsList.add(t);
                        } else if ((t.getAccessDates().getOpenDate() != null)
                                && (t.getAccessDates().getOpenDate().after(currentTime))) {

                        } else {
                            lastPosttimeTopicsList.add(t);
                        }
                    } else {
                        lastPosttimeTopicsList.add(t);
                    }
                }

            } else {
                lastPosttimeTopicsList.add(t);
            }

        }

        // compare with visited topics time
        List<Topic> visitedTopics = null;

        if ((!facilitator) && (forumTopicsSpecialAccess != null) && (forumTopicsSpecialAccess.size() > 0)) {
            visitedTopics = getUserForumTopicVisittimes(forum.getId(), user.getId());
        } else {
            visitedTopics = getUserForumTopicVisittimes(forum.getId(), user.getId());
        }

        if (lastPosttimeTopicsList.size() > visitedTopics.size()) {
            forum.setUnread(true);
            return;
        }

        Map<Integer, Topic> visitedTopicsMap = new HashMap<Integer, Topic>();
        for (Topic t : visitedTopics) {
            visitedTopicsMap.put(Integer.valueOf(t.getId()), t);
        }

        for (Topic lastPosttimeTopic : lastPosttimeTopicsList) {
            Topic lastVistiedTopic = visitedTopicsMap.get(Integer.valueOf(lastPosttimeTopic.getId()));

            if (lastVistiedTopic != null) {
                // if topic is marked as unread mark the forum as unread
                if (!lastVistiedTopic.getRead()) {
                    forum.setUnread(true);
                    return;
                }

                if (lastVistiedTopic.getTime().before(lastPosttimeTopic.getTime())) {
                    forum.setUnread(true);
                    return;
                }
            } else {
                forum.setUnread(true);
                return;
            }
        }
    }

    /**
     * Check if item is accessible to the user (may be blocked item from coursemap)
     * 
     * @param context   The context
     * 
     * @param id      The item id
     * 
     * @param userId   The user id
     * 
     * @return   true - if the item is accessible to the user
     *          false - if the item is not accessible to the user
     */
    protected Boolean checkItemAccess(String context, String id, String userId) {
        if (this.accessAdvisor == null)
            return Boolean.TRUE;

        return !accessAdvisor.denyAccess("sakai.jforum", context, id, userId);

    }

    /**
     * check topic dates for the user. If the topics are accessible to the user they are removed from the list
     * 
     * @param topics   The topics
     * 
     * @param user   Jforum user
     */
    protected void checkTopicDates(List<Topic> topics, User user) {
        Date currentTime = Calendar.getInstance().getTime();

        for (Iterator<Topic> topicIter = topics.iterator(); topicIter.hasNext();) {
            Topic topic = topicIter.next();

            if (topic.getStatus() == Topic.TopicStatus.LOCKED.getStatus()) {
                ((TopicImpl) topic).setMayPost(Boolean.FALSE);
            }

            // topic special access
            List<SpecialAccess> topicSpecialAccessList = topic.getSpecialAccess();
            boolean topicSpecialAccessUser = false;
            boolean topicSpecialAccessUserAccess = false;
            SpecialAccess userSa = null;

            if ((user != null)
                    && ((topic.getAccessDates() != null) && ((topic.getAccessDates().getOpenDate() != null)
                            || (topic.getAccessDates().getDueDate() != null)
                            || (topic.getAccessDates().getAllowUntilDate() != null)))) {
                for (SpecialAccess sa : topicSpecialAccessList) {
                    if (sa.getUserIds().contains(new Integer(user.getId()))) {
                        userSa = sa;

                        topicSpecialAccessUser = true;

                        if (!sa.isOverrideStartDate()) {
                            sa.getAccessDates().setOpenDate(topic.getAccessDates().getOpenDate());
                        }

                        if (!sa.isOverrideHideUntilOpen()) {
                            sa.getAccessDates().setHideUntilOpen(topic.getAccessDates().isHideUntilOpen());
                        }

                        if (!sa.isOverrideEndDate()) {
                            sa.getAccessDates().setDueDate(topic.getAccessDates().getDueDate());
                        }

                        /*if (!sa.isOverrideLockEndDate())
                        {
                           sa.getAccessDates().setLocked(topic.getAccessDates().isLocked());
                        }*/

                        if (!sa.isOverrideAllowUntilDate()) {

                            sa.getAccessDates().setAllowUntilDate(topic.getAccessDates().getAllowUntilDate());
                        }

                        if (sa.getAccessDates().getOpenDate() == null) {
                            topicSpecialAccessUserAccess = true;
                        } else {
                            if (sa.getAccessDates().getOpenDate().after(currentTime)) {
                                if (!sa.getAccessDates().isHideUntilOpen()) {
                                    topicSpecialAccessUserAccess = true;
                                }
                            } else {
                                topicSpecialAccessUserAccess = true;
                            }
                        }

                        break;
                    }
                }
            }

            topic.getSpecialAccess().clear();
            if (topicSpecialAccessUser) {
                topic.getSpecialAccess().add(userSa);

                if (topicSpecialAccessUserAccess) {
                    //if (topic.mayPost() && userSa.getAccessDates().getDueDate() != null && userSa.getAccessDates().isLocked())
                    if (topic.mayPost()) {
                        if (userSa.getAccessDates().getAllowUntilDate() != null) {
                            if (userSa.getAccessDates().getAllowUntilDate().after(currentTime)) {
                                ((TopicImpl) topic).setMayPost(Boolean.FALSE);
                            }
                        } else if (userSa.getAccessDates().getDueDate() != null) {
                            if (userSa.getAccessDates().getDueDate().after(currentTime)) {
                                ((TopicImpl) topic).setMayPost(Boolean.FALSE);
                            }
                        }
                    }
                    continue;
                } else {
                    topicIter.remove();
                    continue;
                }
            }
            // topic dates
            else if ((topic.getAccessDates() != null) && (topic.getAccessDates().getOpenDate() != null)) {
                if (topic.getAccessDates().getOpenDate().after(currentTime)
                        && topic.getAccessDates().isHideUntilOpen()) {
                    topicIter.remove();
                    continue;
                }
            }

            //if (topic.mayPost() && (!topicSpecialAccessUserAccess) && ((topic.getAccessDates().getDueDate()) != null && (topic.getAccessDates().isLocked())))
            if (topic.mayPost() && topic.getAccessDates() != null && (!topicSpecialAccessUserAccess)) {
                if (topic.getAccessDates().getAllowUntilDate() != null) {
                    if (topic.getAccessDates().getAllowUntilDate().after(currentTime)) {
                        ((TopicImpl) topic).setMayPost(Boolean.FALSE);
                    }
                } else if (topic.getAccessDates().getDueDate() != null) {
                    if (topic.getAccessDates().getDueDate().after(currentTime)) {
                        ((TopicImpl) topic).setMayPost(Boolean.FALSE);
                    }
                }
            }
        }
    }

    /**
     * check topic dates for the use
     * 
     * @param topic   The topic
     * 
     * @param user   Jforum user
     * 
     * @throws JForumAccessException   if user is not allowed to access topic
     */
    protected void checkTopicDates(Topic topic, User user) throws JForumAccessException {
        Date currentTime = Calendar.getInstance().getTime();

        if (topic.getStatus() == Topic.TopicStatus.LOCKED.getStatus()) {
            ((TopicImpl) topic).setMayPost(Boolean.FALSE);
        }

        // topic special access
        List<SpecialAccess> topicSpecialAccessList = topic.getSpecialAccess();
        boolean topicSpecialAccessUser = false;
        boolean topicSpecialAccessUserAccess = false;
        SpecialAccess userSa = null;

        if ((user != null) && ((topic.getAccessDates() != null)
                && ((topic.getAccessDates().getOpenDate() != null) || (topic.getAccessDates().getDueDate() != null)
                        || (topic.getAccessDates().getAllowUntilDate() != null)))) {
            for (SpecialAccess sa : topicSpecialAccessList) {
                if (sa.getUserIds().contains(new Integer(user.getId()))) {
                    userSa = sa;

                    topicSpecialAccessUser = true;

                    if (!sa.isOverrideStartDate()) {
                        sa.getAccessDates().setOpenDate(topic.getAccessDates().getOpenDate());
                    }

                    if (!sa.isOverrideHideUntilOpen()) {
                        sa.getAccessDates().setHideUntilOpen(topic.getAccessDates().isHideUntilOpen());
                    }

                    if (!sa.isOverrideEndDate()) {
                        sa.getAccessDates().setDueDate(topic.getAccessDates().getDueDate());
                    }

                    /*if (!sa.isOverrideLockEndDate())
                    {
                       sa.getAccessDates().setLocked(topic.getAccessDates().isLocked());
                    }*/

                    if (!sa.isOverrideAllowUntilDate()) {

                        sa.getAccessDates().setAllowUntilDate(topic.getAccessDates().getAllowUntilDate());
                    }

                    if (sa.getAccessDates().getOpenDate() == null) {
                        topicSpecialAccessUserAccess = true;
                    } else {
                        if (sa.getAccessDates().getOpenDate().after(currentTime)) {
                            if (!sa.getAccessDates().isHideUntilOpen()) {
                                topicSpecialAccessUserAccess = true;
                            }
                        } else {
                            topicSpecialAccessUserAccess = true;
                        }
                    }
                    break;
                }
            }
        }

        topic.getSpecialAccess().clear();
        if (topicSpecialAccessUser) {
            topic.getSpecialAccess().add(userSa);

            if (topicSpecialAccessUserAccess) {
                //if (topic.mayPost() && userSa.getAccessDates().getDueDate() != null && userSa.getAccessDates().isLocked())
                if (topic.mayPost()) {
                    if (userSa.getAccessDates().getAllowUntilDate() != null) {
                        if (userSa.getAccessDates().getAllowUntilDate().before(currentTime)) {
                            ((TopicImpl) topic).setMayPost(Boolean.FALSE);
                        }
                    } else if (userSa.getAccessDates().getDueDate() != null) {
                        if (userSa.getAccessDates().getDueDate().before(currentTime)) {
                            ((TopicImpl) topic).setMayPost(Boolean.FALSE);
                        }
                    }
                }
                return;
            } else {
                throw new JForumAccessException(user.getSakaiUserId());
            }
        }
        // topic dates
        else if ((topic.getAccessDates() != null) && (topic.getAccessDates().getOpenDate() != null)) {
            if (topic.getAccessDates().getOpenDate().after(currentTime)) {
                throw new JForumAccessException(user.getSakaiUserId());
            }
        }

        //if (topic.mayPost() && (!topicSpecialAccessUserAccess) && ((topic.getAccessDates().getDueDate()) != null && (topic.getAccessDates().isLocked())))
        if (topic.mayPost() && topic.getAccessDates() != null && (!topicSpecialAccessUserAccess)) {
            if (topic.getAccessDates().getAllowUntilDate() != null) {
                if (topic.getAccessDates().getAllowUntilDate().before(currentTime)) {
                    ((TopicImpl) topic).setMayPost(Boolean.FALSE);
                }
            } else if (topic.getAccessDates().getDueDate() != null) {
                if (topic.getAccessDates().getDueDate().before(currentTime)) {
                    ((TopicImpl) topic).setMayPost(Boolean.FALSE);
                }
            }
        }
    }

    /**
     * Check topic and mark the read status
     * 
     * @param forum      Forum
     * 
     * @param user      Jforum user
     * 
     * @param siteId   Course/site id
     */
    protected void checkUnreadTopics(Forum forum, User user, String siteId) {
        // mark and posts pagination
        String postsPerPageStr = ServerConfigurationService.getString(POSTS_PER_PAGE);
        //String hotTopicBeginStr = ServerConfigurationService.getString(HOT_TOPIC_BEGIN);

        //int hotBegin = -1;
        int postsPerPage = -1;

        /*if ((hotTopicBeginStr != null) && (hotTopicBeginStr.trim().length() > 0))
        {
           try
           {
        hotBegin = Integer.parseInt(hotTopicBeginStr);
           }
           catch (NumberFormatException e)
           {
        //ignore error
           }
        }*/

        if ((postsPerPageStr != null) && (postsPerPageStr.trim().length() > 0)) {
            try {
                postsPerPage = Integer.parseInt(postsPerPageStr);
            } catch (NumberFormatException e) {
                //ignore error
            }
        }

        if (user != null) {
            // marked all read time
            Date markAllReadTime = jforumUserService.getMarkAllReadTime(siteId, user.getId());
            boolean facilitator = jforumSecurityService.isJForumFacilitator(siteId, user.getSakaiUserId());

            /*get all topics with latest post time and check with topic read time*/
            List<Topic> topicsLastPosttimes = null;

            if (markAllReadTime == null) {
                // check all topics
                if (facilitator) {
                    topicsLastPosttimes = getForumTopicLatestPosttimes(forum.getId(), user.getId(), null, false);
                } else {
                    topicsLastPosttimes = getForumTopicLatestPosttimes(forum.getId(), user.getId(), null, true);
                }
            } else {
                /*check topics whose latest post time is after mark all read time and not posted by the current user*/
                if (facilitator) {
                    topicsLastPosttimes = getForumTopicLatestPosttimes(forum.getId(), user.getId(), markAllReadTime,
                            false);
                } else {
                    topicsLastPosttimes = getForumTopicLatestPosttimes(forum.getId(), user.getId(), markAllReadTime,
                            true);
                }
            }

            Map<Integer, Topic> lastPosttimeTopicsMap = new HashMap<Integer, Topic>();
            for (Topic topicPostTimes : topicsLastPosttimes) {
                lastPosttimeTopicsMap.put(new Integer(topicPostTimes.getId()), topicPostTimes);
            }

            // compare with visited topics time
            List<Topic> visitedTopics = getUserForumTopicVisittimes(forum.getId(), user.getId());

            Map<Integer, Topic> visitedTopicsMap = new HashMap<Integer, Topic>();
            for (Topic visitedTopic : visitedTopics) {
                visitedTopicsMap.put(new Integer(visitedTopic.getId()), visitedTopic);
            }

            Topic visitedTopic = null;
            Topic lastPostTimeTopic = null;
            for (Topic topic : forum.getTopics()) {
                /*if (user == null)
                {
                   topic.setRead(false);
                   continue;
                }*/

                visitedTopic = visitedTopicsMap.get(new Integer(topic.getId()));
                lastPostTimeTopic = lastPosttimeTopicsMap.get(new Integer(topic.getId()));

                if (visitedTopic == null) {
                    Date lastPostTime = null;

                    if (lastPostTimeTopic != null) {
                        lastPostTime = lastPostTimeTopic.getTime();
                    }

                    if (lastPostTime != null) {
                        topic.setRead(false);
                    } else {
                        topic.setRead(true);
                    }
                } else {
                    // if topic is marked as unread mark the topic as unread
                    if (!visitedTopic.getRead()) {
                        topic.setRead(Boolean.FALSE);
                        continue;
                    }

                    Date lastPostTime = null;

                    if (lastPostTimeTopic != null) {
                        lastPostTime = lastPostTimeTopic.getTime();
                    }

                    if (lastPostTime != null) {
                        if (lastPostTime.before(visitedTopic.getTime())) {
                            topic.setRead(true);
                        } else {
                            topic.setRead(false);
                        }
                    } else {
                        topic.setRead(true);
                    }

                }

                // Check if this is a hot topic
                /*if (hotBegin != -1)
                {
                   topic.setHot(topic.getTotalReplies() >= hotBegin);
                }*/

                // posts paginate
                if (postsPerPage != -1) {
                    if (topic.getTotalReplies() + 1 > postsPerPage) {
                        topic.setPaginate(true);
                    } else {
                        topic.setPaginate(false);
                    }
                }
            }
        } else {
            for (Topic topic : forum.getTopics()) {
                topic.setRead(false);

                // Check if this is a hot topic
                /*if (hotBegin != -1)
                {
                   topic.setHot(topic.getTotalReplies() >= hotBegin);
                }*/

                // posts paginate
                if (postsPerPage != -1) {
                    if (topic.getTotalReplies() + 1 > postsPerPage) {
                        topic.setPaginate(true);
                    } else {
                        topic.setPaginate(false);
                    }
                }
            }
        }
    }

    /**
     * Checks user unread topics
     * 
     * @param context   Context or site id
     * 
     * @param topics   Topics
     * 
     * @param user      Jforum user
     */
    protected void checkUnreadTopics(String context, List<Topic> topics, User user) {
        if ((topics == null) || (topics.isEmpty())) {
            return;
        }

        if (user != null) {
            // marked all read time
            Date markAllReadTime = jforumUserService.getMarkAllReadTime(context, user.getId());

            /*// topic mark or visit time
            Date topicMarkTime = null;
            Date topicLatestPostTime = null;
            Topic lastPostimeTopic = null;*/

            /*get topics with latest post time and check with topic read time(or visited time) and mark all read time*/
            for (Topic topic : topics) {
                markUserTopicReadStatus(user, markAllReadTime, topic);
            }
        } else {
            /*
            String value = ServerConfigurationService.getString(HOT_TOPIC_BEGIN);
                
            int hotBegin = -1;
                
            if ((value != null) && (value.trim().length() > 0))
            {
               try
               {
                  hotBegin = Integer.parseInt(value);
               }
               catch (NumberFormatException e)
               {
                  //ignore error
               }
            }
                
            for (Topic topic : topics) 
            {
               if (hotBegin != -1)
               {
                  topic.setHot(topic.getTotalReplies() >= hotBegin);
               }
               topic.setRead(false);
            }*/
            String postsPerPageStr = ServerConfigurationService.getString(POSTS_PER_PAGE);
            //String hotTopicBeginStr = ServerConfigurationService.getString(HOT_TOPIC_BEGIN);

            //int hotBegin = -1;
            int postsPerPage = -1;

            /*if ((hotTopicBeginStr != null) && (hotTopicBeginStr.trim().length() > 0))
            {
               try
               {
                  hotBegin = Integer.parseInt(hotTopicBeginStr);
               }
               catch (NumberFormatException e)
               {
                  //ignore error
               }
            }*/

            if ((postsPerPageStr != null) && (postsPerPageStr.trim().length() > 0)) {
                try {
                    postsPerPage = Integer.parseInt(postsPerPageStr);
                } catch (NumberFormatException e) {
                    //ignore error
                }
            }

            // Check if this is a hot topic
            for (Topic topic : topics) {
                /*if (hotBegin != -1)
                {
                   topic.setHot(topic.getTotalReplies() >= hotBegin);
                }*/
                topic.setRead(false);

                // posts paginate
                if (postsPerPage != -1) {
                    if (topic.getTotalReplies() + 1 > postsPerPage) {
                        topic.setPaginate(true);
                    } else {
                        topic.setPaginate(false);
                    }
                }
            }
        }
    }

    /**
     * Verifies user topic access
     * 
     * @param topic      Topic
     * 
     * @param sakaiUserId   Sakai user id
     * 
     * @throws JForumAccessException   If user is not allowed to access this item or perform this action
     */
    protected void checkUserTopicAccess(Topic topic, String sakaiUserId) throws JForumAccessException {
        if (topic == null) {
            return;
        }

        Category category = null;
        Forum forum = null;

        forum = topic.getForum();

        if (forum == null) {
            return;
        }

        category = forum.getCategory();
        if (category == null) {
            return;
        }

        boolean mayPost = true;
        if (forum.getType() == Forum.ForumType.READ_ONLY.getType()) {
            mayPost = false;
            ((TopicImpl) topic).setMayPost(Boolean.FALSE);
        }

        if (mayPost && topic.getStatus() == Topic.TopicStatus.LOCKED.getStatus()) {
            mayPost = false;
            ((TopicImpl) topic).setMayPost(Boolean.FALSE);
        }

        boolean categoryForumDates = false;

        Date currentTime = new Date();

        User user = jforumUserService.getBySakaiUserId(sakaiUserId);

        // ignore forums with category invalid dates
        /*if ((category.getAccessDates() != null) && (category.getAccessDates().getOpenDate() != null) && (category.getAccessDates().getDueDate() != null))
        {
           if (category.getAccessDates().getOpenDate().after(category.getAccessDates().getDueDate()))
           {   
        throw new JForumAccessException(sakaiUserId);
           }
        }*/
        // ignore forums with category invalid dates
        if (category.getAccessDates() != null) {
            if (!category.getAccessDates().isDatesValid()) {
                throw new JForumAccessException(sakaiUserId);
            }
        }

        // ignore forums with invalid dates
        /*if ((forum.getAccessDates() != null) && (forum.getAccessDates().getOpenDate() != null) && (forum.getAccessDates().getDueDate() != null))
        {
           if (forum.getAccessDates().getOpenDate().after(forum.getAccessDates().getDueDate()))
           {   
        throw new JForumAccessException(sakaiUserId);
           }
        }*/
        if (forum.getAccessDates() != null) {
            if (!forum.getAccessDates().isDatesValid()) {
                throw new JForumAccessException(sakaiUserId);
            }
        }

        // forum access type
        if (forum.getAccessType() == Forum.ForumAccess.DENY.getAccessType()) {
            throw new JForumAccessException(sakaiUserId);
        }

        // forum groups
        if (forum.getAccessType() == Forum.ForumAccess.GROUPS.getAccessType()) {
            boolean userInGroup = false;

            try {
                Site site = siteService.getSite(category.getContext());
                Collection<org.sakaiproject.site.api.Group> userGroups = site.getGroupsWithMember(sakaiUserId);

                for (org.sakaiproject.site.api.Group grp : userGroups) {
                    if ((forum.getGroups() != null) && (forum.getGroups().contains(grp.getId()))) {
                        userInGroup = true;
                        break;
                    }
                }
            } catch (IdUnusedException e) {
                if (logger.isWarnEnabled()) {
                    logger.warn("filterUserTopicPosts: missing site: " + category.getContext());
                }
            }

            // user not in any group
            if (!userInGroup) {
                throw new JForumAccessException(sakaiUserId);
            }
        }

        // forum special access
        List<SpecialAccess> forumSpecialAccessList = forum.getSpecialAccess();
        boolean forumSpecialAccessUser = false;
        boolean forumSpecialAccessUserAccess = false;
        SpecialAccess userSa = null;

        if (user != null && ((forum.getAccessDates() != null)
                && ((forum.getAccessDates().getOpenDate() != null) || (forum.getAccessDates().getDueDate() != null)
                        || (forum.getAccessDates().getAllowUntilDate() != null)))) {
            for (SpecialAccess sa : forumSpecialAccessList) {
                if (sa.getUserIds().contains(new Integer(user.getId()))) {
                    userSa = sa;

                    forumSpecialAccessUser = true;

                    if (!sa.isOverrideStartDate()) {
                        sa.getAccessDates().setOpenDate(forum.getAccessDates().getOpenDate());
                    }

                    if (!sa.isOverrideHideUntilOpen()) {
                        sa.getAccessDates().setHideUntilOpen(forum.getAccessDates().isHideUntilOpen());
                    }

                    if (!sa.isOverrideEndDate()) {
                        sa.getAccessDates().setDueDate(forum.getAccessDates().getDueDate());
                    }

                    /*if (!sa.isOverrideLockEndDate())
                    {
                       sa.getAccessDates().setLocked(forum.getAccessDates().isLocked());
                    }*/

                    if (!sa.isOverrideAllowUntilDate()) {

                        sa.getAccessDates().setAllowUntilDate(forum.getAccessDates().getAllowUntilDate());
                    }

                    if (sa.getAccessDates().getOpenDate() == null) {
                        forumSpecialAccessUserAccess = true;
                    } else {
                        if (sa.getAccessDates().getOpenDate().before(currentTime)) {
                            forumSpecialAccessUserAccess = true;
                        }
                    }
                    break;
                }
            }
        }

        forum.getSpecialAccess().clear();
        if (forumSpecialAccessUser) {
            forum.getSpecialAccess().add(userSa);

            if (forumSpecialAccessUserAccess) {
                //if (mayPost && userSa.getAccessDates().getDueDate() != null && userSa.getAccessDates().isLocked())
                if (mayPost) {
                    if (userSa.getAccessDates().getAllowUntilDate() != null) {
                        if (userSa.getAccessDates().getAllowUntilDate().before(currentTime)) {
                            mayPost = false;
                            ((TopicImpl) topic).setMayPost(Boolean.FALSE);
                        }
                    } else if (userSa.getAccessDates().getDueDate() != null) {
                        if (userSa.getAccessDates().getDueDate().before(currentTime)) {
                            mayPost = false;
                            ((TopicImpl) topic).setMayPost(Boolean.FALSE);
                        }
                    }
                }
            } else {
                throw new JForumAccessException(sakaiUserId);
            }
        }
        // forum dates
        else if ((forum.getAccessDates() != null) && (forum.getAccessDates().getOpenDate() != null)) {
            if (forum.getAccessDates().getOpenDate().after(currentTime)) {
                throw new JForumAccessException(sakaiUserId);
            }
        }

        if ((forum.getAccessDates() != null)
                && ((forum.getAccessDates().getOpenDate() != null) || (forum.getAccessDates().getDueDate() != null)
                        || (forum.getAccessDates().getAllowUntilDate() != null))) {
            categoryForumDates = true;

            //if (mayPost && (!forumSpecialAccessUserAccess) && ((forum.getAccessDates().getDueDate()) != null && (forum.getAccessDates().isLocked())))
            if (mayPost && !forumSpecialAccessUserAccess) {
                if (forum.getAccessDates().getAllowUntilDate() != null) {
                    if (forum.getAccessDates().getAllowUntilDate().before(currentTime)) {
                        mayPost = false;
                        ((TopicImpl) topic).setMayPost(Boolean.FALSE);
                    }
                } else if (forum.getAccessDates().getDueDate() != null) {
                    if (forum.getAccessDates().getDueDate().before(currentTime)) {
                        mayPost = false;
                        ((TopicImpl) topic).setMayPost(Boolean.FALSE);
                    }
                }
            }
        } else if ((category.getAccessDates() != null) && ((category.getAccessDates().getOpenDate() != null)
                || (category.getAccessDates().getDueDate() != null)
                || (category.getAccessDates().getAllowUntilDate() != null))) {
            categoryForumDates = true;

            // category dates
            if ((!forumSpecialAccessUser) && (category.getAccessDates().getOpenDate() != null)) {
                if (category.getAccessDates().getOpenDate().after(currentTime)) {
                    throw new JForumAccessException(sakaiUserId);
                }
            }

            //if (mayPost && (!forumSpecialAccessUserAccess) && ((category.getAccessDates().getDueDate()) != null && (category.getAccessDates().isLocked())))
            if (mayPost && !forumSpecialAccessUserAccess) {
                if (category.getAccessDates().getAllowUntilDate() != null) {
                    if (category.getAccessDates().getAllowUntilDate().before(currentTime)) {
                        mayPost = false;
                        ((TopicImpl) topic).setMayPost(Boolean.FALSE);
                    }
                } else if ((category.getAccessDates().getDueDate() != null)
                        && category.getAccessDates().getDueDate().before(currentTime)) {
                    mayPost = false;
                    ((TopicImpl) topic).setMayPost(Boolean.FALSE);
                }
            }
        }

        // topics dates if no category or forum dates
        if (!categoryForumDates) {
            checkTopicDates(topic, user);
        }

        // for gradable topic verify with course map access advisor
        if (topic.isGradeTopic()) {
            Boolean checkAccess = checkItemAccess(category.getContext(),
                    JForumService.CMItemIdPrefix.TOPIC.getCMItemIdPrefix() + String.valueOf(topic.getId()),
                    sakaiUserId);

            if (!checkAccess) {
                topic.setBlocked(true);
                topic.setBlockedByTitle(getItemAccessMessage(category.getContext(),
                        JForumService.CMItemIdPrefix.TOPIC.getCMItemIdPrefix() + String.valueOf(topic.getId()),
                        sakaiUserId));
                topic.setBlockedByDetails(getItemAccessDetails(category.getContext(),
                        JForumService.CMItemIdPrefix.TOPIC.getCMItemIdPrefix() + String.valueOf(topic.getId()),
                        sakaiUserId));
            }
        } else if (forum.getGradeType() == Grade.GradeType.FORUM.getType()) {
            Boolean checkAccess = checkItemAccess(category.getContext(),
                    JForumService.CMItemIdPrefix.FORUM.getCMItemIdPrefix() + String.valueOf(forum.getId()),
                    sakaiUserId);

            if (!checkAccess) {
                forum.setBlocked(true);
                forum.setBlockedByTitle(getItemAccessMessage(category.getContext(),
                        JForumService.CMItemIdPrefix.FORUM.getCMItemIdPrefix() + String.valueOf(forum.getId()),
                        sakaiUserId));
                forum.setBlockedByDetails(getItemAccessDetails(category.getContext(),
                        JForumService.CMItemIdPrefix.FORUM.getCMItemIdPrefix() + String.valueOf(forum.getId()),
                        sakaiUserId));
            }
        } else if (category.isGradable()) {
            Boolean checkAccess = checkItemAccess(category.getContext(),
                    JForumService.CMItemIdPrefix.CAT.getCMItemIdPrefix() + String.valueOf(category.getId()),
                    sakaiUserId);

            if (!checkAccess) {
                category.setBlocked(true);
                category.setBlockedByTitle(getItemAccessMessage(category.getContext(),
                        JForumService.CMItemIdPrefix.CAT.getCMItemIdPrefix() + String.valueOf(category.getId()),
                        sakaiUserId));
                category.setBlockedByDetails(getItemAccessDetails(category.getContext(),
                        JForumService.CMItemIdPrefix.CAT.getCMItemIdPrefix() + String.valueOf(category.getId()),
                        sakaiUserId));
            }
        }
    }

    /**
     * Clear the topic from thread local cache
     * 
     * @param topic
     *        The topic.
     */
    protected void clearCache(Topic topic) {
        // clear the cache
        this.threadLocalManager.set(cacheKey(String.valueOf(topic.getId())), null);
    }

    /**
     * Create copy of topic
     * 
     * @param topic      topic
     * 
     * @return   The copy of topic
     */
    protected TopicImpl clone(TopicImpl topic) {
        return new TopicImpl(topic);
    }

    /**
     * Remove users who are not in site
     * 
     * @param users   Jforum users
     * 
     * @param copyallsiteusers   All site users
     * @return
     */
    protected User dropUsers(List<User> users, List<String> copyAllSiteUsers) {
        Iterator<User> dropiter = users.iterator();
        User adminuser = null;
        while (dropiter.hasNext()) {
            User dropusr = dropiter.next();

            String dropSakUsrId = dropusr.getSakaiUserId();
            if (dropSakUsrId.equalsIgnoreCase("admin"))
                adminuser = dropusr;
            if (!copyAllSiteUsers.contains(dropSakUsrId.toLowerCase())) {
                dropiter.remove();
            }
        }
        return adminuser;
    }

    /**
     * Filters user topics for the user that the user is accessible to
     * 
     * @param forum      Forum with topics list to be filtered
     * 
     * @param sakaiUserId   Sakai user id
     * 
     * @throws JForumAccessException   If user is not accessible to category or fourm
     */
    protected void filterUserForumTopics(Forum forum, String sakaiUserId) throws JForumAccessException {
        if (forum == null) {
            return;
        }

        boolean categoryForumDates = false;

        Date currentTime = Calendar.getInstance().getTime();

        User user = jforumUserService.getBySakaiUserId(sakaiUserId);

        Category category = forum.getCategory();
        if (category == null) {
            return;
        }

        // ignore forums with category invalid dates
        /*if ((category.getAccessDates() != null) && (category.getAccessDates().getOpenDate() != null) && (category.getAccessDates().getDueDate() != null))
        {
           if (category.getAccessDates().getOpenDate().after(category.getAccessDates().getDueDate()))
           {   
        throw new JForumAccessException(sakaiUserId);
           }
        }*/
        // ignore forums with category invalid dates
        if (category.getAccessDates() != null) {
            if (!category.getAccessDates().isDatesValid()) {
                throw new JForumAccessException(sakaiUserId);
            }
        }

        // ignore forums with invalid dates
        /*if ((forum.getAccessDates() != null) && (forum.getAccessDates().getOpenDate() != null) && (forum.getAccessDates().getDueDate() != null))
        {
           if (forum.getAccessDates().getOpenDate().after(forum.getAccessDates().getDueDate()))
           {   
        throw new JForumAccessException(sakaiUserId);
           }
        }*/
        if ((forum.getAccessDates() != null) && !forum.getAccessDates().isDatesValid()) {
            throw new JForumAccessException(sakaiUserId);
        }

        boolean mayPost = true;
        if (forum.getType() == Forum.ForumType.READ_ONLY.getType()) {
            mayPost = false;
            for (Topic topic : forum.getTopics()) {
                ((TopicImpl) topic).setMayPost(Boolean.FALSE);
            }
        }

        /*if ((forum.getAccessDates() != null) && (forum.getAccessDates().getOpenDate() != null))
        {
           if (forum.getAccessDates().getOpenDate().after(currentTime))
           {   
        category.getForums().clear();
        return;
           }
        }*/

        // forum access type
        if (forum.getAccessType() == Forum.ForumAccess.DENY.getAccessType()) {
            throw new JForumAccessException(sakaiUserId);
        }

        // forum groups
        if (forum.getAccessType() == Forum.ForumAccess.GROUPS.getAccessType()) {
            boolean userInGroup = false;

            try {
                Site site = siteService.getSite(category.getContext());
                Collection<org.sakaiproject.site.api.Group> userGroups = site.getGroupsWithMember(sakaiUserId);

                for (org.sakaiproject.site.api.Group grp : userGroups) {
                    if ((forum.getGroups() != null) && (forum.getGroups().contains(grp.getId()))) {
                        userInGroup = true;
                        break;
                    }
                }
            } catch (IdUnusedException e) {
                if (logger.isWarnEnabled()) {
                    logger.warn("getUserCourseMapItemsByContext: missing site: " + category.getContext());
                }
            }

            // user not in any group
            if (!userInGroup) {
                throw new JForumAccessException(sakaiUserId);
            }
        }

        // forum special access
        List<SpecialAccess> forumSpecialAccessList = forum.getSpecialAccess();
        boolean forumSpecialAccessUser = false;
        boolean forumSpecialAccessUserAccess = false;
        SpecialAccess userSa = null;

        if (user != null && ((forum.getAccessDates() != null) && ((forum.getAccessDates().getOpenDate() != null)
                || (forum.getAccessDates().getDueDate() != null)))) {
            for (SpecialAccess sa : forumSpecialAccessList) {
                if (sa.getUserIds().contains(new Integer(user.getId()))) {
                    userSa = sa;

                    forumSpecialAccessUser = true;

                    if (!sa.isOverrideStartDate()) {
                        sa.getAccessDates().setOpenDate(forum.getAccessDates().getOpenDate());
                    }

                    if (!sa.isOverrideHideUntilOpen()) {
                        sa.getAccessDates().setHideUntilOpen(forum.getAccessDates().isHideUntilOpen());
                    }

                    if (!sa.isOverrideEndDate()) {
                        sa.getAccessDates().setDueDate(forum.getAccessDates().getDueDate());
                    }

                    /*if (!sa.isOverrideLockEndDate())
                    {
                       sa.getAccessDates().setLocked(forum.getAccessDates().isLocked());
                    }*/

                    if (!sa.isOverrideAllowUntilDate()) {

                        sa.getAccessDates().setAllowUntilDate(forum.getAccessDates().getAllowUntilDate());
                    }

                    if (sa.getAccessDates().getOpenDate() == null) {
                        forumSpecialAccessUserAccess = true;
                    } else {
                        if (sa.getAccessDates().getOpenDate().after(currentTime)) {
                            if (!sa.getAccessDates().isHideUntilOpen()) {
                                forumSpecialAccessUserAccess = true;
                            }
                        } else {
                            forumSpecialAccessUserAccess = true;
                        }
                    }
                    break;
                }
            }
        }

        forum.getSpecialAccess().clear();
        if (forumSpecialAccessUser) {
            forum.getSpecialAccess().add(userSa);

            if (forumSpecialAccessUserAccess) {
                if (mayPost) {
                    for (Topic topic : forum.getTopics()) {
                        if (userSa.getAccessDates().getAllowUntilDate() != null) {
                            if (userSa.getAccessDates().getAllowUntilDate().before(currentTime)) {
                                mayPost = false;
                                ((TopicImpl) topic).setMayPost(Boolean.FALSE);
                            }
                        } else if (userSa.getAccessDates().getDueDate() != null) {
                            if (userSa.getAccessDates().getDueDate().before(currentTime)) {
                                mayPost = false;
                                ((TopicImpl) topic).setMayPost(Boolean.FALSE);
                            }
                        }
                    }
                }
            } else {
                throw new JForumAccessException(sakaiUserId);
            }
        }
        // forum dates
        else if ((forum.getAccessDates() != null) && (forum.getAccessDates().getOpenDate() != null)) {
            if (forum.getAccessDates().getOpenDate().after(currentTime)) {
                throw new JForumAccessException(sakaiUserId);
            }
        }

        if ((forum.getAccessDates() != null)
                && ((forum.getAccessDates().getOpenDate() != null) || (forum.getAccessDates().getDueDate() != null)
                        || (forum.getAccessDates().getAllowUntilDate() != null))) {
            categoryForumDates = true;

            //if (mayPost && (!forumSpecialAccessUserAccess) && ((forum.getAccessDates().getDueDate()) != null && (forum.getAccessDates().isLocked())))
            if (mayPost && !forumSpecialAccessUserAccess) {
                if ((forum.getAccessDates().getAllowUntilDate() != null)
                        && (forum.getAccessDates().getAllowUntilDate().before(currentTime))) {
                    for (Topic topic : forum.getTopics()) {
                        ((TopicImpl) topic).setMayPost(Boolean.FALSE);
                    }
                } else if ((forum.getAccessDates().getDueDate() != null)
                        && (forum.getAccessDates().getDueDate().before(currentTime))) {
                    for (Topic topic : forum.getTopics()) {
                        ((TopicImpl) topic).setMayPost(Boolean.FALSE);
                    }
                }
            }
        } else if ((category.getAccessDates() != null) && ((category.getAccessDates().getOpenDate() != null)
                || (category.getAccessDates().getDueDate() != null)
                || (category.getAccessDates().getAllowUntilDate() != null))) {
            categoryForumDates = true;

            // category dates
            if ((!forumSpecialAccessUser) && (category.getAccessDates().getOpenDate() != null)) {
                if (category.getAccessDates().getOpenDate().after(currentTime)) {
                    throw new JForumAccessException(sakaiUserId);
                }
            }

            //if (mayPost && (!forumSpecialAccessUserAccess) && ((category.getAccessDates().getDueDate()) != null && (category.getAccessDates().isLocked())))
            if (mayPost && !forumSpecialAccessUserAccess) {
                if (category.getAccessDates().getAllowUntilDate() != null) {
                    if (category.getAccessDates().getAllowUntilDate().before(currentTime)) {
                        for (Topic topic : forum.getTopics()) {
                            ((TopicImpl) topic).setMayPost(Boolean.FALSE);
                        }
                    }
                } else if ((category.getAccessDates().getDueDate() != null)
                        && category.getAccessDates().getDueDate().before(currentTime)) {
                    for (Topic topic : forum.getTopics()) {
                        ((TopicImpl) topic).setMayPost(Boolean.FALSE);
                    }
                }
            }
        }

        // topics dates if no category or forum dates
        if (!categoryForumDates) {
            checkTopicDates(forum.getTopics(), user);
        }

        for (Topic topic : forum.getTopics()) {
            // for gradable topic verify with course map access advisor
            if (topic.isGradeTopic()) {
                Boolean checkAccess = checkItemAccess(category.getContext(),
                        JForumService.CMItemIdPrefix.TOPIC.getCMItemIdPrefix() + String.valueOf(topic.getId()),
                        sakaiUserId);

                if (!checkAccess) {
                    topic.setBlocked(true);
                    topic.setBlockedByTitle(getItemAccessMessage(category.getContext(),
                            JForumService.CMItemIdPrefix.TOPIC.getCMItemIdPrefix() + String.valueOf(topic.getId()),
                            sakaiUserId));
                    topic.setBlockedByDetails(getItemAccessDetails(category.getContext(),
                            JForumService.CMItemIdPrefix.TOPIC.getCMItemIdPrefix() + String.valueOf(topic.getId()),
                            sakaiUserId));
                }
            }
        }

        //add blocked details for category or forum is they are gradable
        if (forum.getGradeType() == Grade.GradeType.FORUM.getType()) {
            Boolean checkAccess = checkItemAccess(category.getContext(),
                    JForumService.CMItemIdPrefix.FORUM.getCMItemIdPrefix() + String.valueOf(forum.getId()),
                    sakaiUserId);

            if (!checkAccess) {
                forum.setBlocked(true);
                forum.setBlockedByTitle(getItemAccessMessage(category.getContext(),
                        JForumService.CMItemIdPrefix.FORUM.getCMItemIdPrefix() + String.valueOf(forum.getId()),
                        sakaiUserId));
                forum.setBlockedByDetails(getItemAccessDetails(category.getContext(),
                        JForumService.CMItemIdPrefix.FORUM.getCMItemIdPrefix() + String.valueOf(forum.getId()),
                        sakaiUserId));
            }
        } else if (category.isGradable()) {
            Boolean checkAccess = checkItemAccess(category.getContext(),
                    JForumService.CMItemIdPrefix.CAT.getCMItemIdPrefix() + String.valueOf(category.getId()),
                    sakaiUserId);

            if (!checkAccess) {
                category.setBlocked(true);
                category.setBlockedByTitle(getItemAccessMessage(category.getContext(),
                        JForumService.CMItemIdPrefix.CAT.getCMItemIdPrefix() + String.valueOf(category.getId()),
                        sakaiUserId));
                category.setBlockedByDetails(getItemAccessDetails(category.getContext(),
                        JForumService.CMItemIdPrefix.CAT.getCMItemIdPrefix() + String.valueOf(category.getId()),
                        sakaiUserId));
            }
        }

        // topics read status
        checkUnreadTopics(forum, user, category.getContext());
    }

    /**
     * Filters user topics
     * 
     * @param topics   Topics
     * 
     * @param sakaiUserId   sakai user id
     */
    protected void filterUserRecentTopics(List<Topic> topics, String sakaiUserId) {
        if (topics == null || topics.size() == 0) {
            return;
        }

        boolean categoryForumDates = false;

        Date currentTime = Calendar.getInstance().getTime();

        User user = jforumUserService.getBySakaiUserId(sakaiUserId);

        //for (Topic topic : topics)
        Topic topic = null;
        Forum forum = null;
        Category category = null;
        boolean mayPost = true;
        for (Iterator<Topic> iter = topics.iterator(); iter.hasNext();) {
            topic = null;
            forum = null;
            categoryForumDates = false;

            topic = iter.next();

            forum = topic.getForum();
            if (forum == null) {
                iter.remove();
                continue;
            }

            category = forum.getCategory();
            if (category == null) {
                iter.remove();
                continue;
            }

            mayPost = true;
            if (forum.getType() == Forum.ForumType.READ_ONLY.getType()) {
                mayPost = false;

                ((TopicImpl) topic).setMayPost(Boolean.FALSE);
            }

            if (mayPost && topic.getStatus() == Topic.TopicStatus.LOCKED.getStatus()) {
                mayPost = false;
                ((TopicImpl) topic).setMayPost(Boolean.FALSE);
            }

            // ignore forums with category invalid dates
            /*if ((category.getAccessDates() != null) && (category.getAccessDates().getOpenDate() != null) && (category.getAccessDates().getDueDate() != null))
            {
               if (category.getAccessDates().getOpenDate().after(category.getAccessDates().getDueDate()))
               {   
                  iter.remove();
                  continue;
               }
            }*/
            if ((category.getAccessDates() != null) && (!category.getAccessDates().isDatesValid())) {
                iter.remove();
                continue;
            }

            // ignore forums with invalid dates
            /*if ((forum.getAccessDates() != null) && (forum.getAccessDates().getOpenDate() != null) && (forum.getAccessDates().getDueDate() != null))
            {
               if (forum.getAccessDates().getOpenDate().after(forum.getAccessDates().getDueDate()))
               {   
                  iter.remove();
                  continue;
               }
            }*/
            if ((forum.getAccessDates() != null) && (!forum.getAccessDates().isDatesValid())) {
                iter.remove();
                continue;
            }

            // forum access type
            if (forum.getAccessType() == Forum.ForumAccess.DENY.getAccessType()) {
                iter.remove();
                continue;
            }

            // forum groups
            if (forum.getAccessType() == Forum.ForumAccess.GROUPS.getAccessType()) {
                boolean userInGroup = false;

                try {
                    Site site = siteService.getSite(category.getContext());
                    Collection<org.sakaiproject.site.api.Group> userGroups = site.getGroupsWithMember(sakaiUserId);

                    for (org.sakaiproject.site.api.Group grp : userGroups) {
                        if ((forum.getGroups() != null) && (forum.getGroups().contains(grp.getId()))) {
                            userInGroup = true;
                            break;
                        }
                    }
                } catch (IdUnusedException e) {
                    if (logger.isWarnEnabled()) {
                        logger.warn("getUserCourseMapItemsByContext: missing site: " + category.getContext());
                    }
                }

                // user not in any group
                if (!userInGroup) {
                    iter.remove();
                    continue;
                }
            }

            // forum special access
            List<SpecialAccess> forumSpecialAccessList = forum.getSpecialAccess();
            boolean forumSpecialAccessUser = false;
            boolean forumSpecialAccessUserAccess = false;
            SpecialAccess userSa = null;

            if (user != null && ((forum.getAccessDates() != null) && ((forum.getAccessDates().getOpenDate() != null)
                    || (forum.getAccessDates().getDueDate() != null)))) {
                for (SpecialAccess sa : forumSpecialAccessList) {
                    if (sa.getUserIds().contains(new Integer(user.getId()))) {
                        userSa = sa;

                        forumSpecialAccessUser = true;

                        if (!sa.isOverrideStartDate()) {
                            sa.getAccessDates().setOpenDate(forum.getAccessDates().getOpenDate());
                        }

                        if (!sa.isOverrideHideUntilOpen()) {
                            sa.getAccessDates().setHideUntilOpen(forum.getAccessDates().isHideUntilOpen());
                        }

                        if (!sa.isOverrideEndDate()) {
                            sa.getAccessDates().setDueDate(forum.getAccessDates().getDueDate());
                        }

                        /*if (!sa.isOverrideLockEndDate())
                        {
                           sa.getAccessDates().setLocked(forum.getAccessDates().isLocked());
                        }*/

                        if (!sa.isOverrideAllowUntilDate()) {

                            sa.getAccessDates().setAllowUntilDate(forum.getAccessDates().getAllowUntilDate());
                        }

                        if (sa.getAccessDates().getOpenDate() == null) {
                            forumSpecialAccessUserAccess = true;
                        } else {
                            if (sa.getAccessDates().getOpenDate().after(currentTime)) {
                                if (!sa.getAccessDates().isHideUntilOpen()) {
                                    forumSpecialAccessUserAccess = true;
                                }
                            } else {
                                forumSpecialAccessUserAccess = true;
                            }
                        }
                        break;
                    }
                }
            }

            forum.getSpecialAccess().clear();
            if (forumSpecialAccessUser) {
                forum.getSpecialAccess().add(userSa);

                if (forumSpecialAccessUserAccess) {
                    //if (mayPost && userSa.getAccessDates().getDueDate() != null && userSa.getAccessDates().isLocked())
                    if (mayPost) {
                        if (userSa.getAccessDates().getAllowUntilDate() != null) {
                            if (userSa.getAccessDates().getAllowUntilDate().before(currentTime)) {
                                mayPost = false;
                                ((TopicImpl) topic).setMayPost(Boolean.FALSE);
                            }
                        } else if (userSa.getAccessDates().getDueDate() != null) {
                            if (userSa.getAccessDates().getDueDate().before(currentTime)) {
                                mayPost = false;
                                ((TopicImpl) topic).setMayPost(Boolean.FALSE);
                            }
                        }
                    }
                } else {
                    iter.remove();
                    continue;
                }
            }
            // forum dates
            else if ((forum.getAccessDates() != null) && (forum.getAccessDates().getOpenDate() != null)) {
                if (forum.getAccessDates().getOpenDate().after(currentTime)) {
                    iter.remove();
                    continue;
                }
            }

            if ((forum.getAccessDates() != null) && ((forum.getAccessDates().getOpenDate() != null)
                    || (forum.getAccessDates().getDueDate() != null)
                    || (forum.getAccessDates().getAllowUntilDate() != null))) {
                categoryForumDates = true;

                //if (mayPost && (!forumSpecialAccessUserAccess) && ((forum.getAccessDates().getDueDate()) != null && (forum.getAccessDates().isLocked())))
                if (mayPost && (!forumSpecialAccessUserAccess)) {
                    if (forum.getAccessDates().getAllowUntilDate() != null) {
                        if (forum.getAccessDates().getAllowUntilDate().before(currentTime)) {
                            mayPost = false;
                            ((TopicImpl) topic).setMayPost(Boolean.FALSE);
                        }
                    } else if (forum.getAccessDates().getDueDate() != null) {
                        if (forum.getAccessDates().getDueDate().before(currentTime)) {
                            mayPost = false;
                            ((TopicImpl) topic).setMayPost(Boolean.FALSE);
                        }
                    }
                }
            } else if ((category.getAccessDates() != null) && ((category.getAccessDates().getOpenDate() != null)
                    || (category.getAccessDates().getDueDate() != null)
                    || (category.getAccessDates().getAllowUntilDate() != null))) {
                categoryForumDates = true;

                // category dates
                if ((!forumSpecialAccessUser) && (category.getAccessDates().getOpenDate() != null)) {
                    if (category.getAccessDates().getOpenDate().after(currentTime)) {
                        iter.remove();
                        continue;
                    }
                }

                //if (mayPost && (!forumSpecialAccessUserAccess) && ((category.getAccessDates().getDueDate()) != null && (category.getAccessDates().isLocked())))
                if (mayPost && !forumSpecialAccessUserAccess) {
                    if (category.getAccessDates().getAllowUntilDate() != null) {
                        if (category.getAccessDates().getAllowUntilDate().before(currentTime)) {
                            mayPost = false;
                            ((TopicImpl) topic).setMayPost(Boolean.FALSE);
                        }
                    } else if ((category.getAccessDates().getDueDate() != null)
                            && category.getAccessDates().getDueDate().before(currentTime)) {
                        mayPost = false;
                        ((TopicImpl) topic).setMayPost(Boolean.FALSE);
                    }
                }
            }

            // topics dates if no category or forum dates
            if (!categoryForumDates) {
                // topic special access
                List<SpecialAccess> topicSpecialAccessList = topic.getSpecialAccess();
                boolean topicSpecialAccessUser = false;
                boolean topicSpecialAccessUserAccess = false;
                SpecialAccess userTopicSa = null;

                if ((user != null)
                        && ((topic.getAccessDates() != null) && ((topic.getAccessDates().getOpenDate() != null)
                                || (topic.getAccessDates().getDueDate() != null)))) {
                    for (SpecialAccess sa : topicSpecialAccessList) {
                        if (sa.getUserIds().contains(new Integer(user.getId()))) {
                            userTopicSa = sa;

                            topicSpecialAccessUser = true;

                            if (!sa.isOverrideStartDate()) {
                                sa.getAccessDates().setOpenDate(topic.getAccessDates().getOpenDate());
                            }

                            if (!sa.isOverrideHideUntilOpen()) {
                                sa.getAccessDates().setHideUntilOpen(topic.getAccessDates().isHideUntilOpen());
                            }

                            if (!sa.isOverrideEndDate()) {
                                sa.getAccessDates().setDueDate(topic.getAccessDates().getDueDate());
                            }

                            /*if (!sa.isOverrideLockEndDate())
                            {
                               sa.getAccessDates().setLocked(topic.getAccessDates().isLocked());
                            }*/

                            if (!sa.isOverrideAllowUntilDate()) {

                                sa.getAccessDates().setAllowUntilDate(topic.getAccessDates().getAllowUntilDate());
                            }

                            if (sa.getAccessDates().getOpenDate() == null) {
                                topicSpecialAccessUserAccess = true;
                            } else {
                                if (sa.getAccessDates().getOpenDate().after(currentTime)) {
                                    if (!sa.getAccessDates().isHideUntilOpen()) {
                                        topicSpecialAccessUserAccess = true;
                                    }
                                } else {
                                    topicSpecialAccessUserAccess = true;
                                }
                            }

                            break;
                        }
                    }
                }

                topic.getSpecialAccess().clear();
                if (topicSpecialAccessUser) {
                    topic.getSpecialAccess().add(userTopicSa);

                    if (topicSpecialAccessUserAccess) {
                        //if (mayPost && userTopicSa.getAccessDates().getDueDate() != null && userTopicSa.getAccessDates().isLocked())
                        if (userTopicSa.getAccessDates().getAllowUntilDate() != null) {
                            if (userTopicSa.getAccessDates().getAllowUntilDate().before(currentTime)) {
                                mayPost = false;
                                ((TopicImpl) topic).setMayPost(Boolean.FALSE);
                            }
                        } else if (userTopicSa.getAccessDates().getDueDate() != null) {
                            if (userTopicSa.getAccessDates().getDueDate().before(currentTime)) {
                                mayPost = false;
                                ((TopicImpl) topic).setMayPost(Boolean.FALSE);
                            }
                        }
                    } else {
                        iter.remove();
                        continue;
                    }
                }
                // topic dates
                else if (topic.getAccessDates() != null) {
                    if ((topic.getAccessDates().getOpenDate() != null)
                            && (topic.getAccessDates().getOpenDate().after(currentTime))) {
                        iter.remove();
                        continue;
                    }

                    //if (mayPost && topic.getAccessDates().getDueDate() != null && topic.getAccessDates().isLocked())
                    if (mayPost) {
                        if (topic.getAccessDates().getAllowUntilDate() != null) {
                            if (topic.getAccessDates().getAllowUntilDate().before(currentTime)) {
                                mayPost = false;
                                ((TopicImpl) topic).setMayPost(Boolean.FALSE);
                            }
                        } else if (topic.getAccessDates().getDueDate() != null) {
                            if (topic.getAccessDates().getDueDate().before(currentTime)) {
                                mayPost = false;
                                ((TopicImpl) topic).setMayPost(Boolean.FALSE);
                            }
                        }
                    }
                }
            }

            //for (Topic topic : forum.getTopics())
            //{
            // for gradable topic verify with course map access advisor
            if (topic.isGradeTopic()) {
                Boolean checkAccess = checkItemAccess(category.getContext(),
                        JForumService.CMItemIdPrefix.TOPIC.getCMItemIdPrefix() + String.valueOf(topic.getId()),
                        sakaiUserId);

                if (!checkAccess) {
                    topic.setBlocked(true);
                    topic.setBlockedByTitle(getItemAccessMessage(category.getContext(),
                            JForumService.CMItemIdPrefix.TOPIC.getCMItemIdPrefix() + String.valueOf(topic.getId()),
                            sakaiUserId));
                    topic.setBlockedByDetails(getItemAccessDetails(category.getContext(),
                            JForumService.CMItemIdPrefix.TOPIC.getCMItemIdPrefix() + String.valueOf(topic.getId()),
                            sakaiUserId));
                }
            }
            //}

            //add blocked details for category or forum is they are gradable
            if (forum.getGradeType() == Grade.GradeType.FORUM.getType()) {
                Boolean checkAccess = checkItemAccess(category.getContext(),
                        JForumService.CMItemIdPrefix.FORUM.getCMItemIdPrefix() + String.valueOf(forum.getId()),
                        sakaiUserId);

                if (!checkAccess) {
                    forum.setBlocked(true);
                    forum.setBlockedByTitle(getItemAccessMessage(category.getContext(),
                            JForumService.CMItemIdPrefix.FORUM.getCMItemIdPrefix() + String.valueOf(forum.getId()),
                            sakaiUserId));
                    forum.setBlockedByDetails(getItemAccessDetails(category.getContext(),
                            JForumService.CMItemIdPrefix.FORUM.getCMItemIdPrefix() + String.valueOf(forum.getId()),
                            sakaiUserId));
                }
            } else if (category.isGradable()) {
                Boolean checkAccess = checkItemAccess(category.getContext(),
                        JForumService.CMItemIdPrefix.CAT.getCMItemIdPrefix() + String.valueOf(category.getId()),
                        sakaiUserId);

                if (!checkAccess) {
                    category.setBlocked(true);
                    category.setBlockedByTitle(getItemAccessMessage(category.getContext(),
                            JForumService.CMItemIdPrefix.CAT.getCMItemIdPrefix() + String.valueOf(category.getId()),
                            sakaiUserId));
                    category.setBlockedByDetails(getItemAccessDetails(category.getContext(),
                            JForumService.CMItemIdPrefix.CAT.getCMItemIdPrefix() + String.valueOf(category.getId()),
                            sakaiUserId));
                }
            }
        }
    }

    /**
     * Checks the user access to topic
     * 
     * @param Topic   Topic with posts
     * 
     * @param userId   Sakai user id
     */
    protected void filterUserTopicPosts(Topic topic, String sakaiUserId) throws JForumAccessException {

        if (topic == null) {
            return;
        }

        Category category = null;
        Forum forum = null;

        forum = topic.getForum();

        if (forum == null) {
            return;
        }

        category = forum.getCategory();
        if (category == null) {
            return;
        }

        boolean mayPost = true;
        if (forum.getType() == Forum.ForumType.READ_ONLY.getType()) {
            mayPost = false;
            ((TopicImpl) topic).setMayPost(Boolean.FALSE);
        }

        if (mayPost && topic.getStatus() == Topic.TopicStatus.LOCKED.getStatus()) {
            mayPost = false;
            ((TopicImpl) topic).setMayPost(Boolean.FALSE);
        }

        boolean categoryForumDates = false;

        Date currentTime = Calendar.getInstance().getTime();

        User user = jforumUserService.getBySakaiUserId(sakaiUserId);

        // ignore forums with category invalid dates
        /*if ((category.getAccessDates() != null) && (category.getAccessDates().getOpenDate() != null)
        && (category.getAccessDates().getDueDate() != null))
        {
           if (category.getAccessDates().getOpenDate().after(category.getAccessDates().getDueDate()))
           {
        throw new JForumAccessException(sakaiUserId);
           }
        }*/
        // ignore forums with category invalid dates
        if (category.getAccessDates() != null) {
            if (!category.getAccessDates().isDatesValid()) {
                throw new JForumAccessException(sakaiUserId);
            }
        }

        // ignore forums with invalid dates
        if ((forum.getAccessDates() != null) && (forum.getAccessDates().getOpenDate() != null)
                && (forum.getAccessDates().getDueDate() != null)) {
            if (forum.getAccessDates().getOpenDate().after(forum.getAccessDates().getDueDate())) {
                throw new JForumAccessException(sakaiUserId);
            }
        }

        // forum access type
        if (forum.getAccessType() == Forum.ForumAccess.DENY.getAccessType()) {
            throw new JForumAccessException(sakaiUserId);
        }

        // forum groups
        if (forum.getAccessType() == Forum.ForumAccess.GROUPS.getAccessType()) {
            boolean userInGroup = false;

            try {
                Site site = siteService.getSite(category.getContext());
                Collection<org.sakaiproject.site.api.Group> userGroups = site.getGroupsWithMember(sakaiUserId);

                for (org.sakaiproject.site.api.Group grp : userGroups) {
                    if ((forum.getGroups() != null) && (forum.getGroups().contains(grp.getId()))) {
                        userInGroup = true;
                        break;
                    }
                }
            } catch (IdUnusedException e) {
                if (logger.isWarnEnabled()) {
                    logger.warn("filterUserTopicPosts: missing site: " + category.getContext());
                }
            }

            // user not in any group
            if (!userInGroup) {
                throw new JForumAccessException(sakaiUserId);
            }
        }

        // forum special access
        List<SpecialAccess> forumSpecialAccessList = forum.getSpecialAccess();
        boolean forumSpecialAccessUser = false;
        boolean forumSpecialAccessUserAccess = false;
        SpecialAccess userSa = null;

        if (user != null && ((forum.getAccessDates() != null) && ((forum.getAccessDates().getOpenDate() != null)
                || (forum.getAccessDates().getDueDate() != null)))) {
            for (SpecialAccess sa : forumSpecialAccessList) {
                if (sa.getUserIds().contains(new Integer(user.getId()))) {
                    userSa = sa;

                    forumSpecialAccessUser = true;

                    if (!sa.isOverrideStartDate()) {
                        sa.getAccessDates().setOpenDate(forum.getAccessDates().getOpenDate());
                    }

                    if (!sa.isOverrideHideUntilOpen()) {
                        sa.getAccessDates().setHideUntilOpen(forum.getAccessDates().isHideUntilOpen());
                    }

                    if (!sa.isOverrideEndDate()) {
                        sa.getAccessDates().setDueDate(forum.getAccessDates().getDueDate());
                    }

                    /*if (!sa.isOverrideLockEndDate())
                    {
                       sa.getAccessDates().setLocked(forum.getAccessDates().isLocked());
                    }*/

                    if (!sa.isOverrideAllowUntilDate()) {

                        sa.getAccessDates().setAllowUntilDate(forum.getAccessDates().getAllowUntilDate());
                    }

                    if (sa.getAccessDates().getOpenDate() == null) {
                        forumSpecialAccessUserAccess = true;
                    } else {
                        if (sa.getAccessDates().getOpenDate().after(currentTime)) {
                            if (!sa.getAccessDates().isHideUntilOpen()) {
                                forumSpecialAccessUserAccess = true;
                            }
                        } else {
                            forumSpecialAccessUserAccess = true;
                        }
                    }
                    break;
                }
            }
        }

        forum.getSpecialAccess().clear();
        if (forumSpecialAccessUser) {
            forum.getSpecialAccess().add(userSa);

            if (forumSpecialAccessUserAccess) {
                //if (mayPost && userSa.getAccessDates().getDueDate() != null && userSa.getAccessDates().isLocked())
                if (mayPost) {
                    if (userSa.getAccessDates().getAllowUntilDate() != null) {
                        if (userSa.getAccessDates().getAllowUntilDate().before(currentTime)) {
                            mayPost = false;
                            ((TopicImpl) topic).setMayPost(Boolean.FALSE);
                        }
                    } else if (userSa.getAccessDates().getDueDate() != null) {
                        if (userSa.getAccessDates().getDueDate().before(currentTime)) {
                            mayPost = false;
                            ((TopicImpl) topic).setMayPost(Boolean.FALSE);
                        }
                    }
                }
            } else {
                throw new JForumAccessException(sakaiUserId);
            }
        }
        // forum dates
        else if ((forum.getAccessDates() != null) && (forum.getAccessDates().getOpenDate() != null)) {
            if (forum.getAccessDates().getOpenDate().after(currentTime)) {
                throw new JForumAccessException(sakaiUserId);
            }
        }

        if ((forum.getAccessDates() != null)
                && ((forum.getAccessDates().getOpenDate() != null) || (forum.getAccessDates().getDueDate() != null)
                        || (forum.getAccessDates().getAllowUntilDate() != null))) {
            categoryForumDates = true;

            //if (mayPost && (!forumSpecialAccessUserAccess) && ((forum.getAccessDates().getDueDate()) != null && (forum.getAccessDates().isLocked())))
            if (mayPost && (!forumSpecialAccessUserAccess)) {
                if (forum.getAccessDates().getAllowUntilDate() != null) {
                    if (forum.getAccessDates().getAllowUntilDate().before(currentTime)) {
                        mayPost = false;
                        ((TopicImpl) topic).setMayPost(Boolean.FALSE);
                    }
                } else if (forum.getAccessDates().getDueDate() != null) {
                    if (forum.getAccessDates().getDueDate().before(currentTime)) {
                        mayPost = false;
                        ((TopicImpl) topic).setMayPost(Boolean.FALSE);
                    }
                }
            }
        } else if ((category.getAccessDates() != null) && ((category.getAccessDates().getOpenDate() != null)
                || (category.getAccessDates().getDueDate() != null)
                || (category.getAccessDates().getAllowUntilDate() != null))) {
            categoryForumDates = true;

            // category dates
            if ((!forumSpecialAccessUser) && (category.getAccessDates().getOpenDate() != null)) {
                if (category.getAccessDates().getOpenDate().after(currentTime)) {
                    throw new JForumAccessException(sakaiUserId);
                }
            }

            //if (mayPost && (!forumSpecialAccessUserAccess) && ((category.getAccessDates().getDueDate()) != null && (category.getAccessDates().isLocked())))
            if (mayPost && !forumSpecialAccessUserAccess) {
                if (category.getAccessDates().getAllowUntilDate() != null) {
                    if (category.getAccessDates().getAllowUntilDate().before(currentTime)) {
                        mayPost = false;
                        ((TopicImpl) topic).setMayPost(Boolean.FALSE);
                    }
                } else if ((category.getAccessDates().getDueDate() != null)
                        && category.getAccessDates().getDueDate().before(currentTime)) {
                    mayPost = false;
                    ((TopicImpl) topic).setMayPost(Boolean.FALSE);
                }
            }
        }

        // topics dates if no category or forum dates
        if (!categoryForumDates) {
            checkTopicDates(topic, user);
        }

        // for gradable topic verify with course map access advisor
        if (topic.isGradeTopic()) {
            Boolean checkAccess = checkItemAccess(category.getContext(),
                    JForumService.CMItemIdPrefix.TOPIC.getCMItemIdPrefix() + String.valueOf(topic.getId()),
                    sakaiUserId);

            if (!checkAccess) {
                topic.setBlocked(true);
                topic.setBlockedByTitle(getItemAccessMessage(category.getContext(),
                        JForumService.CMItemIdPrefix.TOPIC.getCMItemIdPrefix() + String.valueOf(topic.getId()),
                        sakaiUserId));
                topic.setBlockedByDetails(getItemAccessDetails(category.getContext(),
                        JForumService.CMItemIdPrefix.TOPIC.getCMItemIdPrefix() + String.valueOf(topic.getId()),
                        sakaiUserId));
            }
        } else if (forum.getGradeType() == Grade.GradeType.FORUM.getType()) {
            Boolean checkAccess = checkItemAccess(category.getContext(),
                    JForumService.CMItemIdPrefix.FORUM.getCMItemIdPrefix() + String.valueOf(forum.getId()),
                    sakaiUserId);

            if (!checkAccess) {
                forum.setBlocked(true);
                forum.setBlockedByTitle(getItemAccessMessage(category.getContext(),
                        JForumService.CMItemIdPrefix.FORUM.getCMItemIdPrefix() + String.valueOf(forum.getId()),
                        sakaiUserId));
                forum.setBlockedByDetails(getItemAccessDetails(category.getContext(),
                        JForumService.CMItemIdPrefix.FORUM.getCMItemIdPrefix() + String.valueOf(forum.getId()),
                        sakaiUserId));
            }
        } else if (category.isGradable()) {
            Boolean checkAccess = checkItemAccess(category.getContext(),
                    JForumService.CMItemIdPrefix.CAT.getCMItemIdPrefix() + String.valueOf(category.getId()),
                    sakaiUserId);

            if (!checkAccess) {
                category.setBlocked(true);
                category.setBlockedByTitle(getItemAccessMessage(category.getContext(),
                        JForumService.CMItemIdPrefix.CAT.getCMItemIdPrefix() + String.valueOf(category.getId()),
                        sakaiUserId));
                category.setBlockedByDetails(getItemAccessDetails(category.getContext(),
                        JForumService.CMItemIdPrefix.CAT.getCMItemIdPrefix() + String.valueOf(category.getId()),
                        sakaiUserId));
            }
        }

        // participant post edit permission. participants cannot edit the post
        // if topic is locked or forum is read only or category/forum/topic has
        // dates and locked after the end date
        for (Post post : topic.getPosts()) {
            if (post.getUserId() == user.getId()) {
                // topic locked
                if (topic.getStatus() == Topic.TopicStatus.LOCKED.getStatus()) {
                    continue;
                }

                // read only forum
                if (forum.getType() == Forum.ForumType.READ_ONLY.getType()) {
                    continue;
                }

                // category due date passed and category locked
                if ((category.getAccessDates() != null) && ((category.getAccessDates().getOpenDate() != null)
                        || (category.getAccessDates().getDueDate() != null)
                        || (category.getAccessDates().getAllowUntilDate() != null))) {
                    if (category.getAccessDates().getAllowUntilDate() != null) {
                        if (category.getAccessDates().getAllowUntilDate().before(currentTime)) {
                            continue;
                        }
                    } else if (category.getAccessDates().getDueDate() != null) {
                        //if (category.getAccessDates().getDueDate().before(currentTime) && category.getAccessDates().isLocked())
                        if (category.getAccessDates().getDueDate().before(currentTime)) {
                            continue;
                        }
                    }
                }

                // forum special access user
                if (forumSpecialAccessUser) {
                    if (forumSpecialAccessUserAccess && userSa != null) {
                        //if (userSa.getAccessDates().getDueDate() != null && userSa.getAccessDates().getDueDate().before(currentTime) && userSa.getAccessDates().isLocked())
                        if (userSa.getAccessDates().getAllowUntilDate() != null) {
                            if (userSa.getAccessDates().getAllowUntilDate().before(currentTime)) {
                                continue;
                            }
                        } else if (userSa.getAccessDates().getDueDate() != null) {
                            if (userSa.getAccessDates().getDueDate().before(currentTime)) {
                                continue;
                            }
                        }
                    } else {
                        continue;
                    }
                }
                // forum due date passed and forum locked
                else if ((forum.getAccessDates() != null) && (forum.getAccessDates().getDueDate() != null
                        || forum.getAccessDates().getAllowUntilDate() != null)) {
                    //if (forum.getAccessDates().getDueDate().before(currentTime) && forum.getAccessDates().isLocked())
                    if (forum.getAccessDates().getAllowUntilDate() != null) {
                        if (forum.getAccessDates().getAllowUntilDate().before(currentTime)) {
                            continue;
                        }
                    } else if (forum.getAccessDates().getDueDate() != null) {
                        if (forum.getAccessDates().getDueDate().before(currentTime)) {
                            continue;
                        }
                    }
                }

                if ((topic.getAccessDates() != null) && ((topic.getAccessDates().getOpenDate() != null)
                        || (topic.getAccessDates().getDueDate() != null)
                        || (topic.getAccessDates().getAllowUntilDate() != null))) {
                    boolean topicSpecialAccessUser = false;
                    // topic special access user
                    if (topic.getSpecialAccess().size() == 1) {
                        SpecialAccess userTopicSa = topic.getSpecialAccess().get(0);

                        if (userTopicSa != null) {
                            topicSpecialAccessUser = true;

                            //if (userTopicSa.getAccessDates().getDueDate() != null && userTopicSa.getAccessDates().getDueDate().before(currentTime) && userTopicSa.getAccessDates().isLocked())
                            if (userTopicSa.getAccessDates().getAllowUntilDate() != null) {
                                if (userTopicSa.getAccessDates().getAllowUntilDate().before(currentTime)) {
                                    continue;
                                }
                            } else if (userTopicSa.getAccessDates().getDueDate() != null) {
                                if (userTopicSa.getAccessDates().getDueDate().before(currentTime)) {
                                    continue;
                                }
                            }
                        }
                    }

                    // topic due date passed and topic locked
                    if (!topicSpecialAccessUser && ((topic.getAccessDates().getDueDate() != null)
                            || (topic.getAccessDates().getAllowUntilDate() != null))) {
                        //if (topic.getAccessDates().getDueDate().before(currentTime) && topic.getAccessDates().isLocked())
                        if (topic.getAccessDates().getAllowUntilDate() != null) {
                            if (topic.getAccessDates().getAllowUntilDate().before(currentTime)) {
                                continue;
                            }
                        } else if (topic.getAccessDates().getDueDate() != null) {
                            if (topic.getAccessDates().getDueDate().before(currentTime)) {
                                continue;
                            }
                        }
                    }
                }

                ((PostImpl) post).setCanEdit(true);
            }
        }
    }

    /**
     * Gets accessible topics and not hidden(visible but not accessible) count of the forum
     * 
     * @param forumId   Forum id
     * 
     * @return   The accessible topics and not hidden(visible but not accessible) count of the forum
     */
    protected int getAccessibleTopicsCountByForum(int forumId) {
        StringBuilder sql = new StringBuilder();

        // though query can be used with just checking hide_until_open = 0 for sanity check time is considered
        sql.append(
                "SELECT count(1) AS accessible_topics_count FROM jforum_topics WHERE forum_id = ? AND (start_date < ? OR start_date IS NULL OR (start_date > ? AND hide_until_open = 0)) ");

        Object[] fields;
        int i = 0;

        fields = new Object[3];
        fields[i++] = forumId;
        fields[i++] = new Timestamp(System.currentTimeMillis());
        fields[i++] = new Timestamp(System.currentTimeMillis());

        int topicMessagesCount = 0;

        final List<Integer> count = new ArrayList<Integer>();

        this.sqlService.dbRead(sql.toString(), fields, new SqlReader() {
            public Object readSqlResultRecord(ResultSet result) {
                try {
                    count.add(result.getInt("accessible_topics_count"));

                    return null;
                } catch (SQLException e) {
                    if (logger.isWarnEnabled()) {
                        logger.warn("getAccessibleTopicsCountByForum: " + e, e);
                    }
                    return null;
                }
            }
        });

        if (count.size() == 1) {
            topicMessagesCount = count.get(0);
        }
        return topicMessagesCount;
    }

    /**
     * get the accessible and not hidden(visible but not accessible) topics messages count
     * 
     * @param forumId   The forum id
     * 
     * @return      The count of the accessible and not hidden(visible but not accessible) topics messages count
     */
    protected int getAccessibleTopicsMessagesCountByForum(int forumId) {
        StringBuilder sql = new StringBuilder();

        // though query can be used with just checking hide_until_open = 0 for sanity check time is considered
        sql.append(
                "SELECT COUNT(p.post_id) AS total_posts FROM jforum_topics t LEFT JOIN jforum_posts p ON p.topic_id = t.topic_id ");
        sql.append(
                "WHERE t.forum_id = ? AND  (start_date < ? OR start_date IS NULL OR (start_date > ? AND hide_until_open = 0 )) GROUP BY t.forum_id");

        Object[] fields;
        int i = 0;

        fields = new Object[3];
        fields[i++] = forumId;
        fields[i++] = new Timestamp(System.currentTimeMillis());
        fields[i++] = new Timestamp(System.currentTimeMillis());

        int topicMessagesCount = 0;

        final List<Integer> count = new ArrayList<Integer>();

        this.sqlService.dbRead(sql.toString(), fields, new SqlReader() {
            public Object readSqlResultRecord(ResultSet result) {
                try {
                    count.add(result.getInt("total_posts"));

                    return null;
                } catch (SQLException e) {
                    if (logger.isWarnEnabled()) {
                        logger.warn("getAccessibleTopicsMessagesCountByForum: " + e, e);
                    }
                    return null;
                }
            }
        });

        if (count.size() == 1) {
            topicMessagesCount = count.get(0);
        }
        return topicMessagesCount;
    }

    /**
     * Gets the item access details if the item is blocked in the coursemap
     * 
     * @param context   The context
     * 
     * @param id      The item id
     * 
     * @param userId   The user id
     * 
     * @return      The item access details
     */
    protected String getItemAccessDetails(String context, String id, String userId) {
        if (this.accessAdvisor == null)
            return null;

        return accessAdvisor.details("sakai.jforum", context, id, userId);
    }

    /**
     * Gets the message if the item is blocked in the coursemap
     * 
     * @param context   The context
     * 
     * @param id      The item id
     * 
     * @param userId   The user id
     * 
     * @return   The message if the item is blocked
     */
    protected String getItemAccessMessage(String context, String id, String userId) {
        if (this.accessAdvisor == null)
            return null;

        return accessAdvisor.message("sakai.jforum", context, id, userId);

    }

    /**
     * Gets the user topic mark or visit time
     *  
     * @param topicId   Topic id
     * 
     * @param user   JForum user
     * 
     * @return   The user topic with mark time or null
     */
    protected Topic getUserTopicMarkTime(int topicId, User user) {
        if ((topicId == 0) || (user == null)) {
            new IllegalArgumentException();
        }

        return topicDao.selectMarkTime(topicId, user.getId());
    }

    /**
     * Mark topic and forum read status of the user
     * 
     * @param topic         Topic
     * 
     * @param sakaiUserId   Sakai user id
     */
    protected void markTopicForumReadStatus(Topic topic, String sakaiUserId) {
        if ((topic == null) || (sakaiUserId == null || sakaiUserId.trim().length() == 0)) {
            return;
        }

        User user = jforumUserService.getBySakaiUserId(sakaiUserId);

        Forum forum = topic.getForum();
        Category category = forum.getCategory();

        if (user == null) {
            topic.setRead(Boolean.FALSE);
            forum.setUnread(true);

            return;
        }

        // marked all read time
        Date markAllReadTime = jforumUserService.getMarkAllReadTime(category.getContext(), user.getId());

        //mark user topic read status. This may not be needed if this method is called when user visits the topic posts as topic is marked as visited.
        markUserTopicReadStatus(user, markAllReadTime, topic);

        if (!topic.getRead().booleanValue()) {
            forum.setUnread(true);
            return;
        }

        //This may not be needed - if topic is read check forum read status with other topics - get all accessible topics with latest post time and check with topic read time
    }

    /**
     * mark user topic read status
     * 
     * @param user      JForum user
     * 
     * @param markAllReadTime   user mark all read time
     * 
     * @param topic      Topic
     */
    protected void markUserTopicReadStatus(User user, Date markAllReadTime, Topic topic) {
        String postsPerPageStr = ServerConfigurationService.getString(POSTS_PER_PAGE);
        //String hotTopicBeginStr = ServerConfigurationService.getString(HOT_TOPIC_BEGIN);

        //int hotBegin = -1;
        int postsPerPage = -1;

        /*if ((hotTopicBeginStr != null) && (hotTopicBeginStr.trim().length() > 0))
        {
           try
           {
        hotBegin = Integer.parseInt(hotTopicBeginStr);
           }
           catch (NumberFormatException e)
           {
        //ignore error
           }
        }*/

        if ((postsPerPageStr != null) && (postsPerPageStr.trim().length() > 0)) {
            try {
                postsPerPage = Integer.parseInt(postsPerPageStr);
            } catch (NumberFormatException e) {
                //ignore error
            }
        }

        // Check if this is a hot topic
        /*if (hotBegin != -1)
        {
           topic.setHot(topic.getTotalReplies() >= hotBegin);
        }*/

        // posts paginate
        if (postsPerPage != -1) {
            if (topic.getTotalReplies() + 1 > postsPerPage) {
                topic.setPaginate(true);
            } else {
                topic.setPaginate(false);
            }
        }

        //topicMarkTime = getUserTopicMarkTime(topic.getId(), user);

        //Date topicMarkTime = null;
        Date topicLatestPostTime = null;
        Topic lastPostimeTopic = null;
        Topic topicUserMarkTime = getUserTopicMarkTime(topic.getId(), user);

        lastPostimeTopic = getTopicLatestPosttime(topic.getId(), user.getId(), markAllReadTime, false);
        if (lastPostimeTopic != null) {
            topicLatestPostTime = lastPostimeTopic.getTime();
        }

        if (topicUserMarkTime == null) {
            if (topicLatestPostTime != null) {
                topic.setRead(false);
            } else {
                topic.setRead(true);
            }
        } else {
            /*Date topicMarkTime = topicUserMarkTime.getTime();
                
            if (markAllReadTime != null)
            {
               if (topicMarkTime.before(markAllReadTime))
               {
                  topic.setRead(true);
               }
            }
            else if (!topicUserMarkTime.getRead())
            {
               topic.setRead(false);
            }            
            else if (topicLatestPostTime != null)
            {
               if  (topicLatestPostTime.before(topicMarkTime))
               {
                  topic.setRead(true);
               }
               else
               {
                  topic.setRead(false);
               }
            }
            else
            {
               topic.setRead(true);
            }*/
            // if topic is marked as unread mark the forum as unread
            if (!topicUserMarkTime.getRead()) {
                topic.setRead(false);
                return;
            }

            Date topicMarkTime = topicUserMarkTime.getTime();

            if (topicLatestPostTime != null) {
                if (topicLatestPostTime.before(topicMarkTime)) {
                    topic.setRead(true);
                } else {
                    topic.setRead(false);
                }
            } else {
                topic.setRead(true);
            }

        }
    }

    /**
     * Notify new topic to users who opted to received new topic email notification
     * 
     * @param category   Category
     */
    protected void notifyNewTopicToUsers(Category category) {
        List<User> usersToNotify = updateUsersInfo(category.getContext());

        Forum topicForum = category.getForums().get(0);
        Topic topic = category.getForums().get(0).getTopics().get(0);
        Post post = category.getForums().get(0).getTopics().get(0).getPosts().get(0);

        // if forum has groups send to the members of the groups
        boolean sendToGroups = false;
        Site site = null;
        if ((topicForum.getAccessType() == Forum.ForumAccess.GROUPS.getAccessType())
                && (topicForum.getGroups() != null && topicForum.getGroups().size() > 0)) {
            sendToGroups = true;

            try {
                site = siteService.getSite(category.getContext());
            } catch (IdUnusedException e) {
                if (logger.isWarnEnabled()) {
                    logger.warn("notifyNewTopicToUsers: missing site: " + category.getContext());
                }
            }
        }

        // remove the user who posted the message
        int posterId = category.getForums().get(0).getTopics().get(0).getPostedBy().getId();

        Iterator<User> usersIterator = usersToNotify.iterator();
        while (usersIterator.hasNext()) {
            User usr = (User) usersIterator.next();

            if (usr == null) {
                usersIterator.remove();
                continue;
            }

            if (usr.getId() == posterId) {
                usersIterator.remove();
                continue;
            }

            // notify only to the users who belong to the group(s) associated with forum
            if (sendToGroups
                    && !jforumSecurityService.isJForumFacilitator(category.getContext(), usr.getSakaiUserId())) {
                boolean userInGroup = false;

                Collection sakaiSiteGroups = site.getGroupsWithMember(usr.getSakaiUserId());

                for (Iterator<Group> usrGrpIter = sakaiSiteGroups.iterator(); usrGrpIter.hasNext();) {
                    org.sakaiproject.site.api.Group grp = (org.sakaiproject.site.api.Group) usrGrpIter.next();
                    if (topicForum.getGroups().contains(grp.getId())) {
                        userInGroup = true;
                        break;
                    }
                }

                if (!userInGroup) {
                    usersIterator.remove();
                    continue;
                }
            }

            if (!usr.isNotifyOnMessagesEnabled() || (usr.getEmail() == null || usr.getEmail().trim().length() == 0)
                    || (!jforumSecurityService.isUserActive(category.getContext(), usr.getSakaiUserId()))) {
                usersIterator.remove();
                continue;
            }

            try {
                new InternetAddress(usr.getEmail());
            } catch (AddressException e) {
                if (logger.isWarnEnabled())
                    logger.warn("notifyNewTopicToUsers(...) : " + usr.getEmail()
                            + " is invalid. And exception is : " + e);
                usersIterator.remove();
                continue;
            }
        }

        if (usersToNotify != null && usersToNotify.size() > 0) {
            // format message text
            Map<String, String> params = new HashMap<String, String>();

            // send email
            InternetAddress from = null;
            InternetAddress[] to = null;

            String topicFrom = "";
            if (topic.getPostedBy() != null) {
                topicFrom = topic.getPostedBy().getFirstName() + " " + topic.getPostedBy().getLastName();
            }

            to = new InternetAddress[usersToNotify.size()];
            int i = 0;
            for (User user : usersToNotify) {
                try {
                    InternetAddress toUserEmail = new InternetAddress(user.getEmail());
                    to[i] = toUserEmail;
                    i++;
                } catch (AddressException e) {
                    if (logger.isWarnEnabled())
                        logger.warn(
                                "notifyNewTopicToUsers(): New topic email notification 'toUserEmail' error : " + e);
                }
            }

            String fromUserEmail = "\"" + ServerConfigurationService.getString("ui.service", "Sakai")
                    + "\"<no-reply@" + ServerConfigurationService.getServerName() + ">";
            try {
                from = new InternetAddress(fromUserEmail);
            } catch (AddressException e1) {
                if (logger.isWarnEnabled())
                    logger.warn(
                            "notifyNewTopicToUsers(): New topic email notification 'fromUserEmail' error : " + e1);
            }

            try {
                if (site == null) {
                    site = siteService.getSite(category.getContext());
                }

                params.put("site.title", site.getTitle());

                String portalUrl = ServerConfigurationService.getPortalUrl();
                params.put("portal.url", portalUrl);

                //String currToolId = ToolManager.getCurrentPlacement().getId();
                //ToolConfiguration toolConfiguration = site.getTool(currToolId);

                ToolConfiguration toolConfiguration = site.getToolForCommonId("sakai.jforum.tool");

                String siteNavUrl = portalUrl + "/" + "site" + "/" + Web.escapeUrl(site.getId());

                if (toolConfiguration != null) {
                    siteNavUrl = siteNavUrl + "/" + "page" + "/" + toolConfiguration.getPageId();
                }

                params.put("site.url", siteNavUrl);
            } catch (IdUnusedException e) {
                if (logger.isWarnEnabled()) {
                    logger.warn(e.toString(), e);
                }
            }

            params.put("topic.title", topic.getTitle());

            params.put("topic.from", topicFrom);
            params.put("post.subject", post.getSubject());

            String postText = post.getText();

            // full URL's for smilies etc
            if (postText != null && postText.trim().length() > 0) {
                postText = XrefHelper.fullUrls(postText);
            }
            params.put("post.text", postText);

            String messageText = EmailUtil.getNewTopicMessageText(params);

            String subject = "[" + site.getTitle() + " - New Post] " + topic.getTitle();

            if (messageText != null) {
                if (logger.isDebugEnabled()) {
                    logger.debug("notifyNewTopicToUsers() - Email notification - to.length :" + to.length);
                }

                if ((post != null) && (post.hasAttachments())) {
                    //email with attachments
                    jforumEmailExecutorService.notifyUsers(from, to, subject, messageText, post.getAttachments());
                } else {
                    jforumEmailExecutorService.notifyUsers(from, to, subject, messageText);
                }
            } else {
                if ((post != null) && (post.hasAttachments())) {
                    //email with attachments
                    jforumEmailExecutorService.notifyUsers(from, to, subject, postText, post.getAttachments());
                } else {
                    jforumEmailExecutorService.notifyUsers(from, to, subject, postText);
                }
            }
        }
    }

    /**
     * Sends a "new post" notification message to all users watching the topic.
     * 
     * @param category
     */
    protected void notifyTopicReplyToUsers(Category category) {
        List<User> siteUsers = updateUsersInfo(category.getContext());

        Forum topicForum = category.getForums().get(0);
        Topic topic = category.getForums().get(0).getTopics().get(0);
        Post post = category.getForums().get(0).getTopics().get(0).getPosts().get(0);

        // remove the user who posted the message
        int posterId = post.getPostedBy().getId();

        List<User> usersToNotify = topicDao.notifyUsers(topic.getId(), posterId);

        // if forum has groups send to the members of the groups
        boolean sendToGroups = false;
        Site site = null;
        if ((topicForum.getAccessType() == Forum.ForumAccess.GROUPS.getAccessType())
                && (topicForum.getGroups() != null && topicForum.getGroups().size() > 0)) {
            sendToGroups = true;

            try {
                site = siteService.getSite(category.getContext());
            } catch (IdUnusedException e) {
                if (logger.isWarnEnabled()) {
                    logger.warn("notifyNewTopicToUsers: missing site: " + category.getContext());
                }
            }
        }

        Iterator<User> usersIterator = usersToNotify.iterator();
        while (usersIterator.hasNext()) {
            User usr = (User) usersIterator.next();

            if (usr == null) {
                usersIterator.remove();
                continue;
            }

            if (usr.getId() == posterId) {
                usersIterator.remove();
                continue;
            }

            // notify only to the users who belong to the group(s) associated with forum
            if (sendToGroups
                    && !jforumSecurityService.isJForumFacilitator(category.getContext(), usr.getSakaiUserId())) {
                boolean userInGroup = false;

                Collection sakaiSiteGroups = site.getGroupsWithMember(usr.getSakaiUserId());

                for (Iterator usrGrpIter = sakaiSiteGroups.iterator(); usrGrpIter.hasNext();) {
                    org.sakaiproject.site.api.Group grp = (org.sakaiproject.site.api.Group) usrGrpIter.next();
                    if (topicForum.getGroups().contains(grp.getId())) {
                        userInGroup = true;
                        break;
                    }
                }

                if (!userInGroup) {
                    usersIterator.remove();
                    continue;
                }
            }

            if ((usr.getEmail() == null || usr.getEmail().trim().length() == 0)
                    || (!jforumSecurityService.isUserActive(category.getContext(), usr.getSakaiUserId()))) {
                usersIterator.remove();
                continue;
            }

            try {
                new InternetAddress(usr.getEmail());
            } catch (AddressException e) {
                if (logger.isWarnEnabled())
                    logger.warn("notifyTopicReplyToUsers(...) : " + usr.getEmail()
                            + " is invalid. And exception is : " + e);
                usersIterator.remove();
                continue;
            }
        }

        if (usersToNotify != null && usersToNotify.size() > 0) {
            // format message text
            Map<String, String> params = new HashMap<String, String>();

            // send email
            InternetAddress from = null;
            InternetAddress[] to = null;

            String postFrom = "";
            if (post.getPostedBy() != null) {
                postFrom = post.getPostedBy().getFirstName() + " " + topic.getPostedBy().getLastName();
            }

            to = new InternetAddress[usersToNotify.size()];
            int i = 0;
            for (User user : usersToNotify) {
                try {
                    InternetAddress toUserEmail = new InternetAddress(user.getEmail());
                    to[i] = toUserEmail;
                    i++;
                } catch (AddressException e) {
                    if (logger.isWarnEnabled())
                        logger.warn(
                                "notifyTopicReplyToUsers(): Topic reply email notification 'toUserEmail' error : "
                                        + e);
                }
            }

            String fromUserEmail = "\"" + ServerConfigurationService.getString("ui.service", "Sakai")
                    + "\"<no-reply@" + ServerConfigurationService.getServerName() + ">";
            try {
                from = new InternetAddress(fromUserEmail);
            } catch (AddressException e1) {
                if (logger.isWarnEnabled())
                    logger.warn("notifyTopicReplyToUsers(): Topic reply email notification 'fromUserEmail' error : "
                            + e1);
            }

            try {
                if (site == null) {
                    site = siteService.getSite(category.getContext());
                }

                params.put("site.title", site.getTitle());

                String portalUrl = ServerConfigurationService.getPortalUrl();

                //String currToolId = ToolManager.getCurrentPlacement().getId();
                //ToolConfiguration toolConfiguration = site.getTool(currToolId);

                ToolConfiguration toolConfiguration = site.getToolForCommonId("sakai.jforum.tool");

                String siteNavUrl = portalUrl + "/" + "site" + "/" + Web.escapeUrl(site.getId());

                if (toolConfiguration != null) {
                    siteNavUrl = siteNavUrl + "/" + "page" + "/" + toolConfiguration.getPageId();
                }

                params.put("site.url", siteNavUrl);
            } catch (IdUnusedException e) {
                if (logger.isWarnEnabled()) {
                    logger.warn(e.toString(), e);
                }
            }

            params.put("topic.title", topic.getTitle());

            params.put("post.from", postFrom);
            params.put("post.subject", post.getSubject());

            String postText = post.getText();

            // full URL's for smilies etc
            if (postText != null && postText.trim().length() > 0) {
                postText = XrefHelper.fullUrls(postText);
            }
            params.put("post.text", postText);

            String messageText = EmailUtil.getNewReplyMessageText(params);

            String subject = "[" + site.getTitle() + " - New Post] " + topic.getTitle();

            if (messageText != null) {
                if (logger.isDebugEnabled()) {
                    logger.debug("notifyTopicReplyToUsers(): Email notification - to.length :" + to.length);
                }

                if ((post != null) && (post.hasAttachments())) {
                    //email with attachments
                    jforumEmailExecutorService.notifyUsers(from, to, subject, messageText, post.getAttachments());
                } else {
                    jforumEmailExecutorService.notifyUsers(from, to, subject, messageText);
                }
            } else {

                if ((post != null) && (post.hasAttachments())) {
                    //email with attachments
                    jforumEmailExecutorService.notifyUsers(from, to, subject, postText, post.getAttachments());
                } else {
                    jforumEmailExecutorService.notifyUsers(from, to, subject, postText);
                }
            }
        }
    }

    /**
     * remove entry from gradebook
     * 
     * @param grade
     */
    protected void removeEntryFromGradeBook(Grade grade) {
        //remove entry from gradebook
        if (grade != null) {

            JForumGBService jForumGBService = null;
            jForumGBService = (JForumGBService) ComponentManager.get("org.etudes.api.app.jforum.JForumGBService");

            if (jForumGBService == null)
                return;

            String gradebookUid = grade.getContext();
            if (jForumGBService.isExternalAssignmentDefined(gradebookUid,
                    JForumGradeService.JFORUM_GRADEBOOK_EXTERNAL_ID_CONCAT + String.valueOf(grade.getId()))) {
                jForumGBService.removeExternalAssessment(gradebookUid,
                        JForumGradeService.JFORUM_GRADEBOOK_EXTERNAL_ID_CONCAT + String.valueOf(grade.getId()));
            }
        }
    }

    /**
     * Update topic dates
     * 
     * @param topic      Topic
     */
    protected void updateDatesTXN(Topic topic) {
        String sql = "UPDATE jforum_topics SET start_date = ?, hide_until_open = ?, end_date = ?, allow_until_date = ? WHERE topic_id = ?";

        Object[] fields = new Object[5];
        int i = 0;

        //fields[i++] = ((topic.getAccessDates() == null) || (topic.getAccessDates().getOpenDate() == null)) ? null : new Timestamp(topic.getAccessDates().getOpenDate().getTime());
        //fields[i++] = ((topic.getAccessDates() == null) || (topic.getAccessDates().getDueDate() == null)) ? null : new Timestamp(topic.getAccessDates().getDueDate().getTime());
        //fields[i++] = ((topic.getAccessDates() != null) && (topic.getAccessDates().isLocked()))? 1 : 0;

        if (topic.getAccessDates() != null) {
            if (topic.getAccessDates().getOpenDate() == null) {
                fields[i++] = null;
                fields[i++] = 0;
            } else {
                fields[i++] = new Timestamp(topic.getAccessDates().getOpenDate().getTime());
                fields[i++] = topic.getAccessDates().isHideUntilOpen() ? 1 : 0;
            }

            if (topic.getAccessDates().getDueDate() == null) {
                fields[i++] = null;
            } else {
                fields[i++] = new Timestamp(topic.getAccessDates().getDueDate().getTime());
            }

            if (topic.getAccessDates().getAllowUntilDate() == null) {
                fields[i++] = null;
            } else {
                fields[i++] = new Timestamp(topic.getAccessDates().getAllowUntilDate().getTime());
            }
        } else {
            fields[i++] = null;
            fields[i++] = 0;
            fields[i++] = null;
            fields[i++] = null;
        }

        fields[i++] = topic.getId();

        if (!this.sqlService.dbWrite(sql.toString(), fields)) {
            throw new RuntimeException("updateDatesTXN: db write failed");
        }
    }

    /**
     * Update existing users info in jforum if changed
     * 
     * @param currentSiteUsers   List of current site users in jforum
     */
    protected void updateExistingUsersInfo(List<User> currentSiteUsers) {
        for (User user : currentSiteUsers) {
            updateJFUser(user);
        }
    }

    /**
     * Sends topic grades to gradebook
     * 
     * @param topic   Topic
     */
    protected void updateGradeBook(Topic topic) {
        Site site = null;

        Forum forum = topic.getForum();
        Category category = forum.getCategory();

        try {
            site = siteService.getSite(category.getContext());
        } catch (IdUnusedException e) {
            if (logger.isWarnEnabled()) {
                logger.warn("updateGradeBook: missing site: " + category.getContext());
            }
        }

        Grade grade = topic.getGrade();

        //check for gradebook tool in the site
        boolean gradebookAvailable = false;

        try {
            if (site == null) {
                site = siteService.getSite(category.getContext());
            }

            String gradebookToolId = ServerConfigurationService.getString(JForumGradeService.GRADEBOOK_TOOL_ID);

            if ((gradebookToolId == null) || (gradebookToolId.trim().length() == 0)) {
                gradebookToolId = "sakai.gradebook.tool";
            }

            if (site.getToolForCommonId(gradebookToolId) != null) {
                gradebookAvailable = true;
            }
        } catch (IdUnusedException e) {
            if (logger.isWarnEnabled()) {
                logger.warn("updateGradeBook: missing site: " + category.getContext());
            }
        }

        if (jforumGBService.isGradebookDefined(grade.getContext()) && gradebookAvailable) {/*
                                                                                             Date now = new Date();
                                                                                                 
                                                                                             Date endDate = null;
                                                                                             if (topic.getAccessDates() != null)
                                                                                             {
                                                                                                endDate = topic.getAccessDates().getDueDate();
                                                                                             }
                                                                                                 
                                                                                             if (endDate == null)
                                                                                             {
                                                                                                if (forum.getAccessDates() != null)
                                                                                                {
                                                                                                   endDate = forum.getAccessDates().getDueDate();
                                                                                                }
                                                                                                    
                                                                                                if (endDate == null)
                                                                                                {
                                                                                                   if (category.getAccessDates() != null)
                                                                                                   {
                                                                                             endDate = category.getAccessDates().getDueDate();
                                                                                                   }
                                                                                                }
                                                                                             }
                                                                                             // new grade should not be in gradebook, sanity check
                                                                                             boolean entryExisInGradebook = false;
                                                                                                 
                                                                                             if (jforumGBService.isExternalAssignmentDefined(grade.getContext(), JForumGradeService.JFORUM_GRADEBOOK_EXTERNAL_ID_CONCAT + String.valueOf(grade.getId())))
                                                                                             {
                                                                                                entryExisInGradebook = true;
                                                                                             }
                                                                                                 
                                                                                             //add or update to gradebook
                                                                                             String url = null;
                                                                                                 
                                                                                             if (entryExisInGradebook)
                                                                                             {
                                                                                                // open date and hide until
                                                                                                if (topic.getAccessDates() != null && topic.getAccessDates().getOpenDate() != null && topic.getAccessDates().getOpenDate().after(now) && topic.getAccessDates().isHideUntilOpen())
                                                                                                {
                                                                                                   jforumGBService.removeExternalAssessment(grade.getContext(), JForumGradeService.JFORUM_GRADEBOOK_EXTERNAL_ID_CONCAT + String.valueOf(grade.getId()));
                                                                                                   //return;
                                                                                                }
                                                                                                    
                                                                                                this.jforumGradeService.updateGradebook(grade, topic);
                                                                                                    
                                                                                                remove entry in the gradebook and add again if there is no entry with the same name in the gradebook.
                                                                                                Then publish the scores from grading page.
                                                                                                    
                                                                                                jforumGBService.removeExternalAssessment(grade.getContext(), JForumGradeService.JFORUM_GRADEBOOK_EXTERNAL_ID_CONCAT + String.valueOf(grade.getId()));
                                                                                                    
                                                                                                    
                                                                                                if (!jforumGBService.isAssignmentDefined(grade.getContext(), topic.getTitle()))
                                                                                                {
                                                                                                   if (!jforumGBService.addExternalAssessment(grade.getContext(), JForumGradeService.JFORUM_GRADEBOOK_EXTERNAL_ID_CONCAT + String.valueOf(grade.getId()), url,  topic.getTitle(), 
                                                                                                JForumUtil.toDoubleScore(grade.getPoints()), endDate, JForumGradeService.GRADE_SENDTOGRADEBOOK_DESCRIPTION))
                                                                                                   {
                                                                                             // update isAddToGradeBook of grade
                                                                                             grade.setAddToGradeBook(false);
                                                                                             jforumGradeService.modifyTopicGrade(grade);
                                                                                                   }
                                                                                                   else
                                                                                                   {
                                                                                             this.jforumGradeService.updateGradebook(grade, topic);
                                                                                                   }
                                                                                                }
                                                                                                else
                                                                                                {
                                                                                                   // update isAddToGradeBook of grade
                                                                                                   grade.setAddToGradeBook(false);
                                                                                                   jforumGradeService.modifyTopicGrade(grade);
                                                                                                }
                                                                                             }
                                                                                             else
                                                                                             {
                                                                                                // open date and hide until 
                                                                                                if (topic.getAccessDates() != null && topic.getAccessDates().getOpenDate() != null && topic.getAccessDates().getOpenDate().after(now) && topic.getAccessDates().isHideUntilOpen())
                                                                                                {
                                                                                                   return;
                                                                                                }
                                                                                                    
                                                                                                if (!jforumGBService.isAssignmentDefined(grade.getContext(), topic.getTitle()))
                                                                                                {
                                                                                                   if (!jforumGBService.addExternalAssessment(grade.getContext(), JForumGradeService.JFORUM_GRADEBOOK_EXTERNAL_ID_CONCAT + String.valueOf(grade.getId()), url, topic.getTitle(), 
                                                                                                JForumUtil.toDoubleScore(grade.getPoints()), endDate, JForumGradeService.GRADE_SENDTOGRADEBOOK_DESCRIPTION))
                                                                                                   {
                                                                                             // update isAddToGradeBook of grade
                                                                                             grade.setAddToGradeBook(false);
                                                                                             jforumGradeService.modifyTopicGrade(grade);
                                                                                                   }
                                                                                                }
                                                                                                else
                                                                                                {
                                                                                                   // update isAddToGradeBook of grade
                                                                                                   grade.setAddToGradeBook(false);
                                                                                                   jforumGradeService.modifyTopicGrade(grade);
                                                                                                }      
                                                                                             }
                                                                                           */
            jforumGradeService.updateGradebook(grade, topic);
        } else {
            if (grade != null) {
                // update isAddToGradeBook of grade
                grade.setAddToGradeBook(false);
                jforumGradeService.modifyTopicGrade(grade);
            }
        }
    }

    /**
     * Update user info
     * 
     * @param jfuser   Jforum user
     */
    protected void updateJFUser(User jfuser) {
        try {
            org.sakaiproject.user.api.User sakUser = UserDirectoryService.getUser(jfuser.getSakaiUserId());

            if (sakUser != null) {
                String sakUsrEmail = sakUser.getEmail();
                String sakUsrFname = sakUser.getFirstName();
                String sakUsrLname = sakUser.getLastName();

                String jforumUsrEmail = jfuser.getEmail();
                String jforumUsrFname = jfuser.getFirstName();
                String jforumUsrLname = jfuser.getLastName();

                boolean changed = false, fnameblank = false;
                // if sakai Eid is changed
                /*if (!sakUser.getEid().equalsIgnoreCase(jfuser.getUsername()))
                {
                   jfuser.setUsername(sakUser.getEid());
                   changed = true;
                }*/

                /*
                 * if sakai user first name and last name are blank then change the jforum user last name as Guest
                 */
                // first name
                if (sakUsrFname != null && sakUsrFname.trim().length() > 0) {
                    if (jforumUsrFname != null) {
                        // compare first names
                        if (!jforumUsrFname.equals(sakUsrFname)) {
                            jfuser.setFirstName(sakUsrFname);
                            changed = true;
                        }
                    } else {
                        jfuser.setFirstName(sakUsrFname);
                        changed = true;
                    }
                } else {
                    fnameblank = true;
                    jfuser.setFirstName("");
                    changed = true;
                }

                // last name
                if (sakUsrLname != null && sakUsrLname.trim().length() > 0) {
                    if (jforumUsrLname != null) {
                        // compare last names
                        if (!jforumUsrLname.equals(sakUsrLname)) {
                            jfuser.setLastName(sakUsrLname);
                            changed = true;
                        }
                    } else {
                        jfuser.setLastName(sakUsrLname);
                        changed = true;
                    }
                } else {
                    if (fnameblank) {
                        jfuser.setLastName("Guest User");
                    } else {
                        jfuser.setLastName("");
                    }
                    changed = true;
                }

                // email
                if (sakUsrEmail != null && sakUsrEmail.trim().length() > 0) {
                    if (jforumUsrEmail != null) {
                        if (!jforumUsrEmail.equals(sakUsrEmail)) {
                            jfuser.setEmail(sakUsrEmail);
                            changed = true;
                        }
                    } else {
                        jfuser.setEmail(sakUsrEmail);
                        changed = true;
                    }
                } else {
                    if (jforumUsrEmail != null && jforumUsrEmail.trim().length() != 0) {
                        jfuser.setEmail("");
                        changed = true;
                    }
                }

                if (changed) {
                    jforumUserService.modifyUserSakaiInfo(jfuser);
                }
            }
        } catch (UserNotDefinedException e) {
            // if (logger.isWarnEnabled()) logger.warn(e, e);
        }
    }

    /**
     * Update users info in jforum if sakai user information is modified
     * 
     * @param siteId   Site id
     * 
     * @return   List of users
     */
    protected List<User> updateUsersInfo(String siteId) {
        Site site = null;

        try {
            site = siteService.getSite(siteId);
        } catch (IdUnusedException e) {
            if (logger.isWarnEnabled()) {
                logger.warn("updateUsersInfo: missing site: " + siteId);
            }
        }

        if (site == null) {
            return null;
        }
        // update users info in the jforum
        Set<String> siteUsers = site.getUsers();

        //remove users that are deleted in sakai
        Iterator<String> sakUserIterator = siteUsers.iterator();
        while (sakUserIterator.hasNext()) {
            String userId = (String) sakUserIterator.next();
            try {
                UserDirectoryService.getUser(userId);
            } catch (UserNotDefinedException e) {
                sakUserIterator.remove();
            }
        }

        List<String> allsiteusers = new ArrayList<String>();

        allsiteusers.addAll(siteUsers);
        List<String> copyallsiteusers = new ArrayList<String>();
        Iterator<String> copyiter = allsiteusers.iterator();
        while (copyiter.hasNext()) {
            String copyname = (copyiter.next()).toLowerCase();
            copyallsiteusers.add(copyname);
        }

        /* for existing site users */
        List<User> exissiteusers = new ArrayList<User>();

        List<User> users = jforumUserService.getSiteUsers(siteId);

        //find new users
        Iterator<User> iter = users.iterator();
        while (iter.hasNext()) {
            User checkusr = iter.next();

            if (allsiteusers.contains(checkusr.getSakaiUserId().toLowerCase())) {
                /* remove from allsiteusers as user is existing */
                allsiteusers.remove(checkusr.getSakaiUserId().toLowerCase());

                /* add existing site users */
                exissiteusers.add(checkusr);
            }
        }

        //Don't create new users here
        //List newUsers = createNewUsers(allsiteusers);

        updateExistingUsersInfo(exissiteusers);

        //users.addAll(newUsers);

        /*dropped students remove from users */
        User adminuser = dropUsers(users, copyallsiteusers);

        if (adminuser != null)
            users.remove(adminuser);

        //Collections.sort(users,new UserOrderComparator());

        return users;
    }
}