Browse Source

Added a CRUDMixin and did some code cleanup.

sh4nks 9 years ago
parent
commit
5230bd0880

+ 20 - 71
flaskbb/forum/models.py

@@ -13,7 +13,9 @@ from datetime import datetime, timedelta
 from flask import url_for, abort
 
 from flaskbb.extensions import db
-from flaskbb.utils.helpers import slugify, get_categories_and_forums, get_forums
+from flaskbb.utils.helpers import slugify, get_categories_and_forums, \
+    get_forums
+from flaskbb.utils.database import CRUDMixin
 from flaskbb.utils.settings import flaskbb_config
 
 
@@ -36,7 +38,7 @@ topictracker = db.Table(
               nullable=False))
 
 
-class TopicsRead(db.Model):
+class TopicsRead(db.Model, CRUDMixin):
     __tablename__ = "topicsread"
 
     user_id = db.Column(db.Integer, db.ForeignKey("users.id"),
@@ -51,23 +53,8 @@ class TopicsRead(db.Model):
                          primary_key=True)
     last_read = db.Column(db.DateTime, default=datetime.utcnow())
 
-    def __repr__(self):
-        return "<{}>".format(self.__class__.__name__)
-
-    def save(self):
-        """Saves a TopicsRead entry."""
-        db.session.add(self)
-        db.session.commit()
-        return self
-
-    def delete(self):
-        """Deletes a TopicsRead entry."""
-        db.session.delete(self)
-        db.session.commit()
-        return self
-
 
