Browse Source

Post/thread edition

Rafał Pitoń 10 years ago
parent
commit
50d958fc6e

+ 1 - 0
misago/conf/defaults.py

@@ -75,6 +75,7 @@ PIPELINE_JS = {
             'misago/js/misago-threads-lists.js',
             'misago/js/misago-threads-lists.js',
             'misago/js/misago-onebox.js',
             'misago/js/misago-onebox.js',
             'misago/js/misago-posting.js',
             'misago/js/misago-posting.js',
+            'misago/js/misago-posts.js',
         ),
         ),
         'output_filename': 'misago.js',
         'output_filename': 'misago.js',
     },
     },

+ 28 - 8
misago/static/misago/css/misago/posts.less

@@ -46,6 +46,23 @@
         }
         }
       }
       }
     }
     }
+
+    &.highlighted {
+      .media-body {
+        .panel-default {
+          background: @post-active-panel-bg;
+          border-color: @post-active-panel-border;
+
+          &:after {
+            border-right-color: @post-active-panel-bg;
+          }
+
+          &:before {
+            border-right-color: @post-active-panel-border;
+          }
+        }
+      }
+    }
   }
   }
 }
 }
 
 
@@ -116,29 +133,31 @@
       }
       }
 
 
       .label {
       .label {
-      	position: relative;
-      	bottom: 2px;
+        position: relative;
+        bottom: 2px;
       }
       }
     }
     }
   }
   }
 }
 }
 
 
+
 // Post alert
 // Post alert
 //
 //
 //==
 //==
 .posts-list {
 .posts-list {
   .panel {
   .panel {
-  	.alert {
-  		border: none;
-  		border-radius: 0px;
+    .alert {
+      border: none;
+      border-radius: 0px;
       margin-top: @line-height-computed / 2;
       margin-top: @line-height-computed / 2;
       margin-bottom: 0;
       margin-bottom: 0;
 
 
-  		text-align: center;
-  	}
+      text-align: center;
+    }
   }
   }
 }
 }
 
 
+
 // Post body
 // Post body
 //
 //
 //==
 //==
@@ -152,6 +171,7 @@
   }
   }
 }
 }
 
 
+
 // Post footer
 // Post footer
 //
 //
 //==
 //==
@@ -160,7 +180,7 @@
     .panel-footer {
     .panel-footer {
       background: none;
       background: none;
       border-top: none;
       border-top: none;
-      margin-top: @line-height-computed * -0.5;
+      margin-top: @line-height-computed * -1.25;
     }
     }
   }
   }
 }
 }

+ 5 - 2
misago/static/misago/css/misago/variables.less

@@ -316,8 +316,11 @@
 @user-card-active-shadow:              @state-clicked;
 @user-card-active-shadow:              @state-clicked;
 
 
 //** Post panels
 //** Post panels
-@post-panel-bg:               @panel-bg;
-@post-panel-border:           @panel-default-border;
+@post-panel-bg:                        @panel-bg;
+@post-panel-border:                    @panel-default-border;
+
+@post-active-panel-bg:                 lighten(#f1c40f, 48%);
+@post-active-panel-border:             lighten(#f1c40f, 40%);
 
 
 
 
 //== Form states and alerts
 //== Form states and alerts

+ 9 - 0
misago/static/misago/css/ranks.less

@@ -32,3 +32,12 @@
     }
     }
   }
   }
 }
 }
+
+
+.posts-list {
+  .post.rank-team {
+    .user-rank {
+      background-color: fadeOut(@brand-accent, 10%);
+    }
+  }
+}

+ 42 - 19
misago/static/misago/js/misago-posting.js

