notifications: ported views to pyramid

This commit is contained in:
Marcin Kuzminski 2017-07-13 18:28:31 +02:00
parent 7caba28f0e
commit 580a0d0ea5
15 changed files with 466 additions and 380 deletions

View file

@ -95,6 +95,28 @@ def includeme(config):
name='my_account_pullrequests_data',
pattern=ADMIN_PREFIX + '/my_account/pull_requests/data')
# notifications
config.add_route(
name='notifications_show_all',
pattern=ADMIN_PREFIX + '/notifications')
# notifications
config.add_route(
name='notifications_mark_all_read',
pattern=ADMIN_PREFIX + '/notifications/mark_all_read')
config.add_route(
name='notifications_show',
pattern=ADMIN_PREFIX + '/notifications/{notification_id}')
config.add_route(
name='notifications_update',
pattern=ADMIN_PREFIX + '/notifications/{notification_id}/update')
config.add_route(
name='notifications_delete',
pattern=ADMIN_PREFIX + '/notifications/{notification_id}/delete')
# channelstream test
config.add_route(
name='my_account_notifications_test_channelstream',

View file

@ -20,7 +20,10 @@
import pytest
from rhodecode.tests import *
from rhodecode.apps._base import ADMIN_PREFIX
from rhodecode.tests import (
TestController, TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS,
TEST_USER_ADMIN_LOGIN, TEST_USER_ADMIN_PASS)
from rhodecode.tests.fixture import Fixture
from rhodecode.model.db import Notification, User
@ -31,12 +34,25 @@ from rhodecode.model.meta import Session
fixture = Fixture()
class TestNotificationsController(TestController):
destroy_users = set()
def route_path(name, params=None, **kwargs):
import urllib
from rhodecode.apps._base import ADMIN_PREFIX
@classmethod
def teardown_class(cls):
fixture.destroy_users(cls.destroy_users)
base_url = {
'notifications_show_all': ADMIN_PREFIX + '/notifications',
'notifications_mark_all_read': ADMIN_PREFIX + '/notifications/mark_all_read',
'notifications_show': ADMIN_PREFIX + '/notifications/{notification_id}',
'notifications_update': ADMIN_PREFIX + '/notifications/{notification_id}/update',
'notifications_delete': ADMIN_PREFIX + '/notifications/{notification_id}/delete',
}[name].format(**kwargs)
if params:
base_url = '{}?{}'.format(base_url, urllib.urlencode(params))
return base_url
class TestNotificationsController(TestController):
def teardown_method(self, method):
for n in Notification.query().all():
@ -44,43 +60,61 @@ class TestNotificationsController(TestController):
Session().delete(inst)
Session().commit()
def test_index(self):
u1 = UserModel().create_or_update(
username='u1', password='qweqwe', email='u1@rhodecode.org',
firstname='u1', lastname='u1')
u1 = u1.user_id
self.destroy_users.add('u1')
def test_show_all(self, user_util):
user = user_util.create_user(password='qweqwe')
user_id = user.user_id
self.log_user(user.username, 'qweqwe')
self.log_user('u1', 'qweqwe')
response = self.app.get(url('notifications'))
response = self.app.get(
route_path('notifications_show_all', params={'type': 'all'}))
response.mustcontain(
'<div class="table">No notifications here yet</div>')
cur_user = self._get_logged_user()
notif = NotificationModel().create(
created_by=u1, notification_subject=u'test_notification_1',
notification_body=u'notification_1', recipients=[cur_user])
notification = NotificationModel().create(
created_by=user_id, notification_subject=u'test_notification_1',
notification_body=u'notification_1', recipients=[user_id])
Session().commit()
response = self.app.get(url('notifications'))
response.mustcontain('id="notification_%s"' % notif.notification_id)
notification_id = notification.notification_id
response = self.app.get(route_path('notifications_show_all',
params={'type': 'all'}))
response.mustcontain('id="notification_%s"' % notification_id)
def test_show_unread(self, user_util):
user = user_util.create_user(password='qweqwe')
user_id = user.user_id
self.log_user(user.username, 'qweqwe')
response = self.app.get(route_path('notifications_show_all'))
response.mustcontain(
'<div class="table">No notifications here yet</div>')
notification = NotificationModel().create(
created_by=user_id, notification_subject=u'test_notification_1',
notification_body=u'notification_1', recipients=[user_id])
# mark the USER notification as unread
user_notification = NotificationModel().get_user_notification(
user_id, notification)
user_notification.read = False
Session().commit()
notification_id = notification.notification_id
response = self.app.get(route_path('notifications_show_all'))
response.mustcontain('id="notification_%s"' % notification_id)
response.mustcontain('<div class="desc unread')
@pytest.mark.parametrize('user,password', [
(TEST_USER_ADMIN_LOGIN, TEST_USER_ADMIN_PASS),
(TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS),
])
def test_delete(self, user, password):
def test_delete(self, user, password, user_util):
self.log_user(user, password)
cur_user = self._get_logged_user()
u1 = UserModel().create_or_update(
username='u1', password='qweqwe',
email='u1@rhodecode.org', firstname='u1', lastname='u1')
u2 = UserModel().create_or_update(
username='u2', password='qweqwe', email='u2@rhodecode.org',
firstname='u2', lastname='u2')
self.destroy_users.add('u1')
self.destroy_users.add('u2')
u1 = user_util.create_user()
u2 = user_util.create_user()
# make notifications
notification = NotificationModel().create(
@ -98,9 +132,10 @@ class TestNotificationsController(TestController):
cur_usr_id = cur_user.user_id
response = self.app.post(
url('notification', notification_id=notification.notification_id),
params={'_method': 'delete', 'csrf_token': self.csrf_token})
assert response.body == 'ok'
route_path('notifications_delete',
notification_id=notification.notification_id),
params={'csrf_token': self.csrf_token})
assert response.json == 'ok'
cur_user = User.get(cur_usr_id)
assert cur_user.notifications == []
@ -109,17 +144,11 @@ class TestNotificationsController(TestController):
(TEST_USER_ADMIN_LOGIN, TEST_USER_ADMIN_PASS),
(TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS),
])
def test_show(self, user, password):
def test_show(self, user, password, user_util):
self.log_user(user, password)
cur_user = self._get_logged_user()
u1 = UserModel().create_or_update(username='u1', password='qweqwe',
email='u1@rhodecode.org',
firstname='u1', lastname='u1')
u2 = UserModel().create_or_update(username='u2', password='qweqwe',
email='u2@rhodecode.org',
firstname='u2', lastname='u2')
self.destroy_users.add('u1')
self.destroy_users.add('u2')
u1 = user_util.create_user()
u2 = user_util.create_user()
subject = u'test'
notif_body = u'hi there'
@ -127,8 +156,9 @@ class TestNotificationsController(TestController):
created_by=cur_user, notification_subject=subject,
notification_body=notif_body, recipients=[cur_user, u1, u2])
response = self.app.get(url(
'notification', notification_id=notification.notification_id))
response = self.app.get(
route_path('notifications_show',
notification_id=notification.notification_id))
response.mustcontain(subject)
response.mustcontain(notif_body)
@ -137,18 +167,11 @@ class TestNotificationsController(TestController):
(TEST_USER_ADMIN_LOGIN, TEST_USER_ADMIN_PASS),
(TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS),
])
def test_update(self, user, password):
def test_update(self, user, password, user_util):
self.log_user(user, password)
cur_user = self._get_logged_user()
u1 = UserModel().create_or_update(username='u1', password='qweqwe',
email='u1@rhodecode.org',
firstname='u1', lastname='u1')
u2 = UserModel().create_or_update(username='u2', password='qweqwe',
email='u2@rhodecode.org',
firstname='u2', lastname='u2')
self.destroy_users.add('u1')
self.destroy_users.add('u2')
u1 = user_util.create_user()
u2 = user_util.create_user()
# make notifications
recipients = [cur_user, u1, u2]
@ -164,9 +187,10 @@ class TestNotificationsController(TestController):
assert u_obj.notifications[0].read == read
response = self.app.post(
url('notification', notification_id=notification.notification_id),
params={'_method': 'put', 'csrf_token': self.csrf_token})
assert response.body == 'ok'
route_path('notifications_update',
notification_id=notification.notification_id),
params={'csrf_token': self.csrf_token})
assert response.json == 'ok'
cur_user = self._get_logged_user()
assert True == cur_user.notifications[0].read
assert True is cur_user.notifications[0].read

