Merge remote-tracking branch 'is_derek/bug405_email_notifications_for_comments' into notifications-merge

Conflicts:
	mediagoblin/db/mongo/migrations.py
This commit is contained in:
Joar Wandborg 2012-06-10 15:53:46 +02:00
commit 879ff4bde6
9 changed files with 143 additions and 30 deletions

View File

@ -198,3 +198,11 @@ def convert_exif_media_data(database):
del media_data['exif'] del media_data['exif']
collection.save(document) collection.save(document)
@RegisterMigration(13)
def user_add_wants_comment_notification(database):
"""
Add wants_comment_notification to user model
"""
add_table_field(database, 'users', 'wants_comment_notification', True)

View File

@ -62,6 +62,8 @@ class User(Document, UserMixin):
we'll change this to a boolean with a key of 'active' and have a we'll change this to a boolean with a key of 'active' and have a
separate field for a reason the user's been disabled if that's separate field for a reason the user's been disabled if that's
appropriate... email_verified is already separate, after all.) appropriate... email_verified is already separate, after all.)
- wants_comment_notification: The user has selected that they want to be
notified when comments are posted on their media.
- verification_key: If the user is awaiting email verification, the user - verification_key: If the user is awaiting email verification, the user
will have to provide this key (which will be encoded in the presented will have to provide this key (which will be encoded in the presented
URL) in order to confirm their email as active. URL) in order to confirm their email as active.
@ -80,6 +82,7 @@ class User(Document, UserMixin):
'pw_hash': unicode, 'pw_hash': unicode,
'email_verified': bool, 'email_verified': bool,
'status': unicode, 'status': unicode,
'wants_comment_notification': bool,
'verification_key': unicode, 'verification_key': unicode,
'is_admin': bool, 'is_admin': bool,
'url': unicode, 'url': unicode,
@ -93,6 +96,7 @@ class User(Document, UserMixin):
default_values = { default_values = {
'created': datetime.datetime.utcnow, 'created': datetime.datetime.utcnow,
'email_verified': False, 'email_verified': False,
'wants_comment_notification': True,
'status': u'needs_email_verification', 'status': u'needs_email_verification',
'is_admin': False} 'is_admin': False}

View File

@ -70,6 +70,7 @@ class User(Base, UserMixin):
pw_hash = Column(Unicode, nullable=False) pw_hash = Column(Unicode, nullable=False)
email_verified = Column(Boolean, default=False) email_verified = Column(Boolean, default=False)
status = Column(Unicode, default=u"needs_email_verification", nullable=False) status = Column(Unicode, default=u"needs_email_verification", nullable=False)
wants_comment_notification = Column(Boolean, default=True, nullable=False)
verification_key = Column(Unicode) verification_key = Column(Unicode)
is_admin = Column(Boolean, default=False, nullable=False) is_admin = Column(Boolean, default=False, nullable=False)
url = Column(Unicode) url = Column(Unicode)

View File

@ -61,14 +61,15 @@ class EditProfileForm(wtforms.Form):
class EditAccountForm(wtforms.Form): class EditAccountForm(wtforms.Form):
old_password = wtforms.PasswordField( old_password = wtforms.PasswordField(
_('Old password'), _('Old password'),
[wtforms.validators.Required()],
description=_( description=_(
"Enter your old password to prove you own this account.")) "Enter your old password to prove you own this account."))
new_password = wtforms.PasswordField( new_password = wtforms.PasswordField(
_('New password'), _('New password'),
[wtforms.validators.Required(), [wtforms.validators.Length(min=6, max=30)],
wtforms.validators.Length(min=6, max=30)],
id="password") id="password")
wants_comment_notification = wtforms.BooleanField(
_('Comment notification?'),
description=_("Check this box to be emailed when someone else comments on your media."))
class EditAttachmentsForm(wtforms.Form): class EditAttachmentsForm(wtforms.Form):

View File