@@ -12,6 +12,9 @@ $(function() {
 
 
     if (this.$markup.text() == '') {
     if (this.$markup.text() == '') {
       this.$markup.hide();
       this.$markup.hide();
+    } else {
+      this.$message.hide();
+      Misago.Onebox.activate(this.$markup);
     }
     }
 
 
     this.api_url = options.api_url;
     this.api_url = options.api_url;
@@ -106,6 +109,7 @@ $(function() {
       this.affix_end = 0;
       this.affix_end = 0;
 
 
       this.on_cancel = null;
       this.on_cancel = null;
+      this.on_post = null;
 
 
     }
     }
 
 
@@ -129,6 +133,12 @@ $(function() {
         this.on_cancel = null;
         this.on_cancel = null;
       }
       }
 
 
+      if (options.on_post !== undefined) {
+        this.on_post = options.on_post
+      } else {
+        this.on_post = null;
+      }
+
       this.$ajax_loader = this.$container.find('.ajax-loader');
       this.$ajax_loader = this.$container.find('.ajax-loader');
       this.$ajax_complete = this.$container.find('.ajax-complete');
       this.$ajax_complete = this.$container.find('.ajax-complete');
 
 
@@ -150,13 +160,22 @@ $(function() {
             if (data.post_url !== undefined) {
             if (data.post_url !== undefined) {
               _this.posted = true;
               _this.posted = true;
               _this.$ajax_loader.hide();
               _this.$ajax_loader.hide();
-              _this.$ajax_complete.addClass('in')
 
 
-              if (data.post_url.indexOf(window.location.pathname) != -1) {
-                window.location.href = data.post_url;
-                window.location.reload(true)
-              } else {
-                window.location.href = data.post_url;
+              var on_post = true;
+
+              if (_this.on_post != null) {
+                on_post = _this.on_post(data);
+              }
+
+              if (on_post) {
+                _this.$ajax_complete.addClass('in')
+
+                if (data.post_url.indexOf(window.location.pathname) != -1) {
+                  window.location.href = data.post_url;
+                  window.location.reload(true)
+                } else {
+                  window.location.href = data.post_url;
+                }
               }
               }
             } else if (data.errors !== undefined) {
             } else if (data.errors !== undefined) {
               Misago.Alerts.error(data.errors[0]);
               Misago.Alerts.error(data.errors[0]);
@@ -174,17 +193,7 @@ $(function() {
       })
       })
 
 
       this.$container.find('button[name="cancel"]').click(function() {
       this.$container.find('button[name="cancel"]').click(function() {
-
-        if (_this.has_content()) {
-          var decision = confirm(lang_dismiss_editor);
-          if (decision) {
-            _this.cancel();
-          }
-        } else {
-          _this.cancel();
-        }
-
-        return false;
+        _this.cancel();
       });
       });
 
 
       return true;
       return true;
@@ -214,6 +223,13 @@ $(function() {
 
 
       if (this.$form !== null) {
       if (this.$form !== null) {
 
 
+        if (this.has_content() && !this.posted) {
+          var decision = confirm(lang_dismiss_editor);
+          if (!decision) {
+            return false;
+          }
+        }
+
         if (this.$preview !== null) {
         if (this.$preview !== null) {
           this.$preview.stop();
           this.$preview.stop();
         }
         }
@@ -223,13 +239,20 @@ $(function() {
           $('.main-footer').show();
           $('.main-footer').show();
         });
         });
 
 
-        if (this.on_cancel !== undefined) {
+        if (this.on_cancel !== null) {
           this.on_cancel();
           this.on_cancel();
         }
         }
 
 
         this._clear();
         this._clear();
+        return true;
       }
       }
 
 
+      return false;
+
+    }
+
+    this.is_open = function() {
+      return this.$form !== null;
     }
     }
 
 
     this.has_content = function() {
     this.has_content = function() {
@@ -249,7 +272,7 @@ $(function() {
   Misago.Posting = new MisagoPosting();
   Misago.Posting = new MisagoPosting();
 
 
   $(window).on("beforeunload", function() {
   $(window).on("beforeunload", function() {
-    if (Misago.Posting.has_content() && !Misago.Posting.posted) {
+    if (Misago.Posting.is_open() && Misago.Posting.has_content() && !Misago.Posting.posted) {
       return lang_dismiss_editor;
       return lang_dismiss_editor;
     }
     }
   })
   })

+ 72 - 0
misago/static/misago/js/misago-posts.js

@@ -0,0 +1,72 @@
+// Controller for posts lists
+$(function() {
+
+  MisagoPost = function($element) {
+
+    this.$e = $element;
+    this.$content = this.$e.find('.post-body');
+    this.id = this.$e.data('id');
+
+    var _this = this;
+
+    this.highlight = function() {
+      $element.addClass('highlighted');
+    }
+
+    this.remove_highlight = function() {
+      $element.removeClass('highlighted');
+    }
+
+    this.change_post = function(new_content) {
+      this.$content.fadeTo('fast', 0.1, function() {
+        _this.$content.html(new_content);
+        Misago.Onebox.activate(_this.$content);
+        Misago.DOM.changed();
+
+        _this.$content.fadeTo('fast', 1);
+      });
+    }
+
+    this.$e.find('.btn-edit').click(function() {
+
+      if (!Misago.Posting.is_open() || Misago.Posting.cancel()) {
+        Misago.Posting.load({
+          api_url: _this.$e.data('edit-url'),
+          on_load: function() {
+            _this.highlight();
+          },
+          on_cancel: function() {
+            _this.remove_highlight();
+          },
+          on_post: function(data) {
+            Misago.Alerts.success(data.message);
+            _this.change_post(data.parsed);
+            Misago.Posting.cancel();
+            return false;
+          }
+        });
+      }
+
+    });
+
+  }
+
+  MisagoPosts = function() {
+
+    this.posts = {};
+
+    var _this = this;
+
+    this.discover_posts = function() {
+      $('.thread-post').each(function() {
+        var id = $(this).data('id');
+        _this.posts[id] = new MisagoPost($(this));
+      });
+    }
+    this.discover_posts();
+
+  }
+
+  Misago.Posts = new MisagoPosts();
+
+});

+ 2 - 2
misago/templates/misago/posting/replyform.html

@@ -1,4 +1,4 @@
-{% load i18n misago_editor misago_forms %}
+{% load i18n misago_editor misago_forms misago_shorthands %}
 {% include 'misago/form_errors.html' %}
 {% include 'misago/form_errors.html' %}
 
 
 {% if form.title %}
 {% if form.title %}
@@ -32,7 +32,7 @@
         <p class="lead empty-message">
         <p class="lead empty-message">
           {% trans "Once you start writing mesage, it's preview will be displayed here." %}
           {% trans "Once you start writing mesage, it's preview will be displayed here." %}
         </p>
         </p>
-        <article class="misago-markup"></article>
+        <article class="misago-markup">{{ post.is_valid|iftrue:post.parsed|safe }}</article>
       </div>
       </div>
       <p class="preview-footer text-muted small">
       <p class="preview-footer text-muted small">
         <span class="fa fa-refresh fa-fw"></span>
         <span class="fa fa-refresh fa-fw"></span>

+ 15 - 14
misago/templates/misago/thread/post.html

@@ -1,5 +1,5 @@
 {% load i18n misago_avatars %}
 {% load i18n misago_avatars %}
-<div id="post-{{ post.pk }}" class="media post">
+<div id="post-{{ post.pk }}" class="media post thread-post{% if post.poster_id and post.poster.rank.css_class %} rank-{{ post.poster.rank.css_class }}{% endif %}" data-id="{{ post.pk }}" data-edit-url="{{ post.get_edit_url }}"=>
   {% if post.poster %}
   {% if post.poster %}
   <a class="user-avatar pull-left" href="{% url USER_PROFILE_URL user_slug=post.poster.slug user_id=post.poster.id %}">
   <a class="user-avatar pull-left" href="{% url USER_PROFILE_URL user_slug=post.poster.slug user_id=post.poster.id %}">
     <img class="media-object" src="{{ post.poster|avatar:100 }}" alt="{% trans "Poster avatar" %}">
     <img class="media-object" src="{{ post.poster|avatar:100 }}" alt="{% trans "Poster avatar" %}">
@@ -17,6 +17,9 @@
         {% include "misago/user_state.html" with user=post.poster state=post.poster.online_state %}
         {% include "misago/user_state.html" with user=post.poster state=post.poster.online_state %}
         <a class="user-name" href="{% url USER_PROFILE_URL user_slug=post.poster.slug user_id=post.poster.id %}">
         <a class="user-name" href="{% url USER_PROFILE_URL user_slug=post.poster.slug user_id=post.poster.id %}">
           {{ post.poster }}</a>
           {{ post.poster }}</a>
+        {% if post.poster.short_title %}
+        <span class="label user-rank">{{ post.poster.short_title }}</span>
+        {% endif %}
         {% else %}
         {% else %}
         <span class="fa fa-power-off fa-fw user-offline tooltip-top" title="{% blocktrans with user=post.poster_name %}{{ user }}'s forum account has been deleted.{% endblocktrans %}"></span>
         <span class="fa fa-power-off fa-fw user-offline tooltip-top" title="{% blocktrans with user=post.poster_name %}{{ user }}'s forum account has been deleted.{% endblocktrans %}"></span>
         <span class="user-name">
         <span class="user-name">
@@ -61,34 +64,32 @@
       </div>
       </div>
       {% endif %}
       {% endif %}
 
 
+      {% if post.is_valid %}
       <div class="panel-body">
       <div class="panel-body">
-
-        {% if post.is_valid %}
         <article class="post-body misago-markup">
         <article class="post-body misago-markup">
           {{ post.parsed|safe }}
           {{ post.parsed|safe }}
         <article>
         <article>
-        {% else %}
-        <p class="lead corrupted-message">
-          <span class="fa fa-exclamation-triangle"></span>
-          {% trans "Post can't be displayed due to invalid message checksum." %}
-        </p>
-        {% endif %}
-
       </div>
       </div>
+      {% else %}
+      <div class="alert alert-danger">
+        <span class="fa fa-exclamation-triangle fa-fw fa-lg"></span>
+        {% trans "Post can't be displayed due to invalid message checksum." %}
+      </div>
+      {% endif %}
 
 
       <div class="panel-footer">
       <div class="panel-footer">
 
 
         {% if thread.acl.can_reply %}
         {% if thread.acl.can_reply %}
-        <button type="button" class="btn btn-primary btn-flat pull-right">
+        <button type="button" class="btn btn-reply btn-primary btn-flat pull-right">
           <span class="fa fa-reply">
           <span class="fa fa-reply">
-          Reply
+          {% trans "Reply" %}
         </button>
         </button>
         {% endif %}
         {% endif %}
 
 
         {% if post.acl.can_edit %}
         {% if post.acl.can_edit %}
-        <button type="button" class="btn btn-default btn-flat pull-right">
+        <button type="button" class="btn btn-edit btn-default btn-flat pull-right">
           <span class="fa fa-pencil">
           <span class="fa fa-pencil">
-          Edit
+          {% trans "Edit" %}
         </button>
         </button>
         {% endif %}
         {% endif %}
 
 

+ 7 - 0
misago/threads/models/post.py

@@ -86,6 +86,13 @@ class Post(models.Model):
             'post_id': self.id
             'post_id': self.id
         })
         })
 
 
+    def get_edit_url(self):
+        return reverse('misago:edit_post', kwargs={
+            'forum_id': self.forum_id,
+            'thread_id': self.thread_id,
+            'post_id': self.id
+        })
+
     @property
     @property
     def short(self):
     def short(self):
         if self.is_valid:
         if self.is_valid:

+ 7 - 5
misago/threads/permissions.py

@@ -293,7 +293,7 @@ def add_acl_to_thread(user, thread):
         'can_close': forum_acl.get('can_close_threads'),
         'can_close': forum_acl.get('can_close_threads'),
         'can_move': forum_acl.get('can_move_threads'),
         'can_move': forum_acl.get('can_move_threads'),
         'can_review': forum_acl.get('can_review_moderated_content'),
         'can_review': forum_acl.get('can_review_moderated_content'),
-        'can_report': forum_acl.get('can_report'),
+        'can_report': forum_acl.get('can_report_content'),
         'can_see_reports': forum_acl.get('can_see_reports')
         'can_see_reports': forum_acl.get('can_see_reports')
     })
     })
 
 
