From c11f21ab3c64c6ef9d9cde8bfdcddf12f6663932 Mon Sep 17 00:00:00 2001 From: Joar Wandborg Date: Mon, 27 Jun 2011 23:39:40 +0200 Subject: [PATCH 1/8] Issue 362 - Add simple comments * Added MediaComment database model Holds `media_entry` (`ObjectId`), `author` (`ObjectId`), `created`, `content` and `content_html`. --- mediagoblin/db/models.py | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/mediagoblin/db/models.py b/mediagoblin/db/models.py index 600b79ff..5d00aa34 100644 --- a/mediagoblin/db/models.py +++ b/mediagoblin/db/models.py @@ -147,8 +147,32 @@ class MediaEntry(Document): def uploader(self): return self.db.User.find_one({'_id': self['uploader']}) +class MediaComment(Document): + __collection__ = 'media_comments' -REGISTER_MODELS = [MediaEntry, User] + structure = { + 'media_entry': ObjectId, + 'author': ObjectId, + 'created': datetime.datetime, + 'content': unicode, + 'content_html': unicode} + + required_fields = [ + 'author', 'created', 'content'] + + default_values = { + 'created': datetime.datetime.utcnow} + + def media_entry(self): + pass + + def author(self): + return self.db.User.find_one({'_id': self['author']}) + +REGISTER_MODELS = [ + MediaEntry, + User, + MediaComment] def register_models(connection): From 6a585e4c52f0c4efe47b33e70059303f24d68287 Mon Sep 17 00:00:00 2001 From: Joar Wandborg Date: Wed, 29 Jun 2011 01:14:52 +0200 Subject: [PATCH 2/8] Issue #362 - Added a route to MediaComment POST handler --- mediagoblin/user_pages/routing.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/mediagoblin/user_pages/routing.py b/mediagoblin/user_pages/routing.py index 92998726..255b6f66 100644 --- a/mediagoblin/user_pages/routing.py +++ b/mediagoblin/user_pages/routing.py @@ -27,4 +27,7 @@ user_routes = [ Route('mediagoblin.edit.edit_media', "/{user}/m/{media}/edit/", controller="mediagoblin.edit.views:edit_media"), Route('mediagoblin.user_pages.atom_feed', '/{user}/atom/', - controller="mediagoblin.user_pages.views:atom_feed")] + controller="mediagoblin.user_pages.views:atom_feed"), + Route('mediagoblin.user_pages.media_post_comment', + '/{user}/m/{media}/comment/add/', + controller="mediagoblin.user_pages.views:media_post_comment")] From 7bd8197f32f17466f14d730546a06166ed6da67a Mon Sep 17 00:00:00 2001 From: Joar Wandborg Date: Wed, 29 Jun 2011 01:16:51 +0200 Subject: [PATCH 3/8] Issue #362 - Updated the MediaComment model * `MediaComment.get_comments()` now uses pagination * `MediaComment.get_comments()` now sorts by `created` DESC * `MediaComment.media_entry` is now **required** * `MediaComment.media_entry()` now returns parent `MediaEntry` --- mediagoblin/db/models.py | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/mediagoblin/db/models.py b/mediagoblin/db/models.py index 5d00aa34..5196dede 100644 --- a/mediagoblin/db/models.py +++ b/mediagoblin/db/models.py @@ -22,7 +22,8 @@ from mediagoblin import util from mediagoblin.auth import lib as auth_lib from mediagoblin import mg_globals from mediagoblin.db import migrations -from mediagoblin.db.util import ObjectId +from mediagoblin.db.util import DESCENDING, ObjectId +from mediagoblin.util import Pagination ################### # Custom validators @@ -115,7 +116,22 @@ class MediaEntry(Document): def main_mediafile(self): pass - + + def get_comments(self, page): + cursor = self.db.MediaComment.find({ + 'media_entry': self['_id']}).sort('created', DESCENDING) + + pagination = Pagination(page, cursor) + comments = pagination() + + data = list() + for comment in comments: + comment['author'] = self.db.User.find_one({ + '_id': comment['author']}) + data.append(comment) + + return (data, pagination) + def generate_slug(self): self['slug'] = util.slugify(self['title']) @@ -158,13 +174,13 @@ class MediaComment(Document): 'content_html': unicode} required_fields = [ - 'author', 'created', 'content'] + 'media_entry', 'author', 'created', 'content'] default_values = { 'created': datetime.datetime.utcnow} def media_entry(self): - pass + return self.db.MediaEntry.find_one({'_id': self['media_entry']}) def author(self): return self.db.User.find_one({'_id': self['author']}) From aa7d1a2fb6148f6e899d7f8756e6c481dab3f532 Mon Sep 17 00:00:00 2001 From: Joar Wandborg Date: Wed, 29 Jun 2011 01:21:46 +0200 Subject: [PATCH 4/8] Issue #362 - Updated media.html with things necessary for the simple comment feature * `media.html` now imports `wtforms.html` for use in comment form rendering * `media.html` now outputs a commenting form if `request.user` evals to `True` * `media.html` now outputs `MediaComments` if `comments` evals to `True` * `media.html` now includes `pagination.html` to handle comment pagination --- .../mediagoblin/user_pages/media.html | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/mediagoblin/templates/mediagoblin/user_pages/media.html b/mediagoblin/templates/mediagoblin/user_pages/media.html index 97ff8e51..cd0bb764 100644 --- a/mediagoblin/templates/mediagoblin/user_pages/media.html +++ b/mediagoblin/templates/mediagoblin/user_pages/media.html @@ -16,6 +16,9 @@ # along with this program. If not, see . #} {% extends "mediagoblin/base.html" %} + +{% import "/mediagoblin/utils/wtforms.html" as wtforms_util %} + {% block mediagoblin_content %} {# temporarily, an "image gallery" that isn't one really ;) #} @@ -42,6 +45,49 @@ user= media.uploader().username, media= media._id) }}">Edit

