Example #1
0
def view_forum(forum_id, slug=None):
    page = request.args.get('page', 1, type=int)

    if current_user.is_authenticated():
        forum = Forum.query.\
            filter(Forum.id == forum_id).\
            options(db.joinedload("category")).\
            outerjoin(ForumsRead,
                      db.and_(ForumsRead.forum_id == Forum.id,
                              ForumsRead.user_id == current_user.id)).\
            add_entity(ForumsRead).\
            first_or_404()

        topics = Topic.query.filter_by(forum_id=forum[0].id).\
            filter(Post.topic_id == Topic.id).\
            outerjoin(TopicsRead,
                      db.and_(TopicsRead.topic_id == Topic.id,
                              TopicsRead.user_id == current_user.id)).\
            add_entity(TopicsRead).\
            order_by(Post.id.desc()).\
            paginate(page, current_app.config['TOPICS_PER_PAGE'], True)
    else:
        forum = Forum.query.filter(Forum.id == forum_id).first_or_404()
        forum = (forum, None)

        topics = Topic.query.filter_by(forum_id=forum[0].id).\
            filter(Post.topic_id == Topic.id).\
            order_by(Post.id.desc()).\
            paginate(page, current_app.config['TOPICS_PER_PAGE'], True, True)

    return render_template("forum/forum.html", forum=forum, topics=topics)
Example #2
0
    def update_read(self, user, forumsread, topicsread):
        """Updates the ForumsRead status for the user. In order to work
        correctly, be sure that `topicsread is **not** `None`.

        :param user: The user for whom we should check if he has read the
                     forum.

        :param forumsread: The forumsread object. It is needed to check if
                           if the forum is unread. If `forumsread` is `None`
                           and the forum is unread, it will create a new entry
                           in the `ForumsRead` relation, else (and the forum
                           is still unread) we are just going to update the
                           entry in the `ForumsRead` relation.

        :param topicsread: The topicsread object is used in combination
                           with the forumsread object to check if the
                           forumsread relation should be updated and
                           therefore is unread.
        """
        if not user.is_authenticated() or topicsread is None:
            return False

        # fetch the unread posts in the forum
        unread_count = Topic.query.\
            outerjoin(TopicsRead,
                      db.and_(TopicsRead.topic_id == Topic.id,
                              TopicsRead.user_id == user.id)).\
            outerjoin(ForumsRead,
                      db.and_(ForumsRead.forum_id == Topic.forum_id,
                              ForumsRead.user_id == user.id)).\
            filter(Topic.forum_id == self.id,
                   db.or_(TopicsRead.last_read == None,
                          TopicsRead.last_read < Topic.last_updated)).\
            count()

        # No unread topics available - trying to mark the forum as read
        if unread_count == 0:

            if forumsread and forumsread.last_read > topicsread.last_read:
                return False

            # ForumRead Entry exists - Updating it because a new topic/post
            # has been submitted and has read everything (obviously, else the
            # unread_count would be useless).
            elif forumsread:
                forumsread.last_read = datetime.utcnow()
                forumsread.save()
                return True

            # No ForumRead Entry existing - creating one.
            forumsread = ForumsRead()
            forumsread.user_id = user.id
            forumsread.forum_id = self.id
            forumsread.last_read = datetime.utcnow()
            forumsread.save()
            return True

        # Nothing updated, because there are still more than 0 unread topics
        return False
Example #3
0
    def get_all(cls, user):
        """Get all categories with all associated forums.
        It returns a list with tuples. Those tuples are containing the category
        and their associated forums (whose are stored in a list).

        For example::

            [(<Category 1>, [(<Forum 2>, <ForumsRead>), (<Forum 1>, None)]),
             (<Category 2>, [(<Forum 3>, None), (<Forum 4>, None)])]

        :param user: The user object is needed to check if we also need their
                     forumsread object.
        """
        if user.is_authenticated():
            forums = cls.query.\
                join(Forum, cls.id == Forum.category_id).\
                outerjoin(ForumsRead,
                          db.and_(ForumsRead.forum_id == Forum.id,
                                  ForumsRead.user_id == user.id)).\
                add_entity(Forum).\
                add_entity(ForumsRead).\
                order_by(Category.position, Category.id,  Forum.position).\
                all()
        else:
            # Get all the forums
            forums = cls.query.\
                join(Forum, cls.id == Forum.category_id).\
                add_entity(Forum).\
                order_by(Category.position, Category.id, Forum.position).\
                all()

        return get_categories_and_forums(forums, user)
Example #4
0
    def get_topics(cls, forum_id, user, page=1, per_page=20):
        """Get the topics for the forum. If the user is logged in,
        it will perform an outerjoin for the topics with the topicsread and
        forumsread relation to check if it is read or unread.

        :param forum_id: The forum id

        :param user: The user object

        :param page: The page whom should be loaded

        :param per_page: How many topics per page should be shown
        """
        if user.is_authenticated():
            topics = Topic.query.filter_by(forum_id=forum_id).\
                outerjoin(TopicsRead,
                          db.and_(TopicsRead.topic_id == Topic.id,
                                  TopicsRead.user_id == user.id)).\
                add_entity(TopicsRead).\
                order_by(Topic.important.desc(), Topic.last_updated.desc()).\
                paginate(page, per_page, True)
        else:
            topics = Topic.query.filter_by(forum_id=forum_id).\
                order_by(Topic.important.desc(), Topic.last_updated.desc()).\
                paginate(page, per_page, True)

            topics.items = [(topic, None) for topic in topics.items]

        return topics
Example #5
0
    def post(self, slug=None, plugin=None):
        form, old_settings, plugin_obj, active_nav = \
            self._determine_active_settings(slug, plugin)
        all_groups = SettingsGroup.query.all()
        all_plugins = PluginRegistry.query.filter(db.and_(
            PluginRegistry.values != None,
            PluginRegistry.enabled == True
        )).all()

        if form.validate_on_submit():
            new_settings = populate_settings_dict(form, old_settings)

            if plugin_obj is not None:
                plugin_obj.update_settings(new_settings)
            else:
                Setting.update(settings=new_settings, app=current_app)

            flash(_("Settings saved."), "success")

        return render_template(
            "management/settings.html",
            form=form,
            all_groups=all_groups,
            all_plugins=all_plugins,
            active_nav=active_nav
        )