@@ -302,7 +302,8 @@ def add_acl_to_thread(user, thread):
             can_change_label = forum_acl.get('can_change_threads_labels') == 1
             can_change_label = forum_acl.get('can_change_threads_labels') == 1
             thread.acl['can_change_label'] = can_change_label
             thread.acl['can_change_label'] = can_change_label
         if not thread.acl['can_hide']:
         if not thread.acl['can_hide']:
-            thread.acl['can_hide'] = forum_acl.get('can_hide_own_threads')
+            if not thread.replies:
+                thread.acl['can_hide'] = forum_acl.get('can_hide_own_threads')
 
 
 
 
 def add_acl_to_post(user, post):
 def add_acl_to_post(user, post):
@@ -314,9 +315,10 @@ def add_acl_to_post(user, post):
         'can_unhide': forum_acl.get('can_hide_threads'),
         'can_unhide': forum_acl.get('can_hide_threads'),
         'can_hide': forum_acl.get('can_hide_threads'),
         'can_hide': forum_acl.get('can_hide_threads'),
         'can_delete': forum_acl.get('can_hide_threads'),
         'can_delete': forum_acl.get('can_hide_threads'),
-        'can_protect': can_reply_thread(user, post.thread),
-        'can_report': can_reply_thread(user, post.thread),
-        'can_approve': can_reply_thread(user, post.thread),
+        'can_protect': forum_acl.get('can_protect_posts'),
+        'can_report': forum_acl.get('can_report_content'),
+        'can_see_reports': forum_acl.get('can_see_reports'),
+        'can_approve': forum_acl.get('can_review_moderated_content'),
     })
     })
 
 
 
 