{% endif %} + {% if request.user %} +
+

Post a comment!

+ {{ wtforms_util.render_field_div(comment_form.comment) }} +
+ +
+
+ {% endif %} + {# + {{ wtforms_util.render_textarea_div(submit_form.description) }} + {{ wtforms_util.render_field_div(submit_form.file) }} + #} + {% if comments %} +

Comments

+ {% for comment in comments %} + + {% endfor %} + {% include "mediagoblin/utils/pagination.html" %} + {% endif %} {% else %}

Sorry, no such media found.

{% endif %} From 9074ee7c1df9cca8a250cc837c3e3efb8e359649 Mon Sep 17 00:00:00 2001 From: Joar Wandborg Date: Wed, 29 Jun 2011 01:29:39 +0200 Subject: [PATCH 5/8] Issue #362 - Updated `mediagoblin.user_pages.views` to handle new "Simple comments" feature * ADDED `media_post_comment(request)` which creates `MediaComments` from POST requests * UPDATED `media_home(request, media, **kwargs)` now passes `comments`, `pagination` and `comments_form` to `media.html` --- mediagoblin/user_pages/views.py | 51 ++++++++++++++++++++++++++++----- 1 file changed, 44 insertions(+), 7 deletions(-) diff --git a/mediagoblin/user_pages/views.py b/mediagoblin/user_pages/views.py index d6cd6034..67a57d97 100644 --- a/mediagoblin/user_pages/views.py +++ b/mediagoblin/user_pages/views.py @@ -15,13 +15,18 @@ # along with this program. If not, see . from webob import exc -from mediagoblin.db.util import DESCENDING -from mediagoblin.util import Pagination, render_to_response +from mediagoblin.db.util import DESCENDING, ObjectId +from mediagoblin.util import Pagination, render_to_response, redirect, \ + clean_html +from mediagoblin.user_pages import forms as user_forms -from mediagoblin.decorators import uses_pagination, get_user_media_entry +from mediagoblin.decorators import uses_pagination, get_user_media_entry, \ + require_active_login from werkzeug.contrib.atom import AtomFeed +import markdown + @uses_pagination def user_home(request, page): """'Homepage' of a User()""" @@ -78,13 +83,45 @@ def user_gallery(request, page): @get_user_media_entry -def media_home(request, media): - """'Homepage' of a MediaEntry()""" +@uses_pagination +def media_home(request, media, **kwargs): + """ + 'Homepage' of a MediaEntry() + """ + + comment_form = user_forms.MediaCommentForm(request.POST) + + (comments, pagination) = media.get_comments(kwargs.get('page')) + return render_to_response( request, 'mediagoblin/user_pages/media.html', - {'media': media}) + {'media': media, + 'comments': comments, + 'pagination': pagination, + 'comment_form': comment_form}) +@require_active_login +def media_post_comment(request): + """ + recieves POST from a MediaEntry() comment form, saves the comment. + """ + comment = request.db.MediaComment() + comment['media_entry'] = ObjectId(request.matchdict['media']) + comment['author'] = request.user['_id'] + comment['content'] = request.POST['comment'] + + md = markdown.Markdown( + safe_mode = 'escape') + comment['content_html'] = clean_html( + md.convert( + comment['content'])) + + comment.save() + + return redirect(request, 'mediagoblin.user_pages.media_home', + media = request.matchdict['media'], + user = request.matchdict['user']) ATOM_DEFAULT_NR_OF_UPDATED_ITEMS = 5 @@ -117,4 +154,4 @@ def atom_feed(request): updated=entry.get('created'), url=entry.url_for_self(request.urlgen)) - return feed.get_response() + return feed.get_response() From 074ac832c3ccd22f58118d51606ede3a1e696b4c Mon Sep 17 00:00:00 2001 From: Joar Wandborg Date: Wed, 29 Jun 2011 01:35:43 +0200 Subject: [PATCH 6/8] Issue #362 - Added new `wtforms.Form`; `MediaCommentForm()` * ADDED `MediaCommentForm` is a form for `MediaComment` user entry and posting. --- mediagoblin/user_pages/forms.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 mediagoblin/user_pages/forms.py diff --git a/mediagoblin/user_pages/forms.py b/mediagoblin/user_pages/forms.py new file mode 100644 index 00000000..9f7d2fbd --- /dev/null +++ b/mediagoblin/user_pages/forms.py @@ -0,0 +1,21 @@ +# GNU MediaGoblin -- federated, autonomous media hosting +# Copyright (C) 2011 Free Software Foundation, Inc +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +import wtforms + +class MediaCommentForm(wtforms.Form): + comment = wtforms.TextAreaField('Comment', + [wtforms.validators.Required()]) \ No newline at end of file From 95e6da024d019c0f93d041aedc19b2bf6697a3fd Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Wed, 29 Jun 2011 20:41:52 -0500 Subject: [PATCH 7/8] Use the cleaned_markdown_conversion method instead of doing that manually --- mediagoblin/user_pages/views.py | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/mediagoblin/user_pages/views.py b/mediagoblin/user_pages/views.py index 67a57d97..d581f19c 100644 --- a/mediagoblin/user_pages/views.py +++ b/mediagoblin/user_pages/views.py @@ -16,8 +16,8 @@ from webob import exc from mediagoblin.db.util import DESCENDING, ObjectId -from mediagoblin.util import Pagination, render_to_response, redirect, \ - clean_html +from mediagoblin.util import ( + Pagination, render_to_response, redirect, cleaned_markdown_conversion) from mediagoblin.user_pages import forms as user_forms from mediagoblin.decorators import uses_pagination, get_user_media_entry, \ @@ -25,7 +25,6 @@ from mediagoblin.decorators import uses_pagination, get_user_media_entry, \ from werkzeug.contrib.atom import AtomFeed -import markdown @uses_pagination def user_home(request, page): @@ -101,6 +100,7 @@ def media_home(request, media, **kwargs): 'pagination': pagination, 'comment_form': comment_form}) + @require_active_login def media_post_comment(request): """ @@ -111,11 +111,7 @@ def media_post_comment(request): comment['author'] = request.user['_id'] comment['content'] = request.POST['comment'] - md = markdown.Markdown( - safe_mode = 'escape') - comment['content_html'] = clean_html( - md.convert( - comment['content'])) + comment['content_html'] = cleaned_markdown_conversion(comment['content']) comment.save() @@ -123,6 +119,7 @@ def media_post_comment(request): media = request.matchdict['media'], user = request.matchdict['user']) + ATOM_DEFAULT_NR_OF_UPDATED_ITEMS = 5 def atom_feed(request): From 52359e9103e85e153e3012332e59bd64abf83dbc Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Wed, 29 Jun 2011 20:51:17 -0500 Subject: [PATCH 8/8] Excitedly send a user a message that their comment was posted. --- mediagoblin/user_pages/views.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/mediagoblin/user_pages/views.py b/mediagoblin/user_pages/views.py index d581f19c..399d2020 100644 --- a/mediagoblin/user_pages/views.py +++ b/mediagoblin/user_pages/views.py @@ -15,6 +15,8 @@ # along with this program. If not, see . from webob import exc + +from mediagoblin import messages from mediagoblin.db.util import DESCENDING, ObjectId from mediagoblin.util import ( Pagination, render_to_response, redirect, cleaned_markdown_conversion) @@ -115,6 +117,10 @@ def media_post_comment(request): comment.save() + messages.add_message( + request, messages.SUCCESS, + 'Comment posted!') + return redirect(request, 'mediagoblin.user_pages.media_home', media = request.matchdict['media'], user = request.matchdict['user'])