diff options
-rw-r--r-- | mediagoblin/db/migrations.py | 16 | ||||
-rw-r--r-- | mediagoblin/db/models.py | 1 | ||||
-rw-r--r-- | mediagoblin/edit/forms.py | 2 | ||||
-rw-r--r-- | mediagoblin/edit/views.py | 4 | ||||
-rw-r--r-- | mediagoblin/notifications/__init__.py | 18 | ||||
-rw-r--r-- | mediagoblin/notifications/routing.py | 4 | ||||
-rw-r--r-- | mediagoblin/notifications/views.py | 29 | ||||
-rw-r--r-- | mediagoblin/static/js/notifications.js | 13 | ||||
-rw-r--r-- | mediagoblin/templates/mediagoblin/base.html | 3 | ||||
-rw-r--r-- | mediagoblin/templates/mediagoblin/fragments/header_notifications.html | 4 | ||||
-rw-r--r-- | mediagoblin/tests/test_notifications.py | 53 |
11 files changed, 133 insertions, 14 deletions
diff --git a/mediagoblin/db/migrations.py b/mediagoblin/db/migrations.py index 374ab4c8..d542d7b9 100644 --- a/mediagoblin/db/migrations.py +++ b/mediagoblin/db/migrations.py @@ -425,7 +425,7 @@ class RequestToken_v0(declarative_base()): callback = Column(Unicode, nullable=False, default=u"oob") created = Column(DateTime, nullable=False, default=datetime.datetime.now) updated = Column(DateTime, nullable=False, default=datetime.datetime.now) - + class AccessToken_v0(declarative_base()): """ Model for representing the access tokens @@ -438,7 +438,7 @@ class AccessToken_v0(declarative_base()): request_token = Column(Unicode, ForeignKey(RequestToken_v0.token)) created = Column(DateTime, nullable=False, default=datetime.datetime.now) updated = Column(DateTime, nullable=False, default=datetime.datetime.now) - + class NonceTimestamp_v0(declarative_base()): """ @@ -460,3 +460,15 @@ def create_oauth1_tables(db): NonceTimestamp_v0.__table__.create(db.bind) db.commit() + + +@RegisterMigration(15, MIGRATIONS) +def wants_notifications(db): + """Add a wants_notifications field to User model""" + metadata = MetaData(bind=db.bind) + user_table = inspect_table(metadata, "core__users") + + col = Column('wants_notifications', Boolean, default=True) + col.create(user_table) + + db.commit() diff --git a/mediagoblin/db/models.py b/mediagoblin/db/models.py index 9cb39ff4..4341e086 100644 --- a/mediagoblin/db/models.py +++ b/mediagoblin/db/models.py @@ -69,6 +69,7 @@ class User(Base, UserMixin): # Intented to be nullable=False, but migrations would not work for it # set to nullable=True implicitly. wants_comment_notification = Column(Boolean, default=True) + wants_notifications = Column(Boolean, default=True) license_preference = Column(Unicode) is_admin = Column(Boolean, default=False, nullable=False) url = Column(Unicode) diff --git a/mediagoblin/edit/forms.py b/mediagoblin/edit/forms.py index 85c243a0..5de1bf96 100644 --- a/mediagoblin/edit/forms.py +++ b/mediagoblin/edit/forms.py @@ -67,6 +67,8 @@ class EditAccountForm(wtforms.Form): normalize_user_or_email_field(allow_user=False)]) wants_comment_notification = wtforms.BooleanField( description=_("Email me when others comment on my media")) + wants_notifications = wtforms.BooleanField( + description=_("Enable/Disable insite notifications")) license_preference = wtforms.SelectField( _('License preference'), [ diff --git a/mediagoblin/edit/views.py b/mediagoblin/edit/views.py index 6aa2acd9..a11cb932 100644 --- a/mediagoblin/edit/views.py +++ b/mediagoblin/edit/views.py @@ -228,10 +228,12 @@ def edit_account(request): user = request.user form = forms.EditAccountForm(request.form, wants_comment_notification=user.wants_comment_notification, - license_preference=user.license_preference) + license_preference=user.license_preference, + wants_notifications=user.wants_notifications) if request.method == 'POST' and form.validate(): user.wants_comment_notification = form.wants_comment_notification.data + user.wants_notifications = form.wants_notifications.data user.license_preference = form.license_preference.data diff --git a/mediagoblin/notifications/__init__.py b/mediagoblin/notifications/__init__.py index ed9f8d78..b6f9f478 100644 --- a/mediagoblin/notifications/__init__.py +++ b/mediagoblin/notifications/__init__.py @@ -17,7 +17,8 @@ import logging from mediagoblin.db.models import Notification, \ - CommentNotification, CommentSubscription + CommentNotification, CommentSubscription, User +from mediagoblin.notifications.task import email_notification_task from mediagoblin.notifications.tools import generate_comment_message _log = logging.getLogger(__name__) @@ -121,6 +122,12 @@ NOTIFICATION_FETCH_LIMIT = 100 def get_notifications(user_id, only_unseen=True): query = Notification.query.filter_by(user_id=user_id) + wants_notifications = User.query.filter_by(id=user_id).first()\ + .wants_notifications + + # If the user does not want notifications, don't return any + if not wants_notifications: + return None if only_unseen: query = query.filter_by(seen=False) @@ -130,12 +137,19 @@ def get_notifications(user_id, only_unseen=True): return notifications + def get_notification_count(user_id, only_unseen=True): query = Notification.query.filter_by(user_id=user_id) + wants_notifications = User.query.filter_by(id=user_id).first()\ + .wants_notifications if only_unseen: query = query.filter_by(seen=False) - count = query.count() + # If the user doesn't want notifications, don't show any + if not wants_notifications: + count = None + else: + count = query.count() return count diff --git a/mediagoblin/notifications/routing.py b/mediagoblin/notifications/routing.py index e57956d3..cd7bbc21 100644 --- a/mediagoblin/notifications/routing.py +++ b/mediagoblin/notifications/routing.py @@ -23,3 +23,7 @@ add_route('mediagoblin.notifications.subscribe_comments', add_route('mediagoblin.notifications.silence_comments', '/u/<string:user>/m/<string:media>/notifications/silence/', 'mediagoblin.notifications.views:silence_comments') + +add_route('mediagoblin.notifications.mark_all_comment_notifications_seen', + '/notifications/comments/mark_all_seen/', + 'mediagoblin.notifications.views:mark_all_comment_notifications_seen') diff --git a/mediagoblin/notifications/views.py b/mediagoblin/notifications/views.py index d275bc92..cfe66b2e 100644 --- a/mediagoblin/notifications/views.py +++ b/mediagoblin/notifications/views.py @@ -14,19 +14,15 @@ # 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.response import render_to_response, render_404, redirect +from mediagoblin.tools.response import 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.decorators import get_user_media_entry, require_active_login from mediagoblin import messages -from mediagoblin.notifications import add_comment_subscription, \ - silence_comment_subscription +from mediagoblin.notifications import (add_comment_subscription, + silence_comment_subscription, mark_comment_notification_seen, + get_notifications) -from werkzeug.exceptions import BadRequest @get_user_media_entry @require_active_login @@ -41,6 +37,7 @@ def subscribe_comments(request, media): return redirect(request, location=media.url_for_self(request.urlgen)) + @get_user_media_entry @require_active_login def silence_comments(request, media): @@ -52,3 +49,17 @@ def silence_comments(request, media): ' %s.') % media.title) return redirect(request, location=media.url_for_self(request.urlgen)) + + +@require_active_login +def mark_all_comment_notifications_seen(request): + """ + Marks all comment notifications seen. + """ + for comment in get_notifications(request.user.id): + mark_comment_notification_seen(comment.subject_id, request.user) + + if request.GET.get('next'): + return redirect(request, location=request.GET.get('next')) + else: + return redirect(request, 'index') diff --git a/mediagoblin/static/js/notifications.js b/mediagoblin/static/js/notifications.js index 0153463a..78694f59 100644 --- a/mediagoblin/static/js/notifications.js +++ b/mediagoblin/static/js/notifications.js @@ -33,4 +33,17 @@ var notifications = {}; $(document).ready(function () { notifications.init(); + + var mark_all_comments_seen = document.getElementById('mark_all_comments_seen'); + + if (mark_all_comments_seen) { + mark_all_comments_seen.href = '#'; + mark_all_comments_seen.onclick = function() { + $.ajax({ + type: 'GET', + url: mark_all_comments_seen_url, + success: function(res, status, xhr) { window.location.reload(); }, + }); + } + } }); diff --git a/mediagoblin/templates/mediagoblin/base.html b/mediagoblin/templates/mediagoblin/base.html index f7e2dff0..f9deb2ad 100644 --- a/mediagoblin/templates/mediagoblin/base.html +++ b/mediagoblin/templates/mediagoblin/base.html @@ -37,6 +37,9 @@ src="{{ request.staticdirect('/js/header_dropdown.js') }}"></script> <script type="text/javascript" src="{{ request.staticdirect('/js/notifications.js') }}"></script> + <script> + var mark_all_comments_seen_url = "{{ request.urlgen('mediagoblin.notifications.mark_all_comment_notifications_seen') }}" + </script> {# For clarification, the difference between the extra_head.html template # and the head template hook is that the former should be used by diff --git a/mediagoblin/templates/mediagoblin/fragments/header_notifications.html b/mediagoblin/templates/mediagoblin/fragments/header_notifications.html index 70d7935a..55759a39 100644 --- a/mediagoblin/templates/mediagoblin/fragments/header_notifications.html +++ b/mediagoblin/templates/mediagoblin/fragments/header_notifications.html @@ -36,5 +36,9 @@ </li> {% endfor %} </ul> + <a href="{{ request.urlgen('mediagoblin.notifications.mark_all_comment_notifications_seen') }}?next={{ + request.base_url|urlencode }}" id="mark_all_comments_seen"> + {% trans %}Mark all read{% endtrans %} + </a> </div> {% endif %} diff --git a/mediagoblin/tests/test_notifications.py b/mediagoblin/tests/test_notifications.py index d52b8d5a..e075d475 100644 --- a/mediagoblin/tests/test_notifications.py +++ b/mediagoblin/tests/test_notifications.py @@ -149,3 +149,56 @@ otherperson@example.com\n\nSGkgb3RoZXJwZXJzb24sCmNocmlzIGNvbW1lbnRlZCBvbiB5b3VyI # User should not have been notified assert len(notifications) == 1 + + def test_mark_all_comment_notifications_seen(self): + """ Test that mark_all_comments_seen works""" + + user = fixture_add_user('otherperson', password='nosreprehto') + + media_entry = fixture_media_entry(uploader=user.id, state=u'processed') + + fixture_comment_subscription(media_entry) + + media_uri_id = '/u/{0}/m/{1}/'.format(user.username, + media_entry.id) + + # add 2 comments + self.test_app.post( + media_uri_id + 'comment/add/', + { + 'comment_content': u'Test comment #43' + } + ) + + self.test_app.post( + media_uri_id + 'comment/add/', + { + 'comment_content': u'Test comment #44' + } + ) + + notifications = Notification.query.filter_by( + user_id=user.id).all() + + assert len(notifications) == 2 + + # both comments should not be marked seen + assert notifications[0].seen == False + assert notifications[1].seen == False + + # login with other user to mark notifications seen + self.logout() + self.login('otherperson', 'nosreprehto') + + # mark all comment notifications seen + res = self.test_app.get('/notifications/comments/mark_all_seen/') + res.follow() + + assert urlparse.urlsplit(res.location)[2] == '/' + + notifications = Notification.query.filter_by( + user_id=user.id).all() + + # both notifications should be marked seen + assert notifications[0].seen == True + assert notifications[1].seen == True |