+ 1 - 1
misago/threads/posting/recordedit.py

@@ -9,7 +9,7 @@ class RecordEditMiddleware(PostingMiddleware):
 
 
         if self.mode == EDIT:
         if self.mode == EDIT:
             self.original_title = self.thread.title
             self.original_title = self.thread.title
-            self.original_post = self.post.raw
+            self.original_post = self.post.original
 
 
     def save(self, form):
     def save(self, form):
         if self.mode == EDIT:
         if self.mode == EDIT:

+ 21 - 11
misago/threads/posting/reply.py

@@ -2,6 +2,7 @@ from misago.markup import Editor
 
 
 from misago.threads.checksums import update_post_checksum
 from misago.threads.checksums import update_post_checksum
 from misago.threads.forms.posting import ReplyForm, ThreadForm
 from misago.threads.forms.posting import ReplyForm, ThreadForm
+from misago.threads.permissions import can_edit_thread
 from misago.threads.posting import PostingMiddleware, START, REPLY, EDIT
 from misago.threads.posting import PostingMiddleware, START, REPLY, EDIT
 
 
 
 
@@ -9,16 +10,26 @@ class ReplyFormMiddleware(PostingMiddleware):
     def make_form(self):
     def make_form(self):
         initial_data = {'title': self.thread.title, 'post': self.post.original}
         initial_data = {'title': self.thread.title, 'post': self.post.original}
 
 