View file

@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2011-2017 RhodeCode GmbH
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License, version 3
# (only), as published by the Free Software Foundation.
#
# 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 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/>.
#
# This program is dual-licensed. If you wish to learn more about the
# RhodeCode Enterprise Edition, including its added features, Support services,
# and proprietary license terms, please see https://rhodecode.com/licenses/

View file

@ -0,0 +1,198 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2010-2017 RhodeCode GmbH
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License, version 3
# (only), as published by the Free Software Foundation.
#
# 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 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/>.
#
# This program is dual-licensed. If you wish to learn more about the
# RhodeCode Enterprise Edition, including its added features, Support services,
# and proprietary license terms, please see https://rhodecode.com/licenses/
import logging
from pyramid.httpexceptions import (
HTTPFound, HTTPNotFound, HTTPInternalServerError)
from pyramid.view import view_config
from rhodecode.apps._base import BaseAppView
from rhodecode.lib.auth import LoginRequired, NotAnonymous, CSRFRequired
from rhodecode.lib import helpers as h
from rhodecode.lib.helpers import Page
from rhodecode.lib.utils2 import safe_int
from rhodecode.model.db import Notification
from rhodecode.model.notification import NotificationModel
from rhodecode.model.meta import Session
log = logging.getLogger(__name__)
class MyAccountNotificationsView(BaseAppView):
def load_default_context(self):
c = self._get_local_tmpl_context()
c.user = c.auth_user.get_instance()
self._register_global_c(c)
return c
def _has_permissions(self, notification):
def is_owner():
user_id = self._rhodecode_db_user.user_id
for user_notification in notification.notifications_to_users:
if user_notification.user.user_id == user_id:
return True
return False
return h.HasPermissionAny('hg.admin')() or is_owner()
@LoginRequired()
@NotAnonymous()
@view_config(
route_name='notifications_show_all', request_method='GET',
renderer='rhodecode:templates/admin/notifications/notifications_show_all.mako')
def notifications_show_all(self):
c = self.load_default_context()
c.unread_count = NotificationModel().get_unread_cnt_for_user(
self._rhodecode_db_user.user_id)
_current_filter = self.request.GET.getall('type') or ['unread']
notifications = NotificationModel().get_for_user(
self._rhodecode_db_user.user_id,
filter_=_current_filter)
p = safe_int(self.request.GET.get('page', 1), 1)
def url_generator(**kw):
_query = self.request.GET.mixed()
_query.update(kw)
return self.request.current_route_path(_query=_query)
c.notifications = Page(notifications, page=p, items_per_page=10,
url=url_generator)
c.unread_type = 'unread'
c.all_type = 'all'
c.pull_request_type = Notification.TYPE_PULL_REQUEST
c.comment_type = [Notification.TYPE_CHANGESET_COMMENT,
Notification.TYPE_PULL_REQUEST_COMMENT]
c.current_filter = 'unread' # default filter
if _current_filter == [c.pull_request_type]:
c.current_filter = 'pull_request'
elif _current_filter == c.comment_type:
c.current_filter = 'comment'
elif _current_filter == [c.unread_type]:
c.current_filter = 'unread'
elif _current_filter == [c.all_type]:
c.current_filter = 'all'
return self._get_template_context(c)
@LoginRequired()
@NotAnonymous()
@CSRFRequired()
@view_config(
route_name='notifications_mark_all_read', request_method='POST',
renderer='rhodecode:templates/admin/notifications/notifications_show_all.mako')
def notifications_mark_all_read(self):
NotificationModel().mark_all_read_for_user(
self._rhodecode_db_user.user_id,
filter_=self.request.GET.getall('type'))
Session().commit()
raise HTTPFound(h.route_path('notifications_show_all'))
@LoginRequired()
@NotAnonymous()
@view_config(
route_name='notifications_show', request_method='GET',
renderer='rhodecode:templates/admin/notifications/notifications_show.mako')
def notifications_show(self):
c = self.load_default_context()
notification_id = self.request.matchdict['notification_id']
notification = Notification.get_or_404(notification_id)
if not self._has_permissions(notification):
log.debug('User %s does not have permission to access notification',
self._rhodecode_user)
raise HTTPNotFound()
u_notification = NotificationModel().get_user_notification(
self._rhodecode_db_user.user_id, notification)
if not u_notification:
log.debug('User %s notification does not exist',
self._rhodecode_user)
raise HTTPNotFound()
# when opening this notification, mark it as read for this use
if not u_notification.read:
u_notification.mark_as_read()
Session().commit()
c.notification = notification
return self._get_template_context(c)
@LoginRequired()
@NotAnonymous()
@CSRFRequired()
@view_config(
route_name='notifications_update', request_method='POST',
renderer='json_ext')
def notification_update(self):
notification_id = self.request.matchdict['notification_id']
notification = Notification.get_or_404(notification_id)
if not self._has_permissions(notification):
log.debug('User %s does not have permission to access notification',
self._rhodecode_user)
raise HTTPNotFound()
try:
# updates notification read flag
NotificationModel().mark_read(
self._rhodecode_user.user_id, notification)
Session().commit()
return 'ok'
except Exception:
Session().rollback()
log.exception("Exception updating a notification item")
raise HTTPInternalServerError()
@LoginRequired()
@NotAnonymous()
@CSRFRequired()
@view_config(
route_name='notifications_delete', request_method='POST',
renderer='json_ext')
def notification_delete(self):
notification_id = self.request.matchdict['notification_id']
notification = Notification.get_or_404(notification_id)
if not self._has_permissions(notification):
log.debug('User %s does not have permission to access notification',
self._rhodecode_user)
raise HTTPNotFound()
try:
# deletes only notification2user
NotificationModel().delete(
self._rhodecode_user.user_id, notification)
Session().commit()
return 'ok'
except Exception:
Session().rollback()
log.exception("Exception deleting a notification item")
raise HTTPInternalServerError()

