Просмотр исходного кода

Clean up Post and Topic forms a bit

Peter Justin 6 лет назад
Родитель
Сommit
8b20502300

+ 37 - 16
flaskbb/forum/forms.py

@@ -24,52 +24,73 @@ from flaskbb.user.models import User
 logger = logging.getLogger(__name__)
 
 
-class QuickreplyForm(FlaskForm):
-    content = TextAreaField(_("Quick reply"), validators=[
+class PostForm(FlaskForm):
+    content = TextAreaField(_("Content"), validators=[
         DataRequired(message=_("You cannot post a reply without content."))])
 
     submit = SubmitField(_("Reply"))
 
     def save(self, user, topic):
         post = Post(content=self.content.data)
+        current_app.pluggy.hook.flaskbb_form_new_post_save(form=self)
         return post.save(user=user, topic=topic)
 
 
-class ReplyForm(FlaskForm):
-    content = TextAreaField(_("Content"), validators=[
-        DataRequired(message=_("You cannot post a reply without content."))])
+class QuickreplyForm(PostForm):
+    pass
+
 
+class ReplyForm(PostForm):
     track_topic = BooleanField(_("Track this topic"), default=False,
                                validators=[Optional()])
 
-    submit = SubmitField(_("Reply"))
-
     def save(self, user, topic):
-        post = Post(content=self.content.data)
-
         if self.track_topic.data:
             user.track_topic(topic)
+        else:
+            user.untrack_topic(topic)
 
-        current_app.pluggy.hook.flaskbb_form_new_post_save(form=self)
-        return post.save(user=user, topic=topic)
+        return super(ReplyForm, self).save(user, topic)
 
 
-class NewTopicForm(ReplyForm):
+class TopicForm(FlaskForm):
     title = StringField(_("Topic title"), validators=[
         DataRequired(message=_("Please choose a title for your topic."))])
 
-    submit = SubmitField(_("Post Topic"))
+    content = TextAreaField(_("Content"), validators=[
+        DataRequired(message=_("You cannot post a reply without content."))])
+
+    track_topic = BooleanField(_("Track this topic"), default=False,
+                               validators=[Optional()])
+
+    submit = SubmitField(_("Post topic"))
 
     def save(self, user, forum):
-        topic = Topic(title=self.title.data)
-        post = Post(content=self.content.data)
+        topic = Topic(title=self.title.data, content=self.content.data)
 
         if self.track_topic.data:
             user.track_topic(topic)
+        else:
+            user.untrack_topic(topic)
 
         current_app.pluggy.hook.flaskbb_form_new_topic_save(form=self,
                                                             topic=topic)
-        return topic.save(user=user, forum=forum, post=post)
+        return topic.save(user=user, forum=forum)
+
+
+class NewTopicForm(TopicForm):
+    pass
+
+
+class EditTopicForm(TopicForm):
+    def populate_obj(self, *objs):
+        """
+        Populates the attributes of the passed `obj`s with data from the
+        form's fields. This is especially useful to populate the topic and
+        post objects at the same time.
+        """
+        for obj in objs:
+            super(EditTopicForm, self).populate_obj(obj)
 
 
 class ReportForm(FlaskForm):

+ 43 - 28
flaskbb/forum/models.py

@@ -193,6 +193,10 @@ class Post(HideableCRUDMixin, db.Model):
         """
         return "<{} {}>".format(self.__class__.__name__, self.id)
 
+    def is_first_post(self):
+        """Checks whether this post is the first post in the topic or not."""
+        return self.topic.is_first_post(self)
+
     def save(self, user=None, topic=None):
         """Saves a new post. If no parameters are passed we assume that
         you will just update an existing post. It returns the object after the
@@ -433,7 +437,6 @@ class Topic(HideableCRUDMixin, db.Model):
                             cascade="all, delete-orphan",
                             post_update=True)
 
-    # Properties
     @property
     def second_last_post(self):
         """Returns the second last post or None."""
@@ -452,31 +455,7 @@ class Topic(HideableCRUDMixin, db.Model):
         """Returns the slugified url for the topic."""
         return url_for("forum.view_topic", topic_id=self.id, slug=self.slug)
 
-    def first_unread(self, topicsread, user, forumsread=None):
-        """Returns the url to the first unread post. If no unread posts exist
-        it will return the url to the topic.
-
-        :param topicsread: The topicsread object for the topic
-        :param user: The user who should be checked if he has read the
-                     last post in the topic
-        :param forumsread: The forumsread object in which the topic is. If you
-                        also want to check if the user has marked the forum as
-                        read, than you will also need to pass an forumsread
-                        object.
-        """
-        # If the topic is unread try to get the first unread post
-        if topic_is_unread(self, topicsread, user, forumsread):
-            query = Post.query.filter(Post.topic_id == self.id)
-            if topicsread is not None:
-                query = query.filter(Post.date_created > topicsread.last_read)
-            post = query.order_by(Post.id.asc()).first()
-            if post is not None:
-                return post.url
-
-        return self.url
-
-    # Methods
-    def __init__(self, title=None, user=None):
+    def __init__(self, title=None, user=None, content=None):
         """Creates a topic object with some initial values.
 
         :param title: The title of the topic.
@@ -492,6 +471,9 @@ class Topic(HideableCRUDMixin, db.Model):
             self.user_id = user.id
             self.username = user.username
 
+        if content:
+            self._post = Post(content=content)
+
         self.date_created = self.last_updated = time_utcnow()
 
     def __repr__(self):
@@ -500,6 +482,36 @@ class Topic(HideableCRUDMixin, db.Model):
         """
         return "<{} {}>".format(self.__class__.__name__, self.id)
 
+    def is_first_post(self, post):
+        """Checks if the post is the first post in the topic.
+
+        :param post: The post object.
+        """
+        return self.first_post_id == post.id
+
+    def first_unread(self, topicsread, user, forumsread=None):
+        """Returns the url to the first unread post. If no unread posts exist
+        it will return the url to the topic.
+
+        :param topicsread: The topicsread object for the topic
+        :param user: The user who should be checked if he has read the
+                     last post in the topic
+        :param forumsread: The forumsread object in which the topic is. If you
+                        also want to check if the user has marked the forum as
+                        read, than you will also need to pass an forumsread
+                        object.
+        """
+        # If the topic is unread try to get the first unread post
+        if topic_is_unread(self, topicsread, user, forumsread):
+            query = Post.query.filter(Post.topic_id == self.id)
+            if topicsread is not None:
+                query = query.filter(Post.date_created > topicsread.last_read)
+            post = query.order_by(Post.id.asc()).first()
+            if post is not None:
+                return post.url
+
+        return self.url
+
     @classmethod
     def get_topic(cls, topic_id, user):
         topic = Topic.query.filter_by(id=topic_id).first_or_404()
@@ -668,11 +680,14 @@ class Topic(HideableCRUDMixin, db.Model):
         db.session.add(self)
         db.session.commit()
 
+        if post is not None:
+            self._post = post
+
         # Create the topic post
-        post.save(user, self)
+        self._post.save(user, self)
 
         # Update the first and last post id
-        self.last_post = self.first_post = post
+        self.last_post = self.first_post = self._post
 
         # Update the topic count
         forum.topic_count += 1

+ 30 - 17
flaskbb/forum/views.py

@@ -22,25 +22,27 @@ from pluggy import HookimplMarker
 from sqlalchemy import asc, desc
 
 from flaskbb.extensions import allows, db
-from flaskbb.markup import make_renderer
-from flaskbb.forum.forms import (NewTopicForm, QuickreplyForm, ReplyForm,
-                                 ReportForm, SearchPageForm, UserSearchForm)
+from flaskbb.forum.forms import (EditTopicForm, NewTopicForm, QuickreplyForm,
+                                 ReplyForm, ReportForm, SearchPageForm,
+                                 UserSearchForm)
 from flaskbb.forum.models import (Category, Forum, ForumsRead, Post, Topic,
                                   TopicsRead)
+from flaskbb.markup import make_renderer
 from flaskbb.user.models import User
-from flaskbb.utils.helpers import (do_topic_action, format_quote,
-                                   get_online_users, real, register_view,
-                                   render_template, time_diff, time_utcnow,
-                                   FlashAndRedirect)
-from flaskbb.utils.requirements import (CanAccessForum,
-                                        CanDeletePost, CanDeleteTopic,
-                                        CanEditPost, CanPostReply,
-                                        CanPostTopic, Has,
+from flaskbb.utils.helpers import (FlashAndRedirect, do_topic_action,
+                                   format_quote, get_online_users, real,
+                                   register_view, render_template, time_diff,
+                                   time_utcnow)
+from flaskbb.utils.requirements import (CanAccessForum, CanDeletePost,
+                                        CanDeleteTopic, CanEditPost,
+                                        CanPostReply, CanPostTopic, Has,
                                         IsAtleastModeratorInForum)
 from flaskbb.utils.settings import flaskbb_config
-from .locals import current_topic, current_forum, current_category
+
+from .locals import current_category, current_forum, current_topic
 from .utils import force_login_if_needed
 
+
 impl = HookimplMarker("flaskbb")
 
 logger = logging.getLogger(__name__)
@@ -292,8 +294,8 @@ class EditTopic(MethodView):
     decorators = [
         login_required,
         allows.requires(
-            CanAccessForum(),
             CanPostTopic,
+            CanEditPost,
             on_fail=FlashAndRedirect(
                 message=_("You are not allowed to edit that topic"),
                 level="warning",
@@ -305,6 +307,7 @@ class EditTopic(MethodView):
     def get(self, topic_id, slug=None):
         topic = Topic.query.filter_by(id=topic_id).first_or_404()
         form = self.form(obj=topic.first_post, title=topic.title)
+        form.track_topic.data = current_user.is_tracking_topic(topic)
 
         return render_template(
             "forum/new_topic.html", forum=topic.forum, form=form, edit_mode=True
@@ -316,10 +319,15 @@ class EditTopic(MethodView):
         form = self.form(obj=post, title=topic.title)
 
         if form.validate_on_submit():
-            form.populate_obj(topic)
-            form.populate_obj(post)
+            form.populate_obj(topic, post)
             post.date_modified = time_utcnow()
             post.modified_by = real(current_user).username
+
+            if form.track_topic.data:
+                current_user.track_topic(topic)
+            else:
+                current_user.untrack_topic(topic)
+
             post.save()
             topic.save()
             return redirect(url_for("forum.view_topic", topic_id=topic.id))
@@ -330,7 +338,7 @@ class EditTopic(MethodView):
 
     def form(self, **kwargs):
         current_app.pluggy.hook.flaskbb_form_new_topic(form=NewTopicForm)
-        return NewTopicForm(**kwargs)
+        return EditTopicForm(**kwargs)
 
 
 class ManageForum(MethodView):
@@ -570,10 +578,11 @@ class EditPost(MethodView):
     def get(self, post_id):
         post = Post.query.filter_by(id=post_id).first_or_404()
 
-        if post.topic.first_post_id == post.id:
+        if post.is_first_post():
             return redirect(url_for("forum.edit_topic", topic_id=post.topic_id))
 
         form = self.form(obj=post)
+        form.track_topic.data = current_user.is_tracking_topic(post.topic)
 
         return render_template(
             "forum/new_post.html", topic=post.topic, form=form, edit_mode=True
@@ -587,6 +596,10 @@ class EditPost(MethodView):
             form.populate_obj(post)
             post.date_modified = time_utcnow()
             post.modified_by = real(current_user).username
+
+            if not form.track_topic.data and current_user.is_tracking_topic(post.topic):
+                current_user.untrack_topic(post.topic)
+
             post.save()
             return redirect(url_for("forum.view_post", post_id=post.id))
 

+ 1 - 1
flaskbb/templates/auth/login.html

@@ -2,7 +2,7 @@
 {% set active_forum_nav=True %}
 
 {% extends theme("layout.html") %}
-{% from theme("macros.html") import horizontal_field, render_boolean_field %}
+{% from theme("macros.html") import horizontal_field %}
 {% block content %}
 
 <div class="panel page-panel">

+ 3 - 1
flaskbb/templates/forum/new_post.html

@@ -10,7 +10,7 @@
 {% extends theme("layout.html") %}
 
 {% block content %}
-{% from theme("macros.html") import render_quickreply, render_submit_field, render_field %}
+{% from theme("macros.html") import render_field, render_submit_field, render_quickreply, render_boolean_field %}
 
 <div class="page-view">
     <ol class="breadcrumb flaskbb-breadcrumb">
@@ -43,6 +43,8 @@
                                     {{ render_quickreply(form.content, div_class="new-message", rows=7, cols=75, placeholder="", **{'data-provide': 'markdown', 'data-autofocus': 'false', 'class': 'flaskbb-editor'}) }}
                                 </div>
 
+                                {{ render_boolean_field(form.track_topic) }}
+
                                 {{ run_hook("flaskbb_tpl_form_new_post_after", form=form) }}
 
                                 <div class="editor-submit">

+ 3 - 1
flaskbb/templates/forum/new_topic.html

@@ -10,7 +10,7 @@
 {% extends theme("layout.html") %}
 
 {% block content %}
-{% from theme("macros.html") import render_field, render_submit_field, render_quickreply %}
+{% from theme("macros.html") import render_field, render_submit_field, render_quickreply, render_boolean_field %}
 
 <div class="page-view">
     <ol class="breadcrumb flaskbb-breadcrumb">
@@ -42,6 +42,8 @@
                                     {{ render_quickreply(form.content, div_class="new-message", rows=7, cols=75, placeholder="", **{'data-provide': 'markdown', 'data-autofocus': 'false', 'class': 'flaskbb-editor'}) }}
                                 </div>
 
+                                {{ render_boolean_field(form.track_topic) }}
+
                                 {{ run_hook("flaskbb_tpl_form_new_topic_after", form=form) }}
 
                                 <div class="editor-submit">