-        if self.mode == START:
+        if self.mode == EDIT:
+            if can_edit_thread(self.user, self.thread):
+                FormType = ThreadForm
+            else:
+                FormType = FormType
+        elif self.mode == START:
+            FormType = ThreadForm
+        else:
+            FormType = FormType
+
+        if FormType == ThreadForm:
             if self.request.method == 'POST':
             if self.request.method == 'POST':
                 form = ThreadForm(self.thread, self.post, self.request.POST)
                 form = ThreadForm(self.thread, self.post, self.request.POST)
             else:
             else:
                 form = ThreadForm(self.thread, self.post, initial=initial_data)
                 form = ThreadForm(self.thread, self.post, initial=initial_data)
         else:
         else:
             if self.request.method == 'POST':
             if self.request.method == 'POST':
-                form = ReplyForm(self.post, self.request.POST)
+                form = FormType(self.post, self.request.POST)
             else:
             else:
-                form = ReplyForm(self.post, initial=initial_data)
+                form = FormType(self.post, initial=initial_data)
 
 
         form.post_editor = Editor(form['post'])
         form.post_editor = Editor(form['post'])
         return form
         return form
@@ -32,14 +43,12 @@ class ReplyFormMiddleware(PostingMiddleware):
             self.new_thread(form)
             self.new_thread(form)
 
 
         if self.mode == EDIT:
         if self.mode == EDIT:
-            self.edit_post()
+            self.edit_post(form)
         else:
         else:
             self.new_post()
             self.new_post()
 
 
         self.post.updated_on = self.datetime
         self.post.updated_on = self.datetime
-
-        if self.mode != EDIT:
-            self.post.save()# We need post id for checksum
+        self.post.save()
 
 
         update_post_checksum(self.post)
         update_post_checksum(self.post)
         self.post.update_fields.append('checksum')
         self.post.update_fields.append('checksum')
@@ -54,10 +63,11 @@ class ReplyFormMiddleware(PostingMiddleware):
         self.thread.last_post_on = self.datetime
         self.thread.last_post_on = self.datetime
         self.thread.save()
         self.thread.save()
 
 
-    def edit_post(self):
-        self.post.last_editor_name = self.user
-        self.post.poster_name = self.user.username
-        self.post.poster_slug = self.user.slug
+    def edit_post(self, form):
+        if form.cleaned_data.get('title'):
+            self.thread.set_title(form.cleaned_data['title'])
+            self.thread.update_fields.extend(('title', 'slug'))
+        self.post.last_editor_name = self.user.username
 
 
     def new_post(self):
     def new_post(self):
         self.post.thread = self.thread
         self.post.thread = self.thread

+ 4 - 3
misago/threads/urls.py

@@ -33,10 +33,11 @@ urlpatterns += patterns('',
 )
 )
 
 
 
 
-from misago.threads.views.threads import StartThreadView, ReplyView, EditView
+from misago.threads.views.posting import PostingView
 urlpatterns += patterns('',
 urlpatterns += patterns('',
-    url(r'^start-thread/(?P<forum_id>\d+)/$', StartThreadView.as_view(), name='start_thread'),
-    url(r'^reply-thread/(?P<forum_id>\d+)/(?P<thread_id>\d+)/$', ReplyView.as_view(), name='reply_thread'),
+    url(r'^start-thread/(?P<forum_id>\d+)/$', PostingView.as_view(), name='start_thread'),
+    url(r'^reply-thread/(?P<forum_id>\d+)/(?P<thread_id>\d+)/$', PostingView.as_view(), name='reply_thread'),
+    url(r'^edit-post/(?P<forum_id>\d+)/(?P<thread_id>\d+)/(?P<post_id>\d+)/edit/$', PostingView.as_view(), name='edit_post'),
 )
 )
 
 
 
 

+ 0 - 1
misago/threads/views/generic/__init__.py

@@ -3,7 +3,6 @@ from misago.threads.views.generic.base import *
 from misago.threads.views.generic.goto import *
 from misago.threads.views.generic.goto import *
 from misago.threads.views.generic.gotopostslist import *
 from misago.threads.views.generic.gotopostslist import *
 from misago.threads.views.generic.post import *
 from misago.threads.views.generic.post import *
-from misago.threads.views.generic.posting import *
 from misago.threads.views.generic.thread import *
 from misago.threads.views.generic.thread import *
 from misago.threads.views.generic.threads import *
 from misago.threads.views.generic.threads import *
 from misago.threads.views.generic.forum import *
 from misago.threads.views.generic.forum import *

+ 32 - 9
misago/threads/views/generic/base.py

@@ -46,6 +46,7 @@ class ThreadMixin(object):
     """
     """
     def get_thread(self, request, lock=False, **kwargs):
     def get_thread(self, request, lock=False, **kwargs):
         thread = self.fetch_thread(request, lock, **kwargs)
         thread = self.fetch_thread(request, lock, **kwargs)