View file

@ -458,20 +458,6 @@ def make_map(config):
m.connect('my_account_password', '/my_account/password',
action='my_account_password', conditions={'method': ['GET']})
# NOTIFICATION REST ROUTES
with rmap.submapper(path_prefix=ADMIN_PREFIX,
controller='admin/notifications') as m:
m.connect('notifications', '/notifications',
action='index', conditions={'method': ['GET']})
m.connect('notifications_mark_all_read', '/notifications/mark_all_read',
action='mark_all_read', conditions={'method': ['POST']})
m.connect('/notifications/{notification_id}',
action='update', conditions={'method': ['PUT']})
m.connect('/notifications/{notification_id}',
action='delete', conditions={'method': ['DELETE']})
m.connect('notification', '/notifications/{notification_id}',
action='show', conditions={'method': ['GET']})
# USER JOURNAL
rmap.connect('journal', '%s/journal' % (ADMIN_PREFIX,),
controller='journal', action='index')

View file

@ -1,157 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2010-2017 RhodeCode GmbH
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License, version 3
# (only), as published by the Free Software Foundation.
#
# 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 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/>.
#
# This program is dual-licensed. If you wish to learn more about the
# RhodeCode Enterprise Edition, including its added features, Support services,
# and proprietary license terms, please see https://rhodecode.com/licenses/
"""
notifications controller for RhodeCode
"""
import logging
import traceback
from pylons import request
from pylons import tmpl_context as c, url
from pylons.controllers.util import redirect, abort
import webhelpers.paginate
from webob.exc import HTTPBadRequest
from rhodecode.lib import auth
from rhodecode.lib.auth import LoginRequired, NotAnonymous
from rhodecode.lib.base import BaseController, render
from rhodecode.lib import helpers as h
from rhodecode.lib.helpers import Page
from rhodecode.lib.utils2 import safe_int
from rhodecode.model.db import Notification
from rhodecode.model.notification import NotificationModel
from rhodecode.model.meta import Session
log = logging.getLogger(__name__)
class NotificationsController(BaseController):
"""REST Controller styled on the Atom Publishing Protocol"""
@LoginRequired()
@NotAnonymous()
def __before__(self):
super(NotificationsController, self).__before__()
def index(self):
"""GET /_admin/notifications: All items in the collection"""
# url('notifications')
c.user = c.rhodecode_user
notif = NotificationModel().get_for_user(
c.rhodecode_user.user_id, filter_=request.GET.getall('type'))
p = safe_int(request.GET.get('page', 1), 1)
notifications_url = webhelpers.paginate.PageURL(
url('notifications'), request.GET)
c.notifications = Page(notif, page=p, items_per_page=10,
url=notifications_url)
c.pull_request_type = Notification.TYPE_PULL_REQUEST
c.comment_type = [Notification.TYPE_CHANGESET_COMMENT,
Notification.TYPE_PULL_REQUEST_COMMENT]
_current_filter = request.GET.getall('type')
c.current_filter = 'all'
if _current_filter == [c.pull_request_type]:
c.current_filter = 'pull_request'
elif _current_filter == c.comment_type:
c.current_filter = 'comment'
if request.is_xhr:
return render('admin/notifications/notifications_data.mako')
return render('admin/notifications/notifications.mako')
@auth.CSRFRequired()
def mark_all_read(self):
if request.is_xhr:
nm = NotificationModel()
# mark all read
nm.mark_all_read_for_user(c.rhodecode_user.user_id,
filter_=request.GET.getall('type'))
Session().commit()
c.user = c.rhodecode_user
notif = nm.get_for_user(c.rhodecode_user.user_id,
filter_=request.GET.getall('type'))
notifications_url = webhelpers.paginate.PageURL(
url('notifications'), request.GET)
c.notifications = Page(notif, page=1, items_per_page=10,
url=notifications_url)
return render('admin/notifications/notifications_data.mako')
def _has_permissions(self, notification):
def is_owner():
user_id = c.rhodecode_user.user_id
for user_notification in notification.notifications_to_users:
if user_notification.user.user_id == user_id:
return True
return False
return h.HasPermissionAny('hg.admin')() or is_owner()
@auth.CSRFRequired()
def update(self, notification_id):
no = Notification.get_or_404(notification_id)
try:
if self._has_permissions(no):
# deletes only notification2user
NotificationModel().mark_read(c.rhodecode_user.user_id, no)
Session().commit()
return 'ok'
except Exception:
Session().rollback()
log.exception("Exception updating a notification item")
raise HTTPBadRequest()
@auth.CSRFRequired()
def delete(self, notification_id):
no = Notification.get_or_404(notification_id)
try:
if self._has_permissions(no):
# deletes only notification2user
NotificationModel().delete(c.rhodecode_user.user_id, no)
Session().commit()
return 'ok'
except Exception:
Session().rollback()
log.exception("Exception deleting a notification item")
raise HTTPBadRequest()
def show(self, notification_id):
c.user = c.rhodecode_user
no = Notification.get_or_404(notification_id)
if no and self._has_permissions(no):
unotification = NotificationModel()\
.get_user_notification(c.user.user_id, no)
# if this association to user is not valid, we don't want to show
# this message
if unotification:
if not unotification.read:
unotification.mark_as_read()
Session().commit()
c.notification = no
return render('admin/notifications/show_notification.mako')
return abort(403)

