/notifications/silence/',
+ 'mediagoblin.notifications.views:silence_comments')
diff --git a/mediagoblin/notifications/task.py b/mediagoblin/notifications/task.py
new file mode 100644
index 00000000..52573b57
--- /dev/null
+++ b/mediagoblin/notifications/task.py
@@ -0,0 +1,46 @@
+# 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 .
+
+import logging
+
+from celery import registry
+from celery.task import Task
+
+from mediagoblin.tools.mail import send_email
+from mediagoblin.db.models import CommentNotification
+
+
+_log = logging.getLogger(__name__)
+
+
+class EmailNotificationTask(Task):
+ '''
+ Celery notification task.
+
+ This task is executed by celeryd to offload long-running operations from
+ the web server.
+ '''
+ def run(self, notification_id, message):
+ cn = CommentNotification.query.filter_by(id=notification_id).first()
+ _log.info('Sending notification email about {0}'.format(cn))
+
+ return send_email(
+ message['from'],
+ [message['to']],
+ message['subject'],
+ message['body'])
+
+email_notification_task = registry.tasks[EmailNotificationTask.name]
diff --git a/mediagoblin/notifications/tools.py b/mediagoblin/notifications/tools.py
new file mode 100644
index 00000000..25432780
--- /dev/null
+++ b/mediagoblin/notifications/tools.py
@@ -0,0 +1,55 @@
+# 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 .
+
+from mediagoblin.tools.template import render_template
+from mediagoblin.tools.translate import pass_to_ugettext as _
+from mediagoblin import mg_globals
+
+def generate_comment_message(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})
+
+ return {
+ 'from': mg_globals.app_config['email_sender_address'],
+ 'to': user.email,
+ 'subject': '{instance_title} - {comment_author} '.format(
+ comment_author=comment_author,
+ instance_title=mg_globals.app_config['html_title']) \
+ + _('commented on your post'),
+ 'body': rendered_email}
diff --git a/mediagoblin/notifications/views.py b/mediagoblin/notifications/views.py
new file mode 100644
index 00000000..d275bc92
--- /dev/null
+++ b/mediagoblin/notifications/views.py
@@ -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 .
+
+from mediagoblin.tools.response import render_to_response, render_404, redirect
+from mediagoblin.tools.translate import pass_to_ugettext as _
+from mediagoblin.decorators import (uses_pagination, get_user_media_entry,
+ get_media_entry_by_id,
+ require_active_login, user_may_delete_media, user_may_alter_collection,
+ get_user_collection, get_user_collection_item, active_user_from_url)
+
+from mediagoblin import messages
+
+from mediagoblin.notifications import add_comment_subscription, \
+ silence_comment_subscription
+
+from werkzeug.exceptions import BadRequest
+
+@get_user_media_entry
+@require_active_login
+def subscribe_comments(request, media):
+
+ add_comment_subscription(request.user, media)
+
+ messages.add_message(request,
+ messages.SUCCESS,
+ _('Subscribed to comments on %s!')
+ % media.title)
+
+ return redirect(request, location=media.url_for_self(request.urlgen))
+
+@get_user_media_entry
+@require_active_login
+def silence_comments(request, media):
+ silence_comment_subscription(request.user, media)
+
+ messages.add_message(request,
+ messages.SUCCESS,
+ _('You will not receive notifications for comments on'
+ ' %s.') % media.title)
+
+ return redirect(request, location=media.url_for_self(request.urlgen))
diff --git a/mediagoblin/routing.py b/mediagoblin/routing.py
index a650f22f..986eb2ed 100644
--- a/mediagoblin/routing.py
+++ b/mediagoblin/routing.py
@@ -35,6 +35,7 @@ def get_url_map():
import mediagoblin.edit.routing
import mediagoblin.webfinger.routing
import mediagoblin.listings.routing
+ import mediagoblin.notifications.routing
for route in PluginManager().get_routes():
add_route(*route)
diff --git a/mediagoblin/static/css/base.css b/mediagoblin/static/css/base.css
index 5b8226e6..888d4e42 100644
--- a/mediagoblin/static/css/base.css
+++ b/mediagoblin/static/css/base.css
@@ -384,6 +384,12 @@ a.comment_whenlink:hover {
margin-top: 8px;
}
+.comment_active {
+ box-shadow: 0px 0px 15px 15px #378566;
+ background: #378566;
+ color: #f7f7f7;
+}
+
textarea#comment_content {
resize: vertical;
width: 100%;
diff --git a/mediagoblin/static/js/notifications.js b/mediagoblin/static/js/notifications.js
new file mode 100644
index 00000000..0153463a
--- /dev/null
+++ b/mediagoblin/static/js/notifications.js
@@ -0,0 +1,36 @@
+'use strict';
+/**
+ * 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 .
+ */
+
+var notifications = {};
+
+(function (n) {
+ n._base = '/';
+ n._endpoint = 'notifications/json';
+
+ n.init = function () {
+ $('.notification-gem').on('click', function () {
+ $('.header_dropdown_down:visible').click();
+ });
+ }
+
+})(notifications)
+
+$(document).ready(function () {
+ notifications.init();
+});
diff --git a/mediagoblin/submit/views.py b/mediagoblin/submit/views.py
index a70c89b4..64e6791b 100644
--- a/mediagoblin/submit/views.py
+++ b/mediagoblin/submit/views.py
@@ -34,6 +34,8 @@ from mediagoblin.media_types import sniff_media, \
from mediagoblin.submit.lib import check_file_field, prepare_queue_task, \
run_process_media, new_upload_entry
+from mediagoblin.notifications import add_comment_subscription
+
@require_active_login
def submit_start(request):
@@ -92,6 +94,8 @@ def submit_start(request):
run_process_media(entry, feed_url)
add_message(request, SUCCESS, _('Woohoo! Submitted!'))
+ add_comment_subscription(request.user, entry)
+
return redirect(request, "mediagoblin.user_pages.user_home",
user=request.user.username)
except Exception as e:
diff --git a/mediagoblin/templates/mediagoblin/base.html b/mediagoblin/templates/mediagoblin/base.html
index 6c7c07d0..25186b18 100644
--- a/mediagoblin/templates/mediagoblin/base.html
+++ b/mediagoblin/templates/mediagoblin/base.html
@@ -34,6 +34,8 @@
src="{{ request.staticdirect('/js/extlib/jquery.js') }}">
+
{# For clarification, the difference between the extra_head.html template
# and the head template hook is that the former should be used by
@@ -57,6 +59,12 @@
{% endif %}
diff --git a/mediagoblin/templates/mediagoblin/fragments/header_notifications.html b/mediagoblin/templates/mediagoblin/fragments/header_notifications.html
new file mode 100644
index 00000000..613100aa
--- /dev/null
+++ b/mediagoblin/templates/mediagoblin/fragments/header_notifications.html
@@ -0,0 +1,40 @@
+{% set notifications = request.notifications.get_notifications(request.user.id) %}
+{% if notifications %}
+
+{% endif %}
diff --git a/mediagoblin/templates/mediagoblin/user_pages/media.html b/mediagoblin/templates/mediagoblin/user_pages/media.html
index fb892fd7..c16e4c78 100644
--- a/mediagoblin/templates/mediagoblin/user_pages/media.html
+++ b/mediagoblin/templates/mediagoblin/user_pages/media.html
@@ -81,6 +81,7 @@
user= media.get_uploader.username,
media_id=media.id) %}
{% trans %}Delete{% endtrans %}
+
{% endif %}
{% autoescape False %}
{{ media.description_html }}
@@ -94,6 +95,8 @@
class="button_action" id="button_addcomment" title="Add a comment">
{% trans %}Add a comment{% endtrans %}
+ {% include "mediagoblin/utils/comment-subscription.html" %}
+
{% endif %}
{% if request.user %}