Example #6
0
def view_category(category_id, slug=None):
    if current_user.is_authenticated():
        forum_query = Category.query.\
            filter(Category.id == category_id).\
            join(Forum, Category.id == Forum.category_id).\
            outerjoin(ForumsRead,
                      db.and_(ForumsRead.forum_id == Forum.id,
                              ForumsRead.user_id == 1)).\
            add_entity(Forum).\
            add_entity(ForumsRead).\
            order_by(Forum.position).\
            all()
    else:
        # we do not need to join the ForumsRead because the user isn't
        # signed in
        forum_query = Category.query.\
            filter(Category.id == category_id).\
            join(Forum, Category.id == Forum.category_id).\
            add_entity(Forum).\
            order_by(Forum.position).\
            all()

        forum_query = [(category, forum, None)
                       for category, forum in forum_query]

    category = get_forums(forum_query)
    return render_template("forum/category.html", categories=category)
Example #7
0
    def get(self, user_id):
        user = User.query.filter_by(id=user_id).first_or_404()
        form = self.form(user)
        member_group = db.and_(
            * [
                db.not_(getattr(Group, p))
                for p in ['admin', 'mod', 'super_mod', 'banned', 'guest']
            ]
        )

        filt = db.or_(
            Group.id.in_(g.id for g in current_user.groups), member_group
        )

        if Permission(IsAtleastSuperModerator, identity=current_user):
            filt = db.or_(filt, Group.mod)

        if Permission(IsAdmin, identity=current_user):
            filt = db.or_(filt, Group.admin, Group.super_mod)

        if Permission(CanBanUser, identity=current_user):
            filt = db.or_(filt, Group.banned)

        group_query = Group.query.filter(filt)

        form.primary_group.query = group_query
        form.secondary_groups.query = group_query

        return render_template(
            'management/user_form.html', form=form, title=_('Edit User')
        )
Example #8
0
    def validate_username(self, field):
        if hasattr(self, "user"):
            user = User.query.filter(db.and_(User.username.like(field.data), db.not_(User.id == self.user.id))).first()
        else:
            user = User.query.filter(User.username.like(field.data)).first()

        if user:
            raise ValidationError(_("This Username is already taken."))
Example #9
0
    def validate_name(self, field):
        if hasattr(self, "group"):
            group = Group.query.filter(db.and_(Group.name.like(field.data), db.not_(Group.id == self.group.id))).first()
        else:
            group = Group.query.filter(Group.name.like(field.data)).first()

        if group:
            raise ValidationError(_("This Group name is already taken."))
Example #10
0
    def validate_guest(self, field):
        if hasattr(self, "group"):
            group = Group.query.filter(db.and_(Group.guest, db.not_(Group.id == self.group.id))).count()
        else:
            group = Group.query.filter_by(guest=True).count()

        if field.data and group > 0:
            raise ValidationError(_("There is already a Guest group."))
Example #11
0
    def get_all(cls, user):
        """Get all categories with all associated forums.
        It returns a list with tuples. Those tuples are containing the category
        and their associated forums (whose are stored in a list).

        For example::

            [(<Category 1>, [(<Forum 2>, <ForumsRead>), (<Forum 1>, None)]),
             (<Category 2>, [(<Forum 3>, None), (<Forum 4>, None)])]

        :param user: The user object is needed to check if we also need their
                     forumsread object.
        """
        # import Group model locally to avoid cicular imports
        from flaskbb.user.models import Group
        if user.is_authenticated():
            # get list of user group ids
            user_groups = [gr.id for gr in user.groups]
            # filter forums by user groups
            user_forums = Forum.query.filter(Forum.groups.any(
                Group.id.in_(user_groups))
            ).subquery()
            forum_alias = aliased(Forum, user_forums)
            # get all
            forums = cls.query.join(
                forum_alias,
                cls.id == forum_alias.category_id
            ).outerjoin(
                ForumsRead,
                db.and_(
                    ForumsRead.forum_id == forum_alias.id,
                    ForumsRead.user_id == user.id
                )
            ).add_entity(
                forum_alias
            ).add_entity(
                ForumsRead
            ).order_by(
                Category.position, Category.id,  forum_alias.position
            ).all()
        else:
            guest_group = Group.get_guest_group()
            # filter forums by guest groups
            guest_forums = Forum.query.filter(
                Forum.groups.any(Group.id==guest_group.id)
            ).subquery()
            forum_alias = aliased(Forum, guest_forums)
            forums = cls.query.join(
                forum_alias,
                cls.id == forum_alias.category_id
            ).add_entity(
                forum_alias
            ).order_by(
                Category.position, Category.id, forum_alias.position
            ).all()

        return get_categories_and_forums(forums, user)
Example #12
0
def view_forum(forum_id):
    page = request.args.get('page', 1, type=int)

    if current_user.is_authenticated():
        forum = Forum.query.\
            filter(Forum.id == forum_id).\
            outerjoin(ForumsRead,
                      db.and_(ForumsRead.forum_id == Forum.id,
                              ForumsRead.user_id == current_user.id)).\
            add_entity(ForumsRead).\
            first_or_404()

        subforums = Forum.query.\
            filter(Forum.parent_id == forum[0].id).\
            outerjoin(ForumsRead,
                      db.and_(ForumsRead.forum_id == Forum.id,
                              ForumsRead.user_id == current_user.id)).\
            add_entity(ForumsRead).\
            all()

        topics = Topic.query.filter_by(forum_id=forum[0].id).\
            filter(Post.topic_id == Topic.id).\
            outerjoin(TopicsRead,
                      db.and_(TopicsRead.topic_id == Topic.id,
                              TopicsRead.user_id == current_user.id)).\
            add_entity(TopicsRead).\
            order_by(Post.id.desc()).\
            paginate(page, current_app.config['TOPICS_PER_PAGE'], True)
    else:
        forum = Forum.query.filter(Forum.id == forum_id).first_or_404()
        forum = (forum, None)

        subforums = Forum.query.filter(Forum.parent_id == forum[0].id).all()
        # This isn't really nice imho, but "add_entity" (see above)
        # makes a list with tuples
        subforums = [(item, None) for item in subforums]

        topics = Topic.query.filter_by(forum_id=forum[0].id).\
            filter(Post.topic_id == Topic.id).\
            order_by(Post.id.desc()).\
            paginate(page, current_app.config['TOPICS_PER_PAGE'], True, True)

    return render_template("forum/forum.html",
                           forum=forum, topics=topics, subforums=subforums)