-class ForumsRead(db.Model):
+class ForumsRead(db.Model, CRUDMixin):
     __tablename__ = "forumsread"
 
     user_id = db.Column(db.Integer, db.ForeignKey("users.id"),
@@ -79,23 +66,8 @@ class ForumsRead(db.Model):
     last_read = db.Column(db.DateTime, default=datetime.utcnow())
     cleared = db.Column(db.DateTime)
 
-    def __repr__(self):
-        return "<{}>".format(self.__class__.__name__)
-
-    def save(self):
-        """Saves a ForumsRead entry."""
-        db.session.add(self)
-        db.session.commit()
-        return self
-
-    def delete(self):
-        """Deletes a ForumsRead entry."""
-        db.session.delete(self)
-        db.session.commit()
-        return self
-
 
-class Report(db.Model):
+class Report(db.Model, CRUDMixin):
     __tablename__ = "reports"
 
     id = db.Column(db.Integer, primary_key=True)
@@ -119,9 +91,7 @@ class Report(db.Model):
         """Saves a report.
 
         :param post: The post that should be reported
-
         :param user: The user who has reported the post
-
         :param reason: The reason why the user has reported the post
         """
 
@@ -139,14 +109,8 @@ class Report(db.Model):
         db.session.commit()
         return self
 
-    def delete(self):
-        """Deletes a report."""
-        db.session.delete(self)
-        db.session.commit()
-        return self
-
 
-class Post(db.Model):
+class Post(db.Model, CRUDMixin):
     __tablename__ = "posts"
     __searchable__ = ['content', 'username']
 
@@ -187,7 +151,6 @@ class Post(db.Model):
         operation was successful.
 
         :param user: The user who has created the post
-
         :param topic: The topic in which the post was created
         """
         # update/edit the post
@@ -205,7 +168,7 @@ class Post(db.Model):
 
             topic.last_updated = datetime.utcnow()
 
-            # This needs to be done before I update the last_post_id.
+            # This needs to be done before the last_post_id gets updated.
             db.session.add(self)
             db.session.commit()
 
@@ -230,7 +193,7 @@ class Post(db.Model):
             return self
 
     def delete(self):
-        """Deletes a post and returns self"""
+        """Deletes a post and returns self."""
         # This will delete the whole topic
         if self.topic.first_post_id == self.id:
             self.topic.delete()
@@ -274,7 +237,7 @@ class Post(db.Model):
         return self
 
 
-class Topic(db.Model):
+class Topic(db.Model, CRUDMixin):
     __tablename__ = "topics"
     __searchable__ = ['title', 'username']
 
@@ -344,12 +307,9 @@ class Topic(db.Model):
         Also, if the ``TRACKER_LENGTH`` is configured, it will just recognize
         topics that are newer than the ``TRACKER_LENGTH`` (in days) as unread.
 
-        TODO: Couldn't think of a better name for this method - ideas?
-
         :param forumsread: The ForumsRead object is needed because we also
                            need to check if the forum has been cleared
                            sometime ago.
-
         :param topicsread: The topicsread object is used to check if there is
                            a new post in the topic.
         """
@@ -383,9 +343,7 @@ class Topic(db.Model):
         Returns True if the tracker has been updated.
 
         :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.
@@ -465,9 +423,7 @@ class Topic(db.Model):
         given, it will only update the topic.
 
         :param user: The user who has created the topic
-
         :param forum: The forum where the topic is stored
-
         :param post: The post object which is connected to the topic
         """
 
@@ -514,7 +470,7 @@ class Topic(db.Model):
             filter_by(forum_id=self.forum_id).\
             order_by(Topic.last_post_id.desc()).limit(2).offset(0).all()
 
-        # do want to delete the topic with the last post?
+        # do we want to delete the topic with the last post in the forum?
         if topic and topic[0].id == self.id:
             try:
                 # Now the second last post will be the last post
@@ -563,7 +519,7 @@ class Topic(db.Model):
         return self
 
 
-class Forum(db.Model):
+class Forum(db.Model, CRUDMixin):
     __tablename__ = "forums"
     __searchable__ = ['title', 'description']
 
@@ -711,11 +667,16 @@ class Forum(db.Model):
             forumsread.save()
             return True
 
-        # Nothing updated, because there are still more than 0 unread topicsread
+        # Nothing updated, because there are still more than 0 unread
+        # topicsread
         return False
 
     def save(self, moderators=None):
-        """Saves a forum"""
+        """Saves a forum
+
+        :param moderators: If given, it will update the moderators in this
+                           forum with the given iterable of user objects.
+       """
         if moderators is not None:
             for moderator in self.moderators:
                 self.moderators.remove(moderator)
@@ -772,7 +733,6 @@ class Forum(db.Model):
         """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.
         """
@@ -798,11 +758,8 @@ class Forum(db.Model):
         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():
@@ -823,7 +780,7 @@ class Forum(db.Model):
         return topics
 
 
-class Category(db.Model):
+class Category(db.Model, CRUDMixin):
     __tablename__ = "categories"
     __searchable__ = ['title', 'description']
 
@@ -857,13 +814,6 @@ class Category(db.Model):
         """
         return "<{} {}>".format(self.__class__.__name__, self.id)
 
-    def save(self):
-        """Saves a category"""
-
-        db.session.add(self)
-        db.session.commit()
-        return self
-
     def delete(self, users=None):
         """Deletes a category. If a list with involved user objects is passed,
         it will also update their post counts
@@ -929,7 +879,6 @@ class Category(db.Model):
             (<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.
         """

+ 3 - 22
flaskbb/management/models.py

@@ -14,9 +14,10 @@ from flask_wtf import Form
 
 from flaskbb._compat import max_integer, text_type, iteritems
 from flaskbb.extensions import db, cache
+from flaskbb.utils.database import CRUDMixin
 
 
-class SettingsGroup(db.Model):
+class SettingsGroup(db.Model, CRUDMixin):
     __tablename__ = "settingsgroup"
 
     key = db.Column(db.String(255), primary_key=True)
@@ -25,18 +26,8 @@ class SettingsGroup(db.Model):
     settings = db.relationship("Setting", lazy="dynamic", backref="group",
                                cascade="all, delete-orphan")
 
-    def save(self):
-        """Saves a settingsgroup."""
-        db.session.add(self)
-        db.session.commit()
 
-    def delete(self):
-        """Deletes a settingsgroup."""
-        db.session.delete(self)
-        db.session.commit()
-
-
-class Setting(db.Model):
+class Setting(db.Model, CRUDMixin):
     __tablename__ = "settings"
 
     key = db.Column(db.String(255), primary_key=True)
@@ -244,13 +235,3 @@ class Setting(db.Model):
     def invalidate_cache(cls):
         """Invalidates this objects cached metadata."""
         cache.delete_memoized(cls.as_dict, cls)
-
-    def save(self):
-        """Saves a setting"""
-        db.session.add(self)
-        db.session.commit()
-
-    def delete(self):
-        """Deletes a setting"""
-        db.session.delete(self)
-        db.session.commit()

+ 3 - 17
flaskbb/message/models.py

@@ -13,9 +13,9 @@ from datetime import datetime
 from sqlalchemy_utils import UUIDType
 
 from flaskbb.extensions import db
+from flaskbb.utils.database import CRUDMixin
 
-
-class Conversation(db.Model):
+class Conversation(db.Model, CRUDMixin):
     __tablename__ = "conversations"
 
     id = db.Column(db.Integer, primary_key=True)
@@ -73,15 +73,8 @@ class Conversation(db.Model):
         db.session.commit()
         return self
 
-    def delete(self):
-        """Deletes a private message"""
-
-        db.session.delete(self)
-        db.session.commit()
-        return self
-
 
-class Message(db.Model):
+class Message(db.Model, CRUDMixin):
     __tablename__ = "messages"
 
     id = db.Column(db.Integer, primary_key=True)
@@ -109,10 +102,3 @@ class Message(db.Model):
         db.session.add(self)
         db.session.commit()
         return self
-
-    def delete(self):
-        """Deletes a private message"""
-
-        db.session.delete(self)
-        db.session.commit()
-        return self

+ 1 - 1
flaskbb/static/css/flaskbb.css

@@ -11,7 +11,7 @@ body {
 }
 
 .forum-moderation, .forum-selectall, .forum-select {
-    display: none;
+
 }
 
 .footer {

+ 3 - 14
flaskbb/user/models.py

@@ -19,6 +19,7 @@ from flask_login import UserMixin, AnonymousUserMixin
 from flaskbb._compat import max_integer
 from flaskbb.extensions import db, cache
 from flaskbb.utils.settings import flaskbb_config
+from flaskbb.utils.database import CRUDMixin
 from flaskbb.forum.models import (Post, Topic, topictracker, TopicsRead,
                                   ForumsRead)
 from flaskbb.message.models import Conversation
@@ -30,7 +31,7 @@ groups_users = db.Table(
     db.Column('group_id', db.Integer(), db.ForeignKey('groups.id')))
 
 
-class Group(db.Model):
+class Group(db.Model, CRUDMixin):
     __tablename__ = "groups"
 
     id = db.Column(db.Integer, primary_key=True)
@@ -62,20 +63,8 @@ class Group(db.Model):
         """
         return "<{} {}>".format(self.__class__.__name__, self.id)
 
-    def save(self):
-        """Saves a group"""
-        db.session.add(self)
-        db.session.commit()
-        return self
-
-    def delete(self):
-        """Deletes a group"""
-        db.session.delete(self)
-        db.session.commit()
-        return self
-
 
-class User(db.Model, UserMixin):
+class User(db.Model, UserMixin, CRUDMixin):
     __tablename__ = "users"
     __searchable__ = ['username', 'email']
 

+ 28 - 0
flaskbb/utils/database.py

@@ -0,0 +1,28 @@
+# -*- coding: utf-8 -*-
+"""
+    flaskbb.utils.database
+    ~~~~~~~~~~~~~~~~~~~~~~
+
+    Some database helpers such as a CRUD mixin.
+
+    :copyright: (c) 2015 by the FlaskBB Team.
+    :license: BSD, see LICENSE for more details.
+"""
+from flaskbb.extensions import db
+
+
+class CRUDMixin(object):
+    def __repr__(self):
+        return "<{}>".format(self.__class__.__name__)
+
+    def save(self):
+        """Saves the object to the database."""
+        db.session.add(self)
+        db.session.commit()
+        return self
+
+    def delete(self):
+        """Delete the object from the database."""
+        db.session.delete(self)
+        db.session.commit()
+        return self

+ 48 - 2
flaskbb/utils/helpers.py

@@ -17,17 +17,19 @@ from io import BytesIO
 from datetime import datetime, timedelta
 
 import requests
-from flask import session, url_for
+import unidecode
+from flask import session, url_for, flash
 from babel.dates import format_timedelta
+from flask_babelex import lazy_gettext as _
 from flask_themes2 import render_theme_template
 from flask_login import current_user
-import unidecode
 
 from flaskbb._compat import range_method, text_type
 from flaskbb.extensions import redis_store
 from flaskbb.utils.settings import flaskbb_config
 from flaskbb.utils.markup import markdown
 
+
 _punct_re = re.compile(r'[\t !"#$%&\'()*\-/<=>?@\[\\\]^_`{|},.]+')
 
 
@@ -57,6 +59,50 @@ def render_template(template, **context):  # pragma: no cover
     return render_theme_template(theme, template, **context)
 
 
+def do_topic_action(topics, user, action, reverse):
+    """Executes a specific action for topics. Returns a list with the modified
+    topic objects.
+
+    :param topics: A iterable with ``Topic`` objects.
+    :param user: The user object which wants to perform the action.
+    :param action: One of the following actions: locked, important and delete.
+    :param reverse: If the action should be done in a reversed way.
+                    For example, to unlock a topic, ``reverse`` should be
+                    set to ``True``.
+    """
+    from flaskbb.utils.permissions import can_moderate, can_delete_topic
+    from flaskbb.user.models import User
+    from flaskbb.forum.models import Post
+
+    modified_topics = 0
+    if action != "delete":
+        for topic in topics:
+            if not can_moderate(user, topic.forum):
+                flash(_("You do not have the permissions to execute this "
+                        "action."), "danger")
+                return False
+
+            if getattr(topic, action) and not reverse:
+                continue
+
+            setattr(topic, action, not reverse)
+            modified_topics += 1
+            topic.save()
+    elif action == "delete":
+        for topic in topics:
+            if not can_delete_topic(user, topic):
+                flash(_("You do not have the permissions to delete this "
+                        "topic."), "danger")
+                return False
+
+            involved_users = User.query.filter(Post.topic_id == topic.id,
+                                               User.id == Post.user_id).all()
+            modified_topics += 1
+            topic.delete(involved_users)
+
+    return modified_topics
+
+
 def get_categories_and_forums(query_result, user):
     """Returns a list with categories. Every category has a list for all
     their associated forums.