Ejemplo n.º 1
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')
        )
Ejemplo n.º 2
0
    def _fix_post_counts(self, forum):
        clauses = [
            Topic.forum_id == forum.id
        ]
        if self.hidden:
            clauses.extend([
                Topic.id != self.id,
                Topic.hidden != True,
            ])
        else:
            clauses.append(db.or_(Topic.id == self.id, Topic.hidden != True))

        forum.topic_count = Topic.query.filter(*clauses).count()

        post_count = clauses + [
            Post.topic_id == Topic.id,
        ]

        if self.hidden:
            post_count.append(Post.hidden != True)
        else:
            post_count.append(
                db.or_(Post.hidden != True, Post.id == self.first_post.id)
            )

        forum.post_count = Post.query.distinct().filter(*post_count).count()
Ejemplo n.º 3
0
    def _fix_post_counts(self, forum):
        clauses = [
            Topic.forum_id == forum.id
        ]
        if self.hidden:
            clauses.extend([
                Topic.id != self.id,
                Topic.hidden != True,
            ])
        else:
            clauses.append(db.or_(Topic.id == self.id, Topic.hidden != True))

        forum.topic_count = Topic.query.filter(*clauses).count()

        post_count = clauses + [
            Post.topic_id == Topic.id,
        ]

        if self.hidden:
            post_count.append(Post.hidden != True)
        else:
            post_count.append(
                db.or_(Post.hidden != True, Post.id == self.first_post.id)
            )

        forum.post_count = Post.query.distinct().filter(*post_count).count()
Ejemplo n.º 4
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')
        )
Ejemplo n.º 5
0
    def authenticate(cls, login, password):
        """A classmethod for authenticating users.
        It returns the user object if the user/password combination is ok.
        If the user has entered too often a wrong password, he will be locked
        out of his account for a specified time.

        :param login: This can be either a username or a email address.
        :param password: The password that is connected to username and email.
        """
        user = cls.query.filter(db.or_(User.username == login,
                                       User.email == login)).first()

        if user is not None:
            if user.check_password(password):
                # reset them after a successful login attempt
                user.login_attempts = 0
                user.save()
                return user

            # user exists, wrong password
            # never had a bad login before
            if user.login_attempts is None:
                user.login_attempts = 1
            else:
                user.login_attempts += 1
            user.last_failed_login = time_utcnow()
            user.save()

        # protection against account enumeration timing attacks
        check_password_hash("dummy password", password)

        raise AuthenticationError
Ejemplo n.º 6
0
    def _update_counts(self):
        if self.hidden:
            clauses = [Post.hidden != True, Post.id != self.id]
        else:
            clauses = [db.or_(Post.hidden != True, Post.id == self.id)]

        user_post_clauses = clauses + [
            Post.user_id == self.user.id,
            Topic.id == Post.topic_id,
            Topic.hidden != True,
        ]

        # Update the post counts
        self.user.post_count = Post.query.filter(*user_post_clauses).count()

        if self.topic.hidden:
            self.topic.post_count = 0
        else:
            topic_post_clauses = clauses + [
                Post.topic_id == self.topic.id,
            ]
            self.topic.post_count = Post.query.filter(
                *topic_post_clauses).count()

        forum_post_clauses = clauses + [
            Post.topic_id == Topic.id,
            Topic.forum_id == self.topic.forum.id,
            Topic.hidden != True,
        ]

        self.topic.forum.post_count = Post.query.filter(
            *forum_post_clauses).count()
Ejemplo n.º 7
0
    def authenticate(cls, login, password):
        """A classmethod for authenticating users.
        It returns the user object if the user/password combination is ok.
        If the user has entered too often a wrong password, he will be locked
        out of his account for a specified time.

        :param login: This can be either a username or a email address.
        :param password: The password that is connected to username and email.
        """
        user = cls.query.filter(
            db.or_(User.username == login, User.email == login)).first()

        if user:
            if user.check_password(password):
                # reset them after a successful login attempt
                user.login_attempts = 0
                user.save()
                return user

            # user exists, wrong password
            user.login_attempts += 1
            user.last_failed_login = datetime.utcnow()
            user.save()

        # protection against account enumeration timing attacks
        dummy_password = os.urandom(15).encode("base-64")
        check_password_hash(dummy_password, password)

        raise AuthenticationError
Ejemplo n.º 8
0
    def _update_counts(self):
        if self.hidden:
            clauses = [Post.hidden != True, Post.id != self.id]
        else:
            clauses = [db.or_(Post.hidden != True, Post.id == self.id)]

        user_post_clauses = clauses + [
            Post.user_id == self.user.id,
            Topic.id == Post.topic_id,
            Topic.hidden != True,
        ]

        # Update the post counts
        self.user.post_count = Post.query.filter(*user_post_clauses).count()

        if self.topic.hidden:
            self.topic.post_count = 0
        else:
            topic_post_clauses = clauses + [
                Post.topic_id == self.topic.id,
            ]
            self.topic.post_count = Post.query.filter(
                *topic_post_clauses
            ).count()

        forum_post_clauses = clauses + [
            Post.topic_id == Topic.id,
            Topic.forum_id == self.topic.forum.id,
            Topic.hidden != True,
        ]

        self.topic.forum.post_count = Post.query.filter(
            *forum_post_clauses
        ).count()
Ejemplo n.º 9
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
Ejemplo n.º 10
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
Ejemplo n.º 11
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"))
Ejemplo n.º 12
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')
        )
Ejemplo n.º 13
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"))
Ejemplo n.º 14
0
    def authenticate(cls, login, password):
        """
        A classmethod for authenticating users
        It returns true if the user exists and has entered a correct password
        """
        user = cls.query.filter(db.or_(User.username == login,
                                       User.email == login)).first()

        if user:
            authenticated = user.check_password(password)
        else:
            authenticated = False
        return user, authenticated
Ejemplo n.º 15
0
    def authenticate(cls, login, password):
        """A classmethod for authenticating users
        It returns true if the user exists and has entered a correct password

        :param login: This can be either a username or a email address.

        :param password: The password that is connected to username and email.
        """

        user = cls.query.filter(
            db.or_(User.username == login, User.email == login)).first()

        if user:
            authenticated = user.check_password(password)
        else:
            authenticated = False
        return user, authenticated
Ejemplo n.º 16
0
    def authenticate(cls, login, password):
        """A classmethod for authenticating users.
        It returns true if the user exists and has entered a correct password

        :param login: This can be either a username or a email address.

        :param password: The password that is connected to username and email.
        """

        user = cls.query.filter(db.or_(User.username == login,
                                       User.email == login)).first()

        if user:
            authenticated = user.check_password(password)
        else:
            authenticated = False
        return user, authenticated
Ejemplo n.º 17
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()
Ejemplo n.º 18
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()
Ejemplo n.º 19
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
Ejemplo n.º 20
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