Example #13
0
def topictracker():
    page = request.args.get("page", 1, type=int)
    topics = current_user.tracked_topics.\
        outerjoin(TopicsRead,
                  db.and_(TopicsRead.topic_id == Topic.id,
                          TopicsRead.user_id == current_user.id)).\
        add_entity(TopicsRead).\
        order_by(Post.id.desc()).\
        paginate(page, flaskbb_config['TOPICS_PER_PAGE'], True)

    return render_template("forum/topictracker.html", topics=topics)
Example #14
0
    def validate_banned(self, field):
        if hasattr(self, "group"):
            group = Group.query.filter(
                db.and_(Group.banned == True,
                        db.not_(Group.id == self.group.id)
                        )
            ).count()
        else:
            group = Group.query.filter_by(banned=True).count()

        if field.data and group > 0:
            raise ValidationError("There is already a Banned group")
Example #15
0
    def validate_email(self, field):
        if hasattr(self, "user"):
            user = User.query.filter(
                db.and_(User.email.like(field.data),
                        db.not_(User.id == self.user.id)
                        )
            ).first()
        else:
            user = User.query.filter(User.email.like(field.data)).first()

        if user:
            raise ValidationError("This email is taken")
Example #16
0
    def get_forums(cls, category_id, user):
        """Get the forums for the category.
        It returns a tuple with the category and the forums with their
        forumsread object are stored in a list.

        A return value can look like this for a category with two forums::

            (<Category 1>, [(<Forum 1>, None), (<Forum 2>, None)])

        :param category_id: The category id
        :param user: The user object is needed to check if we also need their
                     forumsread object.
        """
        from flaskbb.user.models import Group
        if user.is_authenticated:
            # get list of user group ids
            user_groups = [gr.id for gr in user.groups]
            # filter forums by user groups
            user_forums = Forum.query.\
                filter(Forum.groups.any(Group.id.in_(user_groups))).\
                subquery()

            forum_alias = aliased(Forum, user_forums)
            forums = cls.query.\
                filter(cls.id == category_id).\
                join(forum_alias, cls.id == forum_alias.category_id).\
                outerjoin(ForumsRead,
                          db.and_(ForumsRead.forum_id == forum_alias.id,
                                  ForumsRead.user_id == user.id)).\
                add_entity(forum_alias).\
                add_entity(ForumsRead).\
                order_by(forum_alias.position).\
                all()
        else:
            guest_group = Group.get_guest_group()
            # filter forums by guest groups
            guest_forums = Forum.query.\
                filter(Forum.groups.any(Group.id == guest_group.id)).\
                subquery()

            forum_alias = aliased(Forum, guest_forums)
            forums = cls.query.\
                filter(cls.id == category_id).\
                join(forum_alias, cls.id == forum_alias.category_id).\
                add_entity(forum_alias).\
                order_by(forum_alias.position).\
                all()

        if not forums:
            abort(404)

        return get_forums(forums, user)
Example #17
0
    def validate_email(self, field):
        if hasattr(self, "user"):
            user = User.query.filter(
                db.and_(
                    User.email.like(field.data),
                    db.not_(User.id == self.user.id)
                )
            ).first()
        else:
            user = User.query.filter(User.email.like(field.data)).first()

        if user:
            raise ValidationError(_("This E-Mail Address is already taken."))
Example #18
0
    def validate_name(self, field):
        if hasattr(self, "group"):
            group = Group.query.filter(
                db.and_(
                    Group.name.like(field.data),
                    db.not_(Group.id == self.group.id)
                )
            ).first()
        else:
            group = Group.query.filter(Group.name.like(field.data)).first()

        if group:
            raise ValidationError(_("This Group name is already taken."))
Example #19
0
    def validate_banned(self, field):
        if hasattr(self, "group"):
            group = Group.query.filter(
                db.and_(
                    Group.banned,
                    db.not_(Group.id == self.group.id)
                )
            ).count()
        else:
            group = Group.query.filter_by(banned=True).count()

        if field.data and group > 0:
            raise ValidationError(_("There is already a Banned group."))
Example #20
0
def index():
    # Get the categories and forums
    if current_user.is_authenticated():
        forum_query = Category.query.\
            join(Forum, Category.id == Forum.category_id).\
            outerjoin(ForumsRead,
                      db.and_(ForumsRead.forum_id == Forum.id,
                              ForumsRead.user_id == current_user.id)).\
            add_entity(Forum).\
            add_entity(ForumsRead).\
            order_by(Category.id, Category.position, Forum.position).\
            all()
    else:
        # we do not need to join the ForumsRead because the user isn't
        # signed in
        forum_query = Category.query.\
            join(Forum, Category.id == Forum.category_id).\
            add_entity(Forum).\
            order_by(Category.id, Category.position, Forum.position).\
            all()

        forum_query = [(category, forum, None)
                       for category, forum in forum_query]

    categories = get_forums(forum_query)

    # Fetch a few stats about the forum
    user_count = User.query.count()
    topic_count = Topic.query.count()
    post_count = Post.query.count()
    newest_user = User.query.order_by(User.id.desc()).first()

    # Check if we use redis or not
    if not current_app.config["REDIS_ENABLED"]:
        online_users = User.query.filter(User.lastseen >= time_diff()).count()

        # Because we do not have server side sessions, we cannot check if there
        # are online guests
        online_guests = None
    else:
        online_users = len(get_online_users())
        online_guests = len(get_online_users(guest=True))

    return render_template("forum/index.html",
                           categories=categories,
                           user_count=user_count,
                           topic_count=topic_count,
                           post_count=post_count,
                           newest_user=newest_user,
                           online_users=online_users,
                           online_guests=online_guests)
Example #21
0
    def get_all(cls, user):
        """Get all categories with all associated forums.
        It returns a list with tuples. Those tuples are containing the category
        and their associated forums (whose are stored in a list).

        For example::

            [(<Category 1>, [(<Forum 2>, <ForumsRead>), (<Forum 1>, None)]),
             (<Category 2>, [(<Forum 3>, None), (<Forum 4>, None)])]

        :param user: The user object is needed to check if we also need their
                     forumsread object.
        """
        # import Group model locally to avoid cicular imports
        from flaskbb.user.models import Group
        if user.is_authenticated:
            # get list of user group ids
            user_groups = [gr.id for gr in user.groups]
            # filter forums by user groups
            user_forums = Forum.query.\
                filter(Forum.groups.any(Group.id.in_(user_groups))).\
                subquery()

            forum_alias = aliased(Forum, user_forums)
            # get all
            forums = cls.query.\
                join(forum_alias, cls.id == forum_alias.category_id).\
                outerjoin(ForumsRead,
                          db.and_(ForumsRead.forum_id == forum_alias.id,
                                  ForumsRead.user_id == user.id)).\
                add_entity(forum_alias).\
                add_entity(ForumsRead).\
                order_by(Category.position, Category.id,
                         forum_alias.position).\
                all()
        else:
            guest_group = Group.get_guest_group()
            # filter forums by guest groups
            guest_forums = Forum.query.\
                filter(Forum.groups.any(Group.id == guest_group.id)).\
                subquery()

            forum_alias = aliased(Forum, guest_forums)
            forums = cls.query.\
                join(forum_alias, cls.id == forum_alias.category_id).\
                add_entity(forum_alias).\
                order_by(Category.position, Category.id,
                         forum_alias.position).\
                all()

        return get_categories_and_forums(forums, user)