View file

@ -166,9 +166,6 @@ class NotificationModel(BaseModel):
def get_for_user(self, user, filter_=None):
"""
Get mentions for given user, filter them if filter dict is given
:param user:
:param filter:
"""
user = self._get_user(user)
@ -177,11 +174,14 @@ class NotificationModel(BaseModel):
.join((
Notification, UserNotification.notification_id ==
Notification.notification_id))
if filter_:
if filter_ == ['all']:
q = q # no filter
elif filter_ == ['unread']:
q = q.filter(UserNotification.read == false())
elif filter_:
q = q.filter(Notification.type_.in_(filter_))
return q.all()
return q
def mark_read(self, user, notification):
try:
@ -207,7 +207,9 @@ class NotificationModel(BaseModel):
.join((
Notification, UserNotification.notification_id ==
Notification.notification_id))
if filter_:
if filter_ == ['unread']:
q = q.filter(UserNotification.read == false())
elif filter_:
q = q.filter(Notification.type_.in_(filter_))
# this is a little inefficient but sqlalchemy doesn't support

View file

@ -160,6 +160,11 @@ function registerRCRoutes() {
pyroutes.register('my_account_notifications_toggle_visibility', '/_admin/my_account/toggle_visibility', []);
pyroutes.register('my_account_pullrequests', '/_admin/my_account/pull_requests', []);
pyroutes.register('my_account_pullrequests_data', '/_admin/my_account/pull_requests/data', []);
pyroutes.register('notifications_show_all', '/_admin/notifications', []);
pyroutes.register('notifications_mark_all_read', '/_admin/notifications/mark_all_read', []);
pyroutes.register('notifications_show', '/_admin/notifications/%(notification_id)s', ['notification_id']);
pyroutes.register('notifications_update', '/_admin/notifications/%(notification_id)s/update', ['notification_id']);
pyroutes.register('notifications_delete', '/_admin/notifications/%(notification_id)s/delete', ['notification_id']);
pyroutes.register('my_account_notifications_test_channelstream', '/_admin/my_account/test_channelstream', []);
pyroutes.register('gists_show', '/_admin/gists', []);
pyroutes.register('gists_new', '/_admin/gists/new', []);

View file

@ -30,35 +30,37 @@ var _run_callbacks = function(callbacks){
}
};
var deleteNotification = function(url, notification_id,callbacks){
var deleteNotification = function(notification_id, callbacks){
var callback = function(o){
var obj = $("#notification_"+notification_id);
obj.remove();
_run_callbacks(callbacks);
};
var postData = {'_method': 'delete', 'csrf_token': CSRF_TOKEN};
var sUrl = url.replace('__NOTIFICATION_ID__',notification_id);
var request = $.post(sUrl, postData)
.done(callback)
.fail(function(data, textStatus, errorThrown){
alert("Error while deleting notification.\nError code {0} ({1}). URL: {2}".format(data.status,data.statusText,$(this)[0].url));
});
var postData = {'csrf_token': CSRF_TOKEN};
var sUrl = pyroutes.url('notifications_delete', {'notification_id': notification_id});
var request =
$.post(sUrl, postData)
.done(callback)
.fail(function(data, textStatus, errorThrown){
alert("Error while deleting notification.\nError code {0} ({1}). URL: {2}".format(data.status,data.statusText,$(this)[0].url));
});
};
var readNotification = function(url, notification_id,callbacks){
var readNotification = function(notification_id, callbacks){
var callback = function(o){
var obj = $("#notification_"+notification_id);
obj.removeClass('unread');
var r_button = $('.read-notification',obj)[0];
var r_button = $('.read-notification', obj)[0];
r_button.remove();
_run_callbacks(callbacks);
};
var postData = {'_method': 'put', 'csrf_token': CSRF_TOKEN};
var sUrl = url.replace('__NOTIFICATION_ID__',notification_id);
var request = $.post(sUrl, postData)
.done(callback)
.fail(function(data, textStatus, errorThrown){
alert("Error while saving notification.\nError code {0} ({1}). URL: {2}".format(data.status,data.statusText,$(this)[0].url));
});
var postData = {'csrf_token': CSRF_TOKEN};
var sUrl = pyroutes.url('notifications_update', {'notification_id': notification_id});
var request =
$.post(sUrl, postData)
.done(callback)
.fail(function(data, textStatus, errorThrown){
alert("Error while saving notification.\nError code {0} ({1}). URL: {2}".format(data.status,data.statusText,$(this)[0].url));
});
};

View file

@ -1,78 +0,0 @@
## -*- coding: utf-8 -*-
<%inherit file="/base/base.mako"/>
<%def name="title()">
${_('My Notifications')} ${c.rhodecode_user.username}
%if c.rhodecode_name:
&middot; ${h.branding(c.rhodecode_name)}
%endif
</%def>
<%def name="breadcrumbs_links()">
${_('My Notifications')}
</%def>
<%def name="menu_bar_nav()">
${self.menu_items(active='admin')}
</%def>
<%def name="main()">
<div class="box">
<!-- box / title -->
<div class="title">
${self.breadcrumbs()}
##<ul class="links">
## <li>
## <span ><a href="#">${_('Compose message')}</a></span>
## </li>
##</ul>
<div class="notifications_buttons">
<span id='all' class="action-link first ${'active' if c.current_filter=='all' else ''}"><a href="${h.url.current()}">${_('All')}</a></span>
<span id='comment' class="action-link ${'active' if c.current_filter=='comment' else ''}"><a href="${h.url.current(type=c.comment_type)}">${_('Comments')}</a></span>
<span id='pull_request' class="action-link last ${'active' if c.current_filter=='pull_request' else ''}"><a href="${h.url.current(type=c.pull_request_type)}">${_('Pull Requests')}</a></span>
%if c.notifications:
<span id='mark_all_read' class="btn btn-default">${_('Mark all as read')}</span>
%endif
</div>
</div>
<div id='notification_data' class='main-content-full'>
<%include file='notifications_data.mako'/>
</div>
</div>
<script type="text/javascript">
var url_action = "${h.url('notification', notification_id='__NOTIFICATION_ID__')}";
var run = function(){
$('#notification_data').on('click','.delete-notification',function(e){
var notification_id = e.currentTarget.id;
deleteNotification(url_action,notification_id)
});
$('#notification_data').on('click','.read-notification',function(e){
var notification_id = e.currentTarget.id;
readNotification(url_action,notification_id)
})
};
run();
$('#mark_all_read').on('click',function(e){
//set notifications as read
var url = "${h.url('notifications_mark_all_read', **request.GET.mixed())}";
$.post(url, {'csrf_token': CSRF_TOKEN}).
done(function(data){
// hide notifications counter
$('#quick_login_link > .menu_link_notifications').hide();
$('#notification_data').html(data);
})
.fail(function(data, textStatus, errorThrown){
alert("Error while saving notifications.\nError code {0} ({1}). URL: {2}".format(data.status,data.statusText,$(this)[0].url));
});
});
var current_filter = $("${c.current_filter}");
if (current_filter.length){
current_filter.addClass('active');
}
</script>
</%def>

View file

@ -1,40 +1,46 @@
<%namespace name="base" file="/base/base.mako"/>
%if c.notifications:
<%
unread = lambda n:{False:'unread'}.get(n)
%>
<div class="notification-list notification-table">
%for notification in c.notifications:
<div id="notification_${notification.notification.notification_id}" class="container ${unread(notification.read)}">
<div class="notification-header">
<div class="desc ${unread(notification.read)}">
<a href="${h.url('notification', notification_id=notification.notification.notification_id)}">
${base.gravatar(notification.notification.created_by_user.email, 16)}
${notification.notification.description}
</a>
</div>
<div class="delete-notifications">
<span id="${notification.notification.notification_id}" class="delete-notification"><i class="icon-delete" ></i></span>
</div>
<div class="read-notifications">
%if not notification.read:
<span id="${notification.notification.notification_id}" class="read-notification"><i class="icon-ok" ></i></span>
%endif
</div>
</div>
<div class="notification-subject"></div>
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">${_('My notifications')}</h3>
</div>
%endfor
</div>
<div class="notification-paginator">
<div class="pagination-wh pagination-left">
${c.notifications.pager('$link_previous ~2~ $link_next')}
<div class="panel-body">
%if c.notifications:
<div class="notification-list notification-table">
%for notification in c.notifications:
<div id="notification_${notification.notification.notification_id}" class="container ${'unread' if not notification.read else '' }">
<div class="notification-header">
<div class="desc ${'unread' if not notification.read else '' }">
<a href="${h.route_path('notifications_show', notification_id=notification.notification.notification_id)}">
${base.gravatar(notification.notification.created_by_user.email, 16)}
${notification.notification.description}
</a>
</div>
<div class="delete-notifications">
<span onclick="deleteNotification(${notification.notification.notification_id})" class="delete-notification tooltip" title="${_('Delete')}"><i class="icon-delete"></i></span>
</div>
<div class="read-notifications">
%if not notification.read:
<span onclick="readNotification(${notification.notification.notification_id})" class="read-notification tooltip" title="${_('Mark as read')}"><i class="icon-ok"></i></span>
%endif
</div>
</div>
<div class="notification-subject"></div>
</div>
%endfor
</div>
<div class="notification-paginator">
<div class="pagination-wh pagination-left">
${c.notifications.pager('$link_previous ~2~ $link_next')}
</div>
</div>
%else:
<div class="table">${_('No notifications here yet')}</div>
%endif
</div>
</div>
%else:
<div class="table">${_('No notifications here yet')}</div>
%endif

View file

@ -9,7 +9,7 @@
</%def>
<%def name="breadcrumbs_links()">
${h.link_to(_('Notifications'), h.url('notifications'))}
${h.link_to(_('My Notifications'), h.route_path('notifications_show_all'))}
&raquo;
${_('Show notification')}
</%def>
@ -32,7 +32,7 @@
${c.notification.description}
</div>
<div class="delete-notifications">
<span id="${c.notification.notification_id}" class="delete-notification action"><i class="icon-delete" ></i></span>
<span class="delete-notification tooltip" title="${_('Delete')}" onclick="deleteNotification(${c.notification.notification_id}, [function(){window.location=pyroutes.url('notifications_show_all')}])" class="delete-notification action"><i class="icon-delete" ></i></span>
</div>
</div>
<div class="notification-body">
@ -46,12 +46,5 @@
</div>
</div>
</div>
<script type="text/javascript">
var url = "${h.url('notification', notification_id='__NOTIFICATION_ID__')}";
var main = "${h.url('notifications')}";
$('.delete-notification').on('click',function(e){
var notification_id = e.currentTarget.id;
deleteNotification(url,notification_id,[function(){window.location=main}])
})
</script>
</%def>

View file

@ -0,0 +1,68 @@
## -*- coding: utf-8 -*-
<%inherit file="/base/base.mako"/>
<%def name="title()">
${_('My Notifications')} ${c.rhodecode_user.username}
%if c.rhodecode_name:
&middot; ${h.branding(c.rhodecode_name)}
%endif
</%def>
<%def name="breadcrumbs_links()">
${_('My Notifications')}
</%def>
<%def name="menu_bar_nav()">
${self.menu_items(active='my_account')}
</%def>
<%def name="main()">
<div class="box">
<div class="title">
${self.breadcrumbs()}
<div class="notifications_buttons">
%if c.notifications:
<button id='mark_all_read' class="btn btn-default" type="submit">
${_('Mark all as read')}
</button>
%else:
<button class="btn btn-default" type="submit" disabled="disabled">
${_('Mark all as read')}
</button>
%endif
</div>
</div>
<div class="sidebar-col-wrapper scw-small">
##main
<div class="sidebar">
<ul class="nav nav-pills nav-stacked">
<li id='unread' class="${'active' if c.current_filter=='unread' else ''}"><a href="${h.route_path('notifications_show_all', _query=dict(type=c.unread_type))}">${_('Unread')} (${c.unread_count})</a></li>
<li id='all' class="${'active' if c.current_filter=='all' else ''}"><a href="${h.route_path('notifications_show_all', _query=dict(type=c.all_type))}">${_('All')}</a></li>
<li id='comment' class="${'active' if c.current_filter=='comment' else ''}"><a href="${h.route_path('notifications_show_all', _query=dict(type=c.comment_type))}">${_('Comments')}</a></li>
<li id='pull_request' class="${'active' if c.current_filter=='pull_request' else ''}"><a href="${h.route_path('notifications_show_all', _query=dict(type=c.pull_request_type))}">${_('Pull Requests')}</a></li>
</ul>
</div>
<div class="main-content-full-width">
<%include file='notifications_data.mako'/>
</div>
</div>
</div>
<script type="text/javascript">
$('#mark_all_read').on('click',function(e){
//set notifications as read
var url = "${h.route_path('notifications_mark_all_read', _query=request.GET.mixed())}";
$.post(url, {'csrf_token': CSRF_TOKEN}).
done(function(data){
window.location = "${request.current_route_path(_query=request.GET.mixed())}";
})
.fail(function(data, textStatus, errorThrown){
alert("Error while saving notifications.\nError code {0} ({1}). URL: {2}".format(data.status,data.statusText,$(this)[0].url));
});
});
</script>
</%def>

View file

@ -359,11 +359,7 @@
</div>
%if c.rhodecode_user.username != h.DEFAULT_USER:
<div class="pill_container">
% if c.unread_notifications == 0:
<a class="menu_link_notifications empty" href="${h.url('notifications')}">${c.unread_notifications}</a>
% else:
<a class="menu_link_notifications" href="${h.url('notifications')}">${c.unread_notifications}</a>
% endif
<a class="menu_link_notifications ${'empty' if c.unread_notifications == 0 else ''}" href="${h.route_path('notifications_show_all')}">${c.unread_notifications}</a>
</div>
% endif
</li>