+        self.check_forum_permissions(request, thread.forum)
         self.check_thread_permissions(request, thread)
         self.check_thread_permissions(request, thread)
 
 
         if kwargs.get('thread_slug'):
         if kwargs.get('thread_slug'):
@@ -58,10 +59,16 @@ class ThreadMixin(object):
         queryset = queryset or Thread.objects
         queryset = queryset or Thread.objects
         if lock:
         if lock:
             queryset = queryset.select_for_update()
             queryset = queryset.select_for_update()
-        if select_related:
-            queryset = queryset.select_related(*select_related)
 
 
-        return get_object_or_404(queryset, id=kwargs.get('thread_id'))
+        select_related = select_related or []
+        if not 'forum' in select_related:
+            select_related.append('forum')
+        queryset = queryset.select_related(*select_related)
+
+        where = {'id': kwargs.get('thread_id')}
+        if 'forum_id' in kwargs:
+            where['forum_id'] = kwargs.get('forum_id')
+        return get_object_or_404(queryset, **where)
 
 
     def check_thread_permissions(self, request, thread):
     def check_thread_permissions(self, request, thread):
         add_acl(request.user, thread)
         add_acl(request.user, thread)
@@ -70,20 +77,36 @@ class ThreadMixin(object):
 
 
 class PostMixin(object):
 class PostMixin(object):
     def get_post(self, request, lock=False, **kwargs):
     def get_post(self, request, lock=False, **kwargs):
-        thread = self.fetch_post(request, lock, **kwargs)
-        self.check_post_permissions(request, thread)
+        post = self.fetch_post(request, lock, **kwargs)
 
 
-        return thread
+        post.thread.forum = post.forum
+
+        self.check_forum_permissions(request, post.forum)
+        self.check_thread_permissions(request, post.thread)
+        self.check_post_permissions(request, post)
+
+        return post
 
 
     def fetch_post(self, request, lock=False, select_related=None,
     def fetch_post(self, request, lock=False, select_related=None,
                    queryset=None, **kwargs):
                    queryset=None, **kwargs):
         queryset = queryset or Post.objects
         queryset = queryset or Post.objects
         if lock:
         if lock:
             queryset = queryset.select_for_update()
             queryset = queryset.select_for_update()
-        if select_related:
-            queryset = queryset.select_related(*select_related)
 
 
-        return get_object_or_404(queryset, id=kwargs.get('post_id'))
+        select_related = select_related or []
+        if not 'forum' in select_related:
+            select_related.append('forum')
+        if not 'thread' in select_related:
+            select_related.append('thread')
+        queryset = queryset.select_related(*select_related)
+
+        where = {'id': kwargs.get('post_id')}
+        if 'thread_id' in kwargs:
+            where['thread_id'] = kwargs.get('thread_id')
+        if 'forum_id' in kwargs:
+            where['forum_id'] = kwargs.get('forum_id')
+
+        return get_object_or_404(queryset, **where)
 
 
     def check_post_permissions(self, request, post):
     def check_post_permissions(self, request, post):
         add_acl(request.user, post)
         add_acl(request.user, post)

+ 33 - 20
misago/threads/views/generic/posting.py → misago/threads/views/posting.py

@@ -12,14 +12,15 @@ from misago.threads import goto
 from misago.threads.posting import (PostingInterrupt, EditorFormset,
 from misago.threads.posting import (PostingInterrupt, EditorFormset,
                                     START, REPLY, EDIT)
                                     START, REPLY, EDIT)
 from misago.threads.models import Thread, Post, Label
 from misago.threads.models import Thread, Post, Label
-from misago.threads.permissions import allow_start_thread, allow_reply_thread
+from misago.threads.permissions import (allow_start_thread, allow_reply_thread,
+                                        can_edit_post)
 from misago.threads.views.generic.base import ViewBase
 from misago.threads.views.generic.base import ViewBase
 
 
 
 
-__all__ = ['EditorView']
+__all__ = ['PostingView']
 
 
 
 
-class EditorView(ViewBase):
+class PostingView(ViewBase):
     """
     """
     Basic view for starting/replying/editing
     Basic view for starting/replying/editing
     """
     """