Example #22
0
    def validate_guest(self, field):
        if hasattr(self, "group"):
            group = Group.query.filter(
                db.and_(
                    Group.guest,
                    db.not_(Group.id == self.group.id)
                )
            ).count()
        else:
            group = Group.query.filter_by(guest=True).count()

        if field.data and group > 0:
            raise ValidationError(_("There is already a group of type "
                                    "'Guest'."))
Example #23
0
    def validate_username(self, field):
        if hasattr(self, "user"):
            user = User.query.filter(
                db.and_(
                    User.username.like(field.data.lower()),
                    db.not_(User.id == self.user.id)
                )
            ).first()
        else:
            user = User.query.filter(
                User.username.like(field.data.lower())
            ).first()

        if user:
            raise ValidationError(_("This username is already taken."))
Example #24
0
    def validate_email(self, field):
        if hasattr(self, "user"):
            user = User.query.filter(
                db.and_(
                    User.email.like(field.data.lower()),
                    db.not_(User.id == self.user.id)
                )
            ).first()
        else:
            user = User.query.filter(
                User.email.like(field.data.lower())
            ).first()

        if user:
            raise ValidationError(_("This email address is already taken."))
Example #25
0
    def get(self):
        page = request.args.get("page", 1, type=int)
        topics = real(current_user).tracked_topics.\
            outerjoin(
                TopicsRead,
                db.and_(
                    TopicsRead.topic_id == Topic.id,
                    TopicsRead.user_id == real(current_user).id
                )).\
            outerjoin(Post, Topic.last_post_id == Post.id).\
            outerjoin(Forum, Topic.forum_id == Forum.id).\
            outerjoin(
                ForumsRead,
                db.and_(
                    ForumsRead.forum_id == Forum.id,
                    ForumsRead.user_id == real(current_user).id
                )).\
            add_entity(Post).\
            add_entity(TopicsRead).\
            add_entity(ForumsRead).\
            order_by(Topic.last_updated.desc()).\
            paginate(page, flaskbb_config["TOPICS_PER_PAGE"], True)

        return render_template("forum/topictracker.html", topics=topics)
Example #26
0
    def get(self):
        page = request.args.get("page", 1, type=int)
        topics = real(current_user).tracked_topics.\
            outerjoin(
                TopicsRead,
                db.and_(
                    TopicsRead.topic_id == Topic.id,
                    TopicsRead.user_id == real(current_user).id
                )).\
            outerjoin(Post, Topic.last_post_id == Post.id).\
            outerjoin(Forum, Topic.forum_id == Forum.id).\
            outerjoin(
                ForumsRead,
                db.and_(
                    ForumsRead.forum_id == Forum.id,
                    ForumsRead.user_id == real(current_user).id
                )).\
            add_entity(Post).\
            add_entity(TopicsRead).\
            add_entity(ForumsRead).\
            order_by(Topic.last_updated.desc()).\
            paginate(page, flaskbb_config["TOPICS_PER_PAGE"], True)

        return render_template("forum/topictracker.html", topics=topics)
    def get(self, slug=None, plugin=None):
        form, old_settings, plugin_obj, active_nav = \
            self._determine_active_settings(slug, plugin)

        # get all groups and plugins - used to build the navigation
        all_groups = SettingsGroup.query.all()
        all_plugins = PluginRegistry.query.filter(
            db.and_(PluginRegistry.values != None,
                    PluginRegistry.enabled == True)).all()
        form = populate_settings_form(form, old_settings)

        return render_template("management/settings.html",
                               form=form,
                               all_groups=all_groups,
                               all_plugins=all_plugins,
                               active_nav=active_nav)
Example #28
0
def edit_user(user_id):
    user = User.query.filter_by(id=user_id).first_or_404()

    if not Permission(CanEditUser, identity=current_user):
        flash(_("You are not allowed to edit this user."), "danger")
        return redirect(url_for("management.users"))

    member_group = db.and_(*[
        db.not_(getattr(Group, p))
        for p in ['admin', 'mod', 'super_mod', 'banned', 'guest']
    ])

    filt = db.or_(Group.id.in_(g.id for g in current_user.groups),
                  member_group)

    if Permission(IsAtleastSuperModerator, identity=current_user):
        filt = db.or_(filt, Group.mod)

    if Permission(IsAdmin, identity=current_user):
        filt = db.or_(filt, Group.admin, Group.super_mod)

    if Permission(CanBanUser, identity=current_user):
        filt = db.or_(filt, Group.banned)

    group_query = Group.query.filter(filt)

    form = EditUserForm(user)
    form.primary_group.query = group_query
    form.secondary_groups.query = group_query
    if form.validate_on_submit():
        form.populate_obj(user)
        user.primary_group_id = form.primary_group.data.id

        # Don't override the password
        if form.password.data:
            user.password = form.password.data

        user.save(groups=form.secondary_groups.data)

        flash(_("User updated."), "success")
        return redirect(url_for("management.edit_user", user_id=user.id))

    return render_template("management/user_form.html",
                           form=form,
                           title=_("Edit User"))
Example #29
0
    def post(self, user_id):
        user = User.query.filter_by(id=user_id).first_or_404()

        member_group = db.and_(
            * [
                db.not_(getattr(Group, p))
                for p in ['admin', 'mod', 'super_mod', 'banned', 'guest']
            ]
        )

        filt = db.or_(
            Group.id.in_(g.id for g in current_user.groups), member_group
        )

        if Permission(IsAtleastSuperModerator, identity=current_user):
            filt = db.or_(filt, Group.mod)

        if Permission(IsAdmin, identity=current_user):
            filt = db.or_(filt, Group.admin, Group.super_mod)

        if Permission(CanBanUser, identity=current_user):
            filt = db.or_(filt, Group.banned)

        group_query = Group.query.filter(filt)

        form = EditUserForm(user)
        form.primary_group.query = group_query
        form.secondary_groups.query = group_query
        if form.validate_on_submit():
            form.populate_obj(user)
            user.primary_group_id = form.primary_group.data.id

            # Don't override the password
            if form.password.data:
                user.password = form.password.data

            user.save(groups=form.secondary_groups.data)

            flash(_('User updated.'), 'success')
            return redirect(url_for('management.edit_user', user_id=user.id))

        return render_template(
            'management/user_form.html', form=form, title=_('Edit User')
        )
Example #30
0
    def get_forums(cls, category_id, user):
        """Get the forums for the category.
        It returns a tuple with the category and the forums with their
        forumsread object are stored in a list.

        A return value can look like this for a category with two forums::

            (<Category 1>, [(<Forum 1>, None), (<Forum 2>, None)])

        :param category_id: The category id

        :param user: The user object is needed to check if we also need their
                     forumsread object.
        """
        from flaskbb.user.models import Group
        if user.is_authenticated():
            # get list of user group ids
            user_groups = [gr.id for gr in user.groups]
            # filter forums by user groups
            user_forums = Forum.query.filter(
                Forum.groups.any(Group.id.in_(user_groups))).subquery()
            forum_alias = aliased(Forum, user_forums)
            forums = cls.query.filter(cls.id == category_id).join(
                forum_alias, cls.id == forum_alias.category_id).outerjoin(
                    ForumsRead,
                    db.and_(ForumsRead.forum_id == forum_alias.id,
                            ForumsRead.user_id == user.id)).add_entity(
                                forum_alias).add_entity(ForumsRead).order_by(
                                    forum_alias.position).all()
        else:
            guest_group = Group.get_guest_group()
            # filter forums by guest groups
            guest_forums = Forum.query.filter(
                Forum.groups.any(Group.id == guest_group.id)).subquery()
            forum_alias = aliased(Forum, guest_forums)
            forums = cls.query.filter(cls.id == category_id).join(
                forum_alias, cls.id == forum_alias.category_id).add_entity(
                    forum_alias).order_by(forum_alias.position).all()

        if not forums:
            abort(404)

        return get_forums(forums, user)
Example #31
0
    def get(self, slug=None, plugin=None):
        form, old_settings, plugin_obj, active_nav = \
            self._determine_active_settings(slug, plugin)

        # get all groups and plugins - used to build the navigation
        all_groups = SettingsGroup.query.all()
        all_plugins = PluginRegistry.query.filter(db.and_(
            PluginRegistry.values != None,
            PluginRegistry.enabled == True
        )).all()
        form = populate_settings_form(form, old_settings)

        return render_template(
            "management/settings.html",
            form=form,
            all_groups=all_groups,
            all_plugins=all_plugins,
            active_nav=active_nav
        )
Example #32
0
def edit_user(user_id):
    user = User.query.filter_by(id=user_id).first_or_404()

    if not can_edit_user(current_user):
        flash(_("You are not allowed to edit this user."), "danger")
        return redirect(url_for("management.users"))

    member_group = db.and_(*[db.not_(getattr(Group, p)) for p in ['admin',
                                              'mod',
                                              'super_mod',
                                              'banned',
                                              'guest'
                                              ]])

    filt = db.or_(Group.id.in_(g.id for g in user.groups),
                   member_group)

    if any(user.permissions[p] for p in ['super_mod', 'admin']):
        filt = db.or_(filt, Group.mod)

    if user.permissions['admin']:
        filt = db.or_(filt, Group.admin, Group.super_mod)

    group_query = Group.query.filter(filt)

    form = EditUserForm(user)
    form.primary_group.query = group_query
    form.secondary_groups.query = group_query
    if form.validate_on_submit():
        form.populate_obj(user)
        user.primary_group_id = form.primary_group.data.id

        # Don't override the password
        if form.password.data:
            user.password = form.password.data

        user.save(groups=form.secondary_groups.data)

        flash(_("User successfully updated."), "success")
        return redirect(url_for("management.edit_user", user_id=user.id))

    return render_template("management/user_form.html", form=form,
                           title=_("Edit User"))
Example #33
0
    def get_forum(cls, forum_id, user):
        """Returns the forum and forumsread object as a tuple for the user.

        :param forum_id: The forum id
        :param user: The user object is needed to check if we also need their
                     forumsread object.
        """
        if user.is_authenticated:
            forum, forumsread = Forum.query.\
                filter(Forum.id == forum_id).\
                options(db.joinedload("category")).\
                outerjoin(ForumsRead,
                          db.and_(ForumsRead.forum_id == Forum.id,
                                  ForumsRead.user_id == user.id)).\
                add_entity(ForumsRead).\
                first_or_404()
        else:
            forum = Forum.query.filter(Forum.id == forum_id).first_or_404()
            forumsread = None

        return forum, forumsread
Example #34
0
    def get_forum(cls, forum_id, user):
        """Returns the forum and forumsread object as a tuple for the user.

        :param forum_id: The forum id
        :param user: The user object is needed to check if we also need their
                     forumsread object.
        """
        if user.is_authenticated:
            forum, forumsread = Forum.query.\
                filter(Forum.id == forum_id).\
                options(db.joinedload("category")).\
                outerjoin(ForumsRead,
                          db.and_(ForumsRead.forum_id == Forum.id,
                                  ForumsRead.user_id == user.id)).\
                add_entity(ForumsRead).\
                first_or_404()
        else:
            forum = Forum.query.filter(Forum.id == forum_id).first_or_404()
            forumsread = None

        return forum, forumsread
    def post(self, slug=None, plugin=None):
        form, old_settings, plugin_obj, active_nav = \
            self._determine_active_settings(slug, plugin)
        all_groups = SettingsGroup.query.all()
        all_plugins = PluginRegistry.query.filter(
            db.and_(PluginRegistry.values != None,
                    PluginRegistry.enabled == True)).all()

        if form.validate_on_submit():
            new_settings = populate_settings_dict(form, old_settings)

            if plugin_obj is not None:
                plugin_obj.update_settings(new_settings)
            else:
                Setting.update(settings=new_settings, app=current_app)

            flash(_("Settings saved."), "success")

        return render_template("management/settings.html",
                               form=form,
                               all_groups=all_groups,
                               all_plugins=all_plugins,
                               active_nav=active_nav)