@ -181,39 +181,50 @@ def edit_profile(request):
@require_active_login @require_active_login
def edit_account(request): def edit_account(request):
user = request.user user = request.user
form = forms.EditAccountForm(request.POST,
wants_comment_notification=user.get('wants_comment_notification'))
form = forms.EditAccountForm(request.POST) if request.method == 'POST':
form_validated = form.validate()
if request.method == 'POST' and form.validate(): #if the user has not filled in the new or old password fields
password_matches = auth_lib.bcrypt_check_password( if not form.new_password.data and not form.old_password.data:
request.POST['old_password'], if form.wants_comment_notification.validate(form):
user.pw_hash) user.wants_comment_notification = \
form.wants_comment_notification.data
user.save()
messages.add_message(request,
messages.SUCCESS,
_("Account settings saved"))
return redirect(request,
'mediagoblin.user_pages.user_home',
user=user.username)
if (request.POST['old_password'] or request.POST['new_password']) and not \ #so the user has filled in one or both of the password fields
password_matches: else:
form.old_password.errors.append(_('Wrong password')) if form_validated:
password_matches = auth_lib.bcrypt_check_password(
return render_to_response( form.old_password.data,
request, user.pw_hash)
'mediagoblin/edit/edit_account.html', if password_matches:
{'user': user, #the entire form validates and the password matches
'form': form}) user.pw_hash = auth_lib.bcrypt_gen_password_hash(
form.new_password.data)
if password_matches: user.wants_comment_notification = \
user.pw_hash = auth_lib.bcrypt_gen_password_hash( form.wants_comment_notification.data
request.POST['new_password']) user.save()
messages.add_message(request,
user.save() messages.SUCCESS,
_("Account settings saved"))
messages.add_message(request, return redirect(request,
messages.SUCCESS, 'mediagoblin.user_pages.user_home',
_("Account settings saved")) user=user.username)
return redirect(request, else:
'mediagoblin.user_pages.user_home', form.old_password.errors.append(_('Wrong password'))
user=user.username)
return render_to_response( return render_to_response(
request, request,
'mediagoblin/edit/edit_account.html', 'mediagoblin/edit/edit_account.html',
{'user': user, {'user': user,
'form': form}) 'form': form})

View File

@ -0,0 +1,26 @@
{#
# GNU MediaGoblin -- federated, autonomous media hosting
# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS.
#
# 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 <http://www.gnu.org/licenses/>.
-#}
{% trans username=username, comment_author=comment_author -%}
Hi {{ username }},
{{ comment_author }} commented on your post ({{ comment_url }}) at GNU MediaGoblin:
{% endtrans %}
{{ comment_content }}
GNU MediaGoblin

View File

@ -37,6 +37,7 @@ def test_change_password(test_app):
'/edit/account/', { '/edit/account/', {
'old_password': 'toast', 'old_password': 'toast',
'new_password': '123456', 'new_password': '123456',
'wants_comment_notification': 'y'
}) })
# test_user has to be fetched again in order to have the current values # test_user has to be fetched again in order to have the current values

View File

@ -0,0 +1,54 @@
# GNU MediaGoblin -- federated, autonomous media hosting
# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS.
#
# 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 <http://www.gnu.org/licenses/>.
from mediagoblin.tools.mail import send_email
from mediagoblin.tools.template import render_template
from mediagoblin.tools.translate import pass_to_ugettext as _
from mediagoblin import mg_globals
def send_comment_email(user, comment, media, request):
"""
Sends comment email to user when a comment is made on their media.
Args:
- user: the user object to whom the email is sent
- comment: the comment object referencing user's media
- media: the media object the comment is about
- request: the request
"""
comment_url = request.urlgen(
'mediagoblin.user_pages.media_home.view_comment',
comment = comment._id,
user = media.get_uploader.username,
media = media.slug_or_id,
qualified = True) + '#comment'
comment_author = comment.get_author['username']
rendered_email = render_template(
request, 'mediagoblin/user_pages/comment_email.txt',
{'username':user.username,
'comment_author':comment_author,
'comment_content':comment.content,
'comment_url':comment_url})
send_email(
mg_globals.app_config['email_sender_address'],
[user.email],
'GNU MediaGoblin - {comment_author} '.format(
comment_author=comment_author) + _('commented on your post'),
rendered_email)

View File

@ -23,6 +23,7 @@ from mediagoblin.tools.translate import pass_to_ugettext as _
from mediagoblin.tools.pagination import Pagination from mediagoblin.tools.pagination import Pagination
from mediagoblin.tools.files import delete_media_files from mediagoblin.tools.files import delete_media_files
from mediagoblin.user_pages import forms as user_forms from mediagoblin.user_pages import forms as user_forms
from mediagoblin.user_pages.lib import send_comment_email
from mediagoblin.decorators import (uses_pagination, get_user_media_entry, from mediagoblin.decorators import (uses_pagination, get_user_media_entry,
require_active_login, user_may_delete_media) require_active_login, user_may_delete_media)
@ -158,6 +159,12 @@ def media_post_comment(request, media):
request, messages.SUCCESS, request, messages.SUCCESS,
_('Your comment has been posted!')) _('Your comment has been posted!'))
media_uploader = media.get_uploader
#don't send email if you comment on your own post
if (comment.author != media_uploader and
media_uploader['wants_comment_notification']):
send_comment_email(media_uploader, comment, media, request)
return exc.HTTPFound( return exc.HTTPFound(
location=media.url_for_self(request.urlgen)) location=media.url_for_self(request.urlgen))