@@ -33,17 +34,25 @@ class EditorView(ViewBase):
         if is_submit:
         if is_submit:
             request.user.lock()
             request.user.lock()
 
 
-        forum = self.get_forum(request, lock=is_submit, **kwargs)
-
+        forum = None
         thread = None
         thread = None
         post = None
         post = None
 
 
-        if 'thread_id' in kwargs:
-            thread = self.get_thread(
-                request, lock=is_submit, queryset=forum.thread_set, **kwargs)
+        if 'post_id' in kwargs:
+            post = self.get_post(request, lock=is_submit, **kwargs)
+            forum = post.forum
+            thread = post.thread
+        elif 'thread_id' in kwargs:
+            thread = self.get_thread(request, lock=is_submit, **kwargs)
+            forum = thread.forum
+        else:
+            forum = self.get_forum(request, lock=is_submit, **kwargs)
 
 
         if thread:
         if thread:
-            mode = REPLY
+            if post:
+                mode = EDIT
+            else:
+                mode = REPLY
         else:
         else:
             mode = START
             mode = START
             thread = Thread(forum=forum)
             thread = Thread(forum=forum)
@@ -60,18 +69,18 @@ class EditorView(ViewBase):
         if mode == START:
         if mode == START:
             self.allow_start(user, forum)
             self.allow_start(user, forum)
         if mode == REPLY:
         if mode == REPLY:
-            self.allow_reply(user, forum, thread)
+            self.allow_reply(user, thread)
         if mode == EDIT:
         if mode == EDIT:
-            self.allow_edit(user, forum, thread, post)
+            self.allow_edit(user, post)
 
 
     def allow_start(self, user, forum):
     def allow_start(self, user, forum):
         allow_start_thread(user, forum)
         allow_start_thread(user, forum)
 
 
-    def allow_reply(self, user, forum, thread):
+    def allow_reply(self, user, thread):
         allow_reply_thread(user, thread)
         allow_reply_thread(user, thread)
 
 
-    def allow_edit(self, user, forum, thread, post):
-        raise NotImplementedError()
+    def allow_edit(self, user, post):
+        can_edit_post(user, post)
 
 
     def dispatch(self, request, *args, **kwargs):
     def dispatch(self, request, *args, **kwargs):
         if request.method == 'POST':
         if request.method == 'POST':
@@ -104,16 +113,20 @@ class EditorView(ViewBase):
                     try:
                     try:
                         formset.save()
                         formset.save()
 
 
-                        if mode == START:
-                            message = _("New thread was posted.")
-                        if mode == REPLY:
-                            message = _("Your reply was posted.")
                         if mode == EDIT:
                         if mode == EDIT:
                             message = _("Message was edited.")
                             message = _("Message was edited.")
-                        messages.success(request, message)
+                        else:
+                            if mode == START:
+                                message = _("New thread was posted.")
+                            if mode == REPLY:
+                                message = _("Your reply was posted.")
+                            messages.success(request, message)
 
 
                         return JsonResponse({
                         return JsonResponse({
-                            'post_url': goto.post(request.user, thread, post)
+                            'message': message,
+                            'post_url': goto.post(request.user, thread, post),
+                            'parsed': post.parsed,
+                            'original': post.original,
                         })
                         })
                     except PostingInterrupt as e:
                     except PostingInterrupt as e:
                         return JsonResponse({'interrupt': e.message})
                         return JsonResponse({'interrupt': e.message})

+ 0 - 12
misago/threads/views/threads.py

@@ -31,15 +31,3 @@ class GotoNewView(ThreadsMixin, generic.GotoNewView):
 
 
 class GotoPostView(ThreadsMixin, generic.GotoPostView):
 class GotoPostView(ThreadsMixin, generic.GotoPostView):
     pass
     pass
-
-
-class StartThreadView(ThreadsMixin, generic.EditorView):
-    pass
-
-
-class ReplyView(ThreadsMixin, generic.EditorView):
-    pass
-
-
-class EditView(ThreadsMixin, generic.EditorView):
-    pass