Example #36
0
def index():
    # Get the categories and forums
    if current_user.is_authenticated():
        categories_query = Forum.query.\
            outerjoin(ForumsRead,
                      db.and_(ForumsRead.forum_id == Forum.id,
                              ForumsRead.user_id == current_user.id)).\
            add_entity(ForumsRead).\
            order_by(Forum.position.asc()).\
            all()
        categories = get_forums(categories_query, current_user=True)
    else:
        categories_query = Forum.query.order_by(Forum.position.asc()).all()
        categories = get_forums(categories_query, current_user=False)

    # Fetch a few stats about the forum
    user_count = User.query.count()
    topic_count = Topic.query.count()
    post_count = Post.query.count()
    newest_user = User.query.order_by(User.id.desc()).first()

    # Check if we use redis or not
    if not current_app.config["REDIS_ENABLED"]:
        online_users = User.query.filter(User.lastseen >= time_diff()).count()
        online_guests = None
    else:
        online_users = len(get_online_users())
        online_guests = len(get_online_users(guest=True))

    return render_template("forum/index.html",
                           categories=categories,
                           user_count=user_count,
                           topic_count=topic_count,
                           post_count=post_count,
                           newest_user=newest_user,
                           online_users=online_users,
                           online_guests=online_guests)
Example #37
0
    def get_forums(cls, category_id, user):
        """Get the forums for the category.
        It returns a tuple with the category and the forums with their
        forumsread object are stored in a list.

        A return value can look like this for a category with two forums::

            (<Category 1>, [(<Forum 1>, None), (<Forum 2>, None)])

        :param category_id: The category id

        :param user: The user object is needed to check if we also need their
                     forumsread object.
        """
        if user.is_authenticated():
            forums = cls.query.\
                filter(cls.id == category_id).\
                join(Forum, cls.id == Forum.category_id).\
                outerjoin(ForumsRead,
                          db.and_(ForumsRead.forum_id == Forum.id,
                                  ForumsRead.user_id == user.id)).\
                add_entity(Forum).\
                add_entity(ForumsRead).\
                order_by(Forum.position).\
                all()
        else:
            forums = cls.query.\
                filter(cls.id == category_id).\
                join(Forum, cls.id == Forum.category_id).\
                add_entity(Forum).\
                order_by(Forum.position).\
                all()

        if not forums:
            abort(404)

        return get_forums(forums, user)
Example #38
0
    def get_forums(cls, category_id, user):
        """Get the forums for the category.
        It returns a tuple with the category and the forums with their
        forumsread object are stored in a list.

        A return value can look like this for a category with two forums::

            (<Category 1>, [(<Forum 1>, None), (<Forum 2>, None)])

        :param category_id: The category id

        :param user: The user object is needed to check if we also need their
                     forumsread object.
        """
        if user.is_authenticated():
            forums = cls.query.\
                filter(cls.id == category_id).\
                join(Forum, cls.id == Forum.category_id).\
                outerjoin(ForumsRead,
                          db.and_(ForumsRead.forum_id == Forum.id,
                                  ForumsRead.user_id == user.id)).\
                add_entity(Forum).\
                add_entity(ForumsRead).\
                order_by(Forum.position).\
                all()
        else:
            forums = cls.query.\
                filter(cls.id == category_id).\
                join(Forum, cls.id == Forum.category_id).\
                add_entity(Forum).\
                order_by(Forum.position).\
                all()

        if not forums:
            abort(404)

        return get_forums(forums, user)
Example #39
0
def topictracker():
    page = request.args.get("page", 1, type=int)
    topics = current_user.tracked_topics.\
        outerjoin(TopicsRead,
                  db.and_(TopicsRead.topic_id == Topic.id,
                          TopicsRead.user_id == current_user.id)).\
        add_entity(TopicsRead).\
        order_by(Topic.last_updated.desc()).\
        paginate(page, flaskbb_config['TOPICS_PER_PAGE'], True)

    # bulk untracking
    if request.method == "POST":
        topic_ids = request.form.getlist("rowid")
        tmp_topics = Topic.query.filter(Topic.id.in_(topic_ids)).all()

        for topic in tmp_topics:
            current_user.untrack_topic(topic)
        current_user.save()

        flash(_("%(topic_count)s topics untracked.",
                topic_count=len(tmp_topics)), "success")
        return redirect(url_for("forum.topictracker"))

    return render_template("forum/topictracker.html", topics=topics)
Example #40
0
    def get_topics(cls, forum_id, user, page=1, per_page=20):
        """Get the topics for the forum. If the user is logged in,
        it will perform an outerjoin for the topics with the topicsread and
        forumsread relation to check if it is read or unread.

        :param forum_id: The forum id
        :param user: The user object
        :param page: The page whom should be loaded
        :param per_page: How many topics per page should be shown
        """
        if user.is_authenticated:
            # Now thats intersting - if i don't do the add_entity(Post)
            # the n+1 still exists when trying to access 'topic.last_post'
            # but without it it will fire another query.
            # This way I don't have to use the last_post object when I
            # iterate over the result set.
            topics = Topic.query.filter_by(forum_id=forum_id).\
                outerjoin(TopicsRead,
                          db.and_(TopicsRead.topic_id == Topic.id,
                                  TopicsRead.user_id == user.id)).\
                outerjoin(Post, Topic.last_post_id == Post.id).\
                add_entity(Post).\
                add_entity(TopicsRead).\
                order_by(Topic.important.desc(), Topic.last_updated.desc()).\
                paginate(page, per_page, True)
        else:
            topics = Topic.query.filter_by(forum_id=forum_id).\
                outerjoin(Post, Topic.last_post_id == Post.id).\
                add_entity(Post).\
                order_by(Topic.important.desc(), Topic.last_updated.desc()).\
                paginate(page, per_page, True)

            topics.items = [(topic, last_post, None)
                            for topic, last_post, in topics.items]

        return topics
Example #41
0
    def get_topics(cls, forum_id, user, page=1, per_page=20):
        """Get the topics for the forum. If the user is logged in,
        it will perform an outerjoin for the topics with the topicsread and
        forumsread relation to check if it is read or unread.

        :param forum_id: The forum id
        :param user: The user object
        :param page: The page whom should be loaded
        :param per_page: How many topics per page should be shown
        """
        if user.is_authenticated:
            # Now thats intersting - if i don't do the add_entity(Post)
            # the n+1 still exists when trying to access 'topic.last_post'
            # but without it it will fire another query.
            # This way I don't have to use the last_post object when I
            # iterate over the result set.
            topics = Topic.query.filter_by(forum_id=forum_id).\
                outerjoin(TopicsRead,
                          db.and_(TopicsRead.topic_id == Topic.id,
                                  TopicsRead.user_id == user.id)).\
                outerjoin(Post, Topic.last_post_id == Post.id).\
                add_entity(Post).\
                add_entity(TopicsRead).\
                order_by(Topic.important.desc(), Topic.last_updated.desc()).\
                paginate(page, per_page, True)
        else:
            topics = Topic.query.filter_by(forum_id=forum_id).\
                outerjoin(Post, Topic.last_post_id == Post.id).\
                add_entity(Post).\
                order_by(Topic.important.desc(), Topic.last_updated.desc()).\
                paginate(page, per_page, True)

            topics.items = [(topic, last_post, None)
                            for topic, last_post, in topics.items]

        return topics
Example #42
0
def topictracker():
    page = request.args.get("page", 1, type=int)
    topics = current_user.tracked_topics.\
        outerjoin(TopicsRead,
                  db.and_(TopicsRead.topic_id == Topic.id,
                          TopicsRead.user_id == current_user.id)).\
        add_entity(TopicsRead).\
        order_by(Topic.last_updated.desc()).\
        paginate(page, flaskbb_config['TOPICS_PER_PAGE'], True)

    # bulk untracking
    if request.method == "POST":
        topic_ids = request.form.getlist("rowid")
        tmp_topics = Topic.query.filter(Topic.id.in_(topic_ids)).all()

        for topic in tmp_topics:
            current_user.untrack_topic(topic)
        current_user.save()

        flash(_("%(topic_count)s topics untracked.",
                topic_count=len(tmp_topics)), "success")
        return redirect(url_for("forum.topictracker"))

    return render_template("forum/topictracker.html", topics=topics)
Example #43
0
    def update_read(self, user, forumsread, topicsread):
        """Updates the ForumsRead status for the user. In order to work
        correctly, be sure that `topicsread is **not** `None`.

        :param user: The user for whom we should check if he has read the
                     forum.

        :param forumsread: The forumsread object. It is needed to check if
                           if the forum is unread. If `forumsread` is `None`
                           and the forum is unread, it will create a new entry
                           in the `ForumsRead` relation, else (and the forum
                           is still unread) we are just going to update the
                           entry in the `ForumsRead` relation.

        :param topicsread: The topicsread object is used in combination
                           with the forumsread object to check if the
                           forumsread relation should be updated and
                           therefore is unread.
        """
        if not user.is_authenticated or topicsread is None:
            return False

        read_cutoff = None
        if flaskbb_config['TRACKER_LENGTH'] > 0:
            read_cutoff = time_utcnow() - timedelta(
                days=flaskbb_config['TRACKER_LENGTH'])

        # fetch the unread posts in the forum
        unread_count = Topic.query.\
            outerjoin(TopicsRead,
                      db.and_(TopicsRead.topic_id == Topic.id,
                              TopicsRead.user_id == user.id)).\
            outerjoin(ForumsRead,
                      db.and_(ForumsRead.forum_id == Topic.forum_id,
                              ForumsRead.user_id == user.id)).\
            filter(Topic.forum_id == self.id,
                   Topic.last_updated > read_cutoff,
                   db.or_(TopicsRead.last_read == None,  # noqa: E711
                          TopicsRead.last_read < Topic.last_updated),
                   db.or_(ForumsRead.last_read == None,  # noqa: E711
                          ForumsRead.last_read < Topic.last_updated)).\
            count()

        # No unread topics available - trying to mark the forum as read
        if unread_count == 0:
            logger.debug("No unread topics. Trying to mark the forum as read.")

            if forumsread and forumsread.last_read > topicsread.last_read:
                logger.debug("forumsread.last_read is newer than "
                             "topicsread.last_read. Everything is read.")
                return False

            # ForumRead Entry exists - Updating it because a new topic/post
            # has been submitted and has read everything (obviously, else the
            # unread_count would be useless).
            elif forumsread:
                logger.debug("Updating existing ForumsRead '{}' object."
                             .format(forumsread))
                forumsread.last_read = time_utcnow()
                forumsread.save()
                return True

            # No ForumRead Entry existing - creating one.
            logger.debug("Creating new ForumsRead object.")
            forumsread = ForumsRead()
            forumsread.user = user
            forumsread.forum = self
            forumsread.last_read = time_utcnow()
            forumsread.save()
            return True

        # Nothing updated, because there are still more than 0 unread
        # topicsread
        logger.debug("No ForumsRead object updated - there are still {} "
                     "unread topics.".format(unread_count))
        return False
Example #44
0
    def update_read(self, user, forumsread, topicsread):
        """Updates the ForumsRead status for the user. In order to work
        correctly, be sure that `topicsread is **not** `None`.

        :param user: The user for whom we should check if he has read the
                     forum.

        :param forumsread: The forumsread object. It is needed to check if
                           if the forum is unread. If `forumsread` is `None`
                           and the forum is unread, it will create a new entry
                           in the `ForumsRead` relation, else (and the forum
                           is still unread) we are just going to update the
                           entry in the `ForumsRead` relation.

        :param topicsread: The topicsread object is used in combination
                           with the forumsread object to check if the
                           forumsread relation should be updated and
                           therefore is unread.
        """
        if not user.is_authenticated or topicsread is None:
            return False

        read_cutoff = None
        if flaskbb_config['TRACKER_LENGTH'] > 0:
            read_cutoff = time_utcnow() - timedelta(
                days=flaskbb_config['TRACKER_LENGTH'])

        # fetch the unread posts in the forum
        unread_count = Topic.query.\
            outerjoin(TopicsRead,
                      db.and_(TopicsRead.topic_id == Topic.id,
                              TopicsRead.user_id == user.id)).\
            outerjoin(ForumsRead,
                      db.and_(ForumsRead.forum_id == Topic.forum_id,
                              ForumsRead.user_id == user.id)).\
            filter(Topic.forum_id == self.id,
                   Topic.last_updated > read_cutoff,
                   db.or_(TopicsRead.last_read == None,
                          TopicsRead.last_read < Topic.last_updated)).\
            count()

        # No unread topics available - trying to mark the forum as read
        if unread_count == 0:

            if forumsread and forumsread.last_read > topicsread.last_read:
                return False

            # ForumRead Entry exists - Updating it because a new topic/post
            # has been submitted and has read everything (obviously, else the
            # unread_count would be useless).
            elif forumsread:
                forumsread.last_read = time_utcnow()
                forumsread.save()
                return True

            # No ForumRead Entry existing - creating one.
            forumsread = ForumsRead()
            forumsread.user_id = user.id
            forumsread.forum_id = self.id
            forumsread.last_read = time_utcnow()
            forumsread.save()
            return True

        # Nothing updated, because there are still more than 0 unread
        # topicsread
        return False
Example #45
0
 def validate_email(self, field):
     user = User.query.filter(db.and_(
                              User.email.like(field.data),
                              db.not_(User.id == self.user.id))).first()
     if user:
         raise ValidationError(_("This E-Mail Address is already taken."))
Example #46
0
 def validate_email(self, field):
     user = User.query.filter(
         db.and_(User.email.like(field.data),
                 db.not_(User.id == self.user.id))).first()
     if user:
         raise ValidationError("This email is taken")
Example #47
0
    def update_read(self, user, forum, forumsread=None):
        """Update the topics read status if the user hasn't read the latest
        post.

        :param user: The user for whom the readstracker should be updated

        :param forum: The forum in which the topic is

        :param forumsread: The forumsread object. It is used to check if there
                           is a new post since the forum has been marked as
                           read
        """

        read_cutoff = datetime.utcnow() - timedelta(
            days=current_app.config['TRACKER_LENGTH'])

        # Anonymous User or the post is too old for inserting it in the
        # TopicsRead model
        if not user.is_authenticated() or \
                read_cutoff > self.last_post.date_created:
            return

        topicread = TopicsRead.query.\
            filter(TopicsRead.user_id == user.id,
                   TopicsRead.topic_id == self.id).first()

        # Can be None if the user has never marked the forum as read. If this
        # condition is false - we need to update the tracker
        if forumsread and forumsread.cleared is not None and \
                forumsread.cleared >= self.last_post.date_created:
            return

        # A new post has been submitted that the user hasn't read.
        # Updating...
        if topicread and (topicread.last_read < self.last_post.date_created):
            topicread.last_read = datetime.utcnow()
            topicread.save()

        # The user has not visited the topic before. Inserting him in
        # the TopicsRead model.
        elif not topicread:
            topicread = TopicsRead()
            topicread.user_id = user.id
            topicread.topic_id = self.id
            topicread.forum_id = self.forum_id
            topicread.last_read = datetime.utcnow()
            topicread.save()

        # else: no unread posts

        if forum:
            # fetch the unread posts in the forum
            unread_count = Topic.query.\
                outerjoin(TopicsRead,
                          db.and_(TopicsRead.topic_id == Topic.id,
                                  TopicsRead.user_id == user.id)).\
                outerjoin(ForumsRead,
                          db.and_(ForumsRead.forum_id == Topic.forum_id,
                                  ForumsRead.user_id == user.id)).\
                filter(Topic.forum_id == forum.id,
                       db.or_(TopicsRead.last_read == None,
                              TopicsRead.last_read < Topic.last_updated)).\
                count()

            #No unread topics available - trying to mark the forum as read
            if unread_count == 0:
                forumread = ForumsRead.query.\
                    filter(ForumsRead.user_id == user.id,
                           ForumsRead.forum_id == forum.id).first()

                # ForumsRead is already up-to-date.
                if forumread and forumread.last_read > topicread.last_read:
                    return

                # ForumRead Entry exists - Updating it because a new post
                # has been submitted that the user hasn't read.
                elif forumread:
                    forumread.last_read = datetime.utcnow()
                    forumread.save()

                # No ForumRead Entry existing - creating one.
                else:
                    forumread = ForumsRead()
                    forumread.user_id = user.id
                    forumread.forum_id = forum.id
                    forumread.last_read = datetime.utcnow()
                    forumread.save()
Example #48
0
    def update_read(self, user, forum, forumsread=None):
        """Update the topics read status if the user hasn't read the latest
        post.

        :param user: The user for whom the readstracker should be updated

        :param forum: The forum in which the topic is

        :param forumsread: The forumsread object. It is used to check if there
                           is a new post since the forum has been marked as
                           read
        """

        read_cutoff = datetime.utcnow() - timedelta(
            days=current_app.config['TRACKER_LENGTH'])

        # Anonymous User or the post is too old for inserting it in the
        # TopicsRead model
        if not user.is_authenticated() or \
                read_cutoff > self.last_post.date_created:
            return

        topicread = TopicsRead.query.\
            filter(TopicsRead.user_id == user.id,
                   TopicsRead.topic_id == self.id).first()

        # Can be None if the user has never marked the forum as read. If this
        # condition is false - we need to update the tracker
        if forumsread and forumsread.cleared is not None and \
                forumsread.cleared >= self.last_post.date_created:
            return

        # A new post has been submitted that the user hasn't read.
        # Updating...
        if topicread and (topicread.last_read < self.last_post.date_created):
            topicread.last_read = datetime.utcnow()
            topicread.save()

        # The user has not visited the topic before. Inserting him in
        # the TopicsRead model.
        elif not topicread:
            topicread = TopicsRead()
            topicread.user_id = user.id
            topicread.topic_id = self.id
            topicread.forum_id = self.forum_id
            topicread.last_read = datetime.utcnow()
            topicread.save()

        # else: no unread posts

        if forum:
            # fetch the unread posts in the forum
            unread_count = Topic.query.\
                outerjoin(TopicsRead,
                          db.and_(TopicsRead.topic_id == Topic.id,
                                  TopicsRead.user_id == user.id)).\
                outerjoin(ForumsRead,
                          db.and_(ForumsRead.forum_id == Topic.forum_id,
                                  ForumsRead.user_id == user.id)).\
                filter(Topic.forum_id == forum.id,
                       db.or_(TopicsRead.last_read == None,
                              TopicsRead.last_read < Topic.last_updated)).\
                count()

            # No unread topics available - trying to mark the forum as read
            if unread_count == 0:
                forumread = ForumsRead.query.\
                    filter(ForumsRead.user_id == user.id,
                           ForumsRead.forum_id == forum.id).first()

                # ForumsRead is already up-to-date.
                if forumread and forumread.last_read > topicread.last_read:
                    return

                # ForumRead Entry exists - Updating it because a new post
                # has been submitted that the user hasn't read.
                elif forumread:
                    forumread.last_read = datetime.utcnow()
                    forumread.save()

                # No ForumRead Entry existing - creating one.
                else:
                    forumread = ForumsRead()
                    forumread.user_id = user.id
                    forumread.forum_id = forum.id
                    forumread.last_read = datetime.utcnow()
                    forumread.save()