Changeset - cac5109ac3b6
[Not reviewed]
rhodecode/config/routing.py
Show inline comments
 
@@ -62,8 +62,8 @@ def make_map(config):
 
    rmap.connect('home', '/', controller='home', action='index')
 
    rmap.connect('repo_switcher', '/repos', controller='home',
 
                 action='repo_switcher')
 
    rmap.connect('branch_tag_switcher', '/branches-tags/{repo_name:.*}', 
 
                 controller='home',action='branch_tag_switcher')    
 
    rmap.connect('branch_tag_switcher', '/branches-tags/{repo_name:.*}',
 
                 controller='home', action='branch_tag_switcher')
 
    rmap.connect('bugtracker',
 
                 "http://bitbucket.org/marcinkuzminski/rhodecode/issues",
 
                 _static=True)
 
@@ -267,14 +267,41 @@ def make_map(config):
 
                  action="show", conditions=dict(method=["GET"]))
 
        m.connect("admin_settings_my_account", "/my_account",
 
                  action="my_account", conditions=dict(method=["GET"]))
 
        m.connect("admin_settings_notifications", "/notifications",
 
                  action="notifications", conditions=dict(method=["GET"]))
 
        m.connect("admin_settings_my_account_update", "/my_account_update",
 
                  action="my_account_update", conditions=dict(method=["PUT"]))
 
        m.connect("admin_settings_create_repository", "/create_repository",
 
                  action="create_repository", conditions=dict(method=["GET"]))
 

	
 

	
 
    #NOTIFICATION REST ROUTES
 
    with rmap.submapper(path_prefix=ADMIN_PREFIX,
 
                        controller='admin/notifications') as m:
 
        m.connect("notifications", "/notifications",
 
                  action="create", conditions=dict(method=["POST"]))
 
        m.connect("notifications", "/notifications",
 
                  action="index", conditions=dict(method=["GET"]))
 
        m.connect("formatted_notifications", "/notifications.{format}",
 
                  action="index", conditions=dict(method=["GET"]))
 
        m.connect("new_notification", "/notifications/new",
 
                  action="new", conditions=dict(method=["GET"]))
 
        m.connect("formatted_new_notification", "/notifications/new.{format}",
 
                  action="new", conditions=dict(method=["GET"]))
 
        m.connect("/notification/{notification_id}",
 
                  action="update", conditions=dict(method=["PUT"]))
 
        m.connect("/notification/{notification_id}",
 
                  action="delete", conditions=dict(method=["DELETE"]))
 
        m.connect("edit_notification", "/notification/{notification_id}/edit",
 
                  action="edit", conditions=dict(method=["GET"]))
 
        m.connect("formatted_edit_notification",
 
                  "/notification/{notification_id}.{format}/edit",
 
                  action="edit", conditions=dict(method=["GET"]))
 
        m.connect("notification", "/notification/{notification_id}",
 
                  action="show", conditions=dict(method=["GET"]))
 
        m.connect("formatted_notification", "/notifications/{notification_id}.{format}",
 
                  action="show", conditions=dict(method=["GET"]))
 

	
 

	
 

	
 
    #ADMIN MAIN PAGES
 
    with rmap.submapper(path_prefix=ADMIN_PREFIX,
 
                        controller='admin/admin') as m:
 
@@ -357,7 +384,7 @@ def make_map(config):
 

	
 
    rmap.connect('changeset_comment_delete', '/{repo_name:.*}/changeset/comment/{comment_id}/delete',
 
                controller='changeset', action='delete_comment',
 
                conditions = dict(function=check_repo, method=["DELETE"]))
 
                conditions=dict(function=check_repo, method=["DELETE"]))
 

	
 
    rmap.connect('raw_changeset_home',
 
                 '/{repo_name:.*}/raw-changeset/{revision}',
rhodecode/controllers/admin/notifications.py
Show inline comments
 
new file 100644
 
import logging
 

	
 
from pylons import tmpl_context as c
 

	
 
from rhodecode.lib.base import BaseController, render
 
from rhodecode.model.db import Notification
 

	
 
from rhodecode.model.notification import NotificationModel
 
from rhodecode.lib.auth import LoginRequired
 
from rhodecode.lib import helpers as h
 

	
 
log = logging.getLogger(__name__)
 

	
 
class NotificationsController(BaseController):
 
    """REST Controller styled on the Atom Publishing Protocol"""
 
    # To properly map this controller, ensure your config/routing.py
 
    # file has a resource setup:
 
    #     map.resource('notification', 'notifications', controller='_admin/notifications', 
 
    #         path_prefix='/_admin', name_prefix='_admin_')
 

	
 
    @LoginRequired()
 
    def __before__(self):
 
        super(NotificationsController, self).__before__()
 

	
 

	
 
    def index(self, format='html'):
 
        """GET /_admin/notifications: All items in the collection"""
 
        # url('notifications')
 
        c.user = self.rhodecode_user
 
        c.notifications = NotificationModel()\
 
                            .get_for_user(self.rhodecode_user.user_id)
 
        return render('admin/notifications/notifications.html')
 

	
 
    def create(self):
 
        """POST /_admin/notifications: Create a new item"""
 
        # url('notifications')
 

	
 
    def new(self, format='html'):
 
        """GET /_admin/notifications/new: Form to create a new item"""
 
        # url('new_notification')
 

	
 
    def update(self, notification_id):
 
        """PUT /_admin/notifications/id: Update an existing item"""
 
        # Forms posted to this method should contain a hidden field:
 
        #    <input type="hidden" name="_method" value="PUT" />
 
        # Or using helpers:
 
        #    h.form(url('notification', notification_id=ID),
 
        #           method='put')
 
        # url('notification', notification_id=ID)
 

	
 
    def delete(self, notification_id):
 
        """DELETE /_admin/notifications/id: Delete an existing item"""
 
        # Forms posted to this method should contain a hidden field:
 
        #    <input type="hidden" name="_method" value="DELETE" />
 
        # Or using helpers:
 
        #    h.form(url('notification', notification_id=ID),
 
        #           method='delete')
 
        # url('notification', notification_id=ID)
 

	
 
        no = Notification.get(notification_id)
 
        owner = lambda: no.notifications_to_users.user.user_id == c.rhodecode_user.user_id
 
        if h.HasPermissionAny('hg.admin', 'repository.admin')() or owner:
 
                NotificationModel().delete(notification_id)
 
                return 'ok'
 
        return 'fail'
 

	
 
    def show(self, notification_id, format='html'):
 
        """GET /_admin/notifications/id: Show a specific item"""
 
        # url('notification', notification_id=ID)
 
        c.user = self.rhodecode_user
 
        c.notification = Notification.get(notification_id)
 

	
 
        unotification = NotificationModel()\
 
                            .get_user_notification(c.user.user_id,
 
                                                   c.notification)
 

	
 
        if unotification.read is False:
 
            unotification.mark_as_read()
 

	
 
        return render('admin/notifications/show_notification.html')
 

	
 
    def edit(self, notification_id, format='html'):
 
        """GET /_admin/notifications/id/edit: Form to edit an existing item"""
 
        # url('edit_notification', notification_id=ID)
rhodecode/controllers/admin/settings.py
Show inline comments
 
@@ -372,14 +372,6 @@ class SettingsController(BaseController)
 

	
 
        return redirect(url('my_account'))
 

	
 

	
 
    @NotAnonymous()
 
    def notifications(self):
 
        c.user = User.get(self.rhodecode_user.user_id)
 
        c.notifications = NotificationModel().get_for_user(c.user.user_id)
 
        return render('admin/users/notifications.html'),
 

	
 

	
 
    @NotAnonymous()
 
    @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
 
    def create_repository(self):
rhodecode/controllers/changeset.py
Show inline comments
 
@@ -32,8 +32,7 @@ from pylons.controllers.util import redi
 
from pylons.decorators import jsonify
 

	
 
import rhodecode.lib.helpers as h
 
from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator, \
 
    NotAnonymous
 
from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
 
from rhodecode.lib.base import BaseRepoController, render
 
from rhodecode.lib.utils import EmptyChangeset
 
from rhodecode.lib.compat import OrderedDict
 
@@ -274,13 +273,12 @@ class ChangesetController(BaseRepoContro
 
        return render('changeset/raw_changeset.html')
 

	
 
    def comment(self, repo_name, revision):
 
        ccmodel = ChangesetCommentsModel()
 

	
 
        ccmodel.create(text=request.POST.get('text'),
 
                       repo_id=c.rhodecode_db_repo.repo_id,
 
                       user_id=c.rhodecode_user.user_id,
 
                       revision=revision, f_path=request.POST.get('f_path'),
 
                       line_no=request.POST.get('line'))
 
        ChangesetCommentsModel().create(text=request.POST.get('text'),
 
                                        repo_id=c.rhodecode_db_repo.repo_id,
 
                                        user_id=c.rhodecode_user.user_id,
 
                                        revision=revision,
 
                                        f_path=request.POST.get('f_path'),
 
                                        line_no=request.POST.get('line'))
 

	
 
        return redirect(h.url('changeset_home', repo_name=repo_name,
 
                              revision=revision))
 
@@ -288,8 +286,8 @@ class ChangesetController(BaseRepoContro
 
    @jsonify
 
    def delete_comment(self, comment_id):
 
        co = ChangesetComment.get(comment_id)
 
        if (h.HasPermissionAny('hg.admin', 'repository.admin')() or
 
            co.author.user_id == c.rhodecode_user.user_id):
 
        owner = lambda : co.author.user_id == c.rhodecode_user.user_id
 
        if h.HasPermissionAny('hg.admin', 'repository.admin')() or owner:
 
            ccmodel = ChangesetCommentsModel()
 
            ccmodel.delete(comment_id=comment_id)
 
            return True
rhodecode/model/comment.py
Show inline comments
 
@@ -23,13 +23,16 @@
 
# You should have received a copy of the GNU General Public License
 
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
 

	
 

	
 
import re
 
import logging
 
import traceback
 

	
 
from pylons.i18n.translation import _
 
from sqlalchemy.util.compat import defaultdict
 

	
 
from rhodecode.lib import helpers as h
 
from rhodecode.model import BaseModel
 
from rhodecode.model.db import ChangesetComment, User, Notification
 
from sqlalchemy.util.compat import defaultdict
 
from rhodecode.model.db import ChangesetComment, User, Repository, Notification
 
from rhodecode.model.notification import NotificationModel
 

	
 
log = logging.getLogger(__name__)
 
@@ -38,6 +41,15 @@ log = logging.getLogger(__name__)
 
class ChangesetCommentsModel(BaseModel):
 

	
 

	
 
    def _extract_mentions(self, s):
 
        usrs = []
 
        for username in re.findall(r'(?:^@|\s@)(\w+)', s):
 
            user_obj = User.get_by_username(username, case_insensitive=True)
 
            if user_obj:
 
                usrs.append(user_obj)
 

	
 
        return usrs
 

	
 
    def create(self, text, repo_id, user_id, revision, f_path=None,
 
               line_no=None):
 
        """
 
@@ -51,8 +63,10 @@ class ChangesetCommentsModel(BaseModel):
 
        :param line_no:
 
        """
 
        if text:
 
            repo = Repository.get(repo_id)
 
            desc = repo.scm_instance.get_changeset(revision).message
 
            comment = ChangesetComment()
 
            comment.repo_id = repo_id
 
            comment.repo = repo
 
            comment.user_id = user_id
 
            comment.revision = revision
 
            comment.text = text
 
@@ -60,18 +74,26 @@ class ChangesetCommentsModel(BaseModel):
 
            comment.line_no = line_no
 

	
 
            self.sa.add(comment)
 
            self.sa.commit()
 
            self.sa.flush()
 

	
 
            # make notification
 
            usr = User.get(user_id)
 
            subj = 'User %s commented on %s' % (usr.username, revision)
 
            line = ''
 
            if line_no:
 
                line = _('on line %s') % line_no
 
            subj = h.link_to('Re commit: %(commit_desc)s %(line)s' % \
 
                                    {'commit_desc':desc,'line':line},
 
                             h.url('changeset_home', repo_name=repo.repo_name,
 
                                   revision = revision,
 
                                   anchor = 'comment-%s' % comment.comment_id
 
                                   )
 
                             )
 
            body = text
 
            recipients = ChangesetComment.get_users(revision=revision)
 
            recipients += self._extract_mentions(body)
 
            NotificationModel().create(created_by=user_id, subject=subj,
 
                                   body = body, recipients = recipients,
 
                                   type_ = Notification.TYPE_CHANGESET_COMMENT)
 

	
 

	
 
            return comment
 

	
 
    def delete(self, comment_id):
rhodecode/model/db.py
Show inline comments
 
@@ -49,7 +49,6 @@ from rhodecode.lib.caching_query import 
 
from rhodecode.model.meta import Base, Session
 

	
 

	
 

	
 
log = logging.getLogger(__name__)
 

	
 
#==============================================================================
 
@@ -286,7 +285,9 @@ class User(Base, BaseModel):
 

	
 
    group_member = relationship('UsersGroupMember', cascade='all')
 

	
 
    notifications = relationship('Notification', secondary='user_to_notification')
 
    notifications = relationship('Notification',
 
                            secondary='user_to_notification',
 
                            order_by=lambda :Notification.created_on.desc())
 

	
 
    @property
 
    def full_contact(self):
 
@@ -301,11 +302,9 @@ class User(Base, BaseModel):
 
        return self.admin
 

	
 
    def __repr__(self):
 
        try:
 
            return "<%s('id:%s:%s')>" % (self.__class__.__name__,
 
                                             self.user_id, self.username)
 
        except:
 
            return self.__class__.__name__
 
        return "<%s('id:%s:%s')>" % (self.__class__.__name__,
 
                                     self.user_id, self.username)
 

	
 

	
 
    @classmethod
 
    def get_by_username(cls, username, case_insensitive=False, cache=False):
 
@@ -336,6 +335,7 @@ class User(Base, BaseModel):
 
        Session.commit()
 
        log.debug('updated user %s lastlogin', self.username)
 

	
 

	
 
class UserLog(Base, BaseModel):
 
    __tablename__ = 'user_logs'
 
    __table_args__ = {'extend_existing':True}
 
@@ -1131,9 +1131,9 @@ class Notification(Base, BaseModel):
 
    __tablename__ = 'notifications'
 
    __table_args__ = ({'extend_existing':True})
 

	
 
    TYPE_CHANGESET_COMMENT = 'cs_comment'
 
    TYPE_MESSAGE = 'message'
 
    TYPE_MENTION = 'mention'
 
    TYPE_CHANGESET_COMMENT = u'cs_comment'
 
    TYPE_MESSAGE = u'message'
 
    TYPE_MENTION = u'mention'
 

	
 
    notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
 
    subject = Column('subject', Unicode(512), nullable=True)
 
@@ -1142,9 +1142,10 @@ class Notification(Base, BaseModel):
 
    created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
 
    type_ = Column('type', Unicode(256))
 

	
 
    create_by_user = relationship('User')
 
    user_notifications = relationship('UserNotification',
 
        primaryjoin = 'Notification.notification_id==UserNotification.notification_id',
 
    created_by_user = relationship('User')
 
    notifications_to_users = relationship('UserNotification',
 
        primaryjoin='Notification.notification_id==UserNotification.notification_id',
 
        lazy='joined',
 
        cascade = "all, delete, delete-orphan")
 

	
 
    @property
 
@@ -1158,16 +1159,20 @@ class Notification(Base, BaseModel):
 
            type_ = Notification.TYPE_MESSAGE
 

	
 
        notification = cls()
 
        notification.create_by_user = created_by
 
        notification.created_by_user = created_by
 
        notification.subject = subject
 
        notification.body = body
 
        notification.type_ = type_
 
        Session.add(notification)
 
        for u in recipients:
 
            u.notifications.append(notification)
 
        Session.commit()
 
        return notification
 

	
 
    @property
 
    def description(self):
 
        from rhodecode.model.notification import NotificationModel
 
        return NotificationModel().make_description(self)
 

	
 
class UserNotification(Base, BaseModel):
 
    __tablename__ = 'user_to_notification'
 
    __table_args__ = (UniqueConstraint('user_id', 'notification_id'),
 
@@ -1179,9 +1184,12 @@ class UserNotification(Base, BaseModel):
 
    sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
 

	
 
    user = relationship('User', single_parent=True, lazy="joined")
 
    notification = relationship('Notification',single_parent=True,
 
                                cascade="all, delete, delete-orphan")
 
    notification = relationship('Notification', single_parent=True,)
 

	
 
    def mark_as_read(self):
 
        self.read = True
 
        Session.add(self)
 
        Session.commit()
 

	
 
class DbMigrateVersion(Base, BaseModel):
 
    __tablename__ = 'db_migrate_version'
rhodecode/model/meta.py
Show inline comments
 
@@ -15,7 +15,8 @@ __all__ = ['Base', 'Session']
 
#
 
Session = scoped_session(
 
                sessionmaker(
 
                    query_cls=caching_query.query_callable(cache_manager)
 
                    query_cls = caching_query.query_callable(cache_manager),
 
                    expire_on_commit = True,
 
                )
 
          )
 

	
rhodecode/model/notification.py
Show inline comments
 
@@ -29,15 +29,38 @@ import traceback
 

	
 
from pylons.i18n.translation import _
 

	
 
from rhodecode.lib import safe_unicode
 
from rhodecode.lib.caching_query import FromCache
 
from rhodecode.lib.helpers import age
 

	
 
from rhodecode.model import BaseModel
 
from rhodecode.model.db import Notification, User, UserNotification
 

	
 
log = logging.getLogger(__name__)
 

	
 
class NotificationModel(BaseModel):
 

	
 

	
 
    def __get_user(self, user):
 
        if isinstance(user, User):
 
            return user
 
        elif isinstance(user, basestring):
 
            return User.get_by_username(username=user)
 
        elif isinstance(user, int):
 
            return User.get(user)
 
        else:
 
            raise Exception('Unsupported user must be one of int,'
 
                            'str or User object')
 

	
 
    def __get_notification(self, notification):
 
        if isinstance(notification, Notification):
 
            return notification
 
        elif isinstance(notification, int):
 
            return Notification.get(notification)
 
        else:
 
            if notification:
 
                raise Exception('notification must be int or Instance'
 
                                ' of Notification got %s' % type(notification))
 

	
 

	
 
    def create(self, created_by, subject, body, recipients,
 
               type_=Notification.TYPE_MESSAGE):
 
        """
 
@@ -55,37 +78,61 @@ class NotificationModel(BaseModel):
 
        if not getattr(recipients, '__iter__', False):
 
            raise Exception('recipients must be a list of iterable')
 

	
 
        created_by_obj = created_by
 
        if not isinstance(created_by, User):
 
            created_by_obj = User.get(created_by)
 

	
 
        created_by_obj = self.__get_user(created_by)
 

	
 
        recipients_objs = []
 
        for u in recipients:
 
            if isinstance(u, User):
 
                recipients_objs.append(u)
 
            elif isinstance(u, basestring):
 
                recipients_objs.append(User.get_by_username(username=u))
 
            elif isinstance(u, int):
 
                recipients_objs.append(User.get(u))
 
            else:
 
                raise Exception('Unsupported recipient must be one of int,'
 
                                'str or User object')
 

	
 
        Notification.create(created_by=created_by_obj, subject=subject,
 
                            body = body, recipients = recipients_objs,
 
            recipients_objs.append(self.__get_user(u))
 
        recipients_objs = set(recipients_objs)
 
        return Notification.create(created_by=created_by_obj, subject=subject,
 
                            body=body, recipients=recipients_objs,
 
                            type_=type_)
 

	
 
    def delete(self, notification_id):
 
        # we don't want to remove actuall notification just the assignment
 
        try:
 
            notification_id = int(notification_id)
 
            no = self.__get_notification(notification_id)
 
            if no:
 
                UserNotification.delete(no.notifications_to_users.user_to_notification_id)
 
                return True
 
        except Exception:
 
            log.error(traceback.format_exc())
 
            raise
 

	
 
    def get_for_user(self, user_id):
 
        return User.get(user_id).notifications
 

	
 
    def get_unread_cnt_for_user(self, user_id):
 
        return UserNotification.query()\
 
                .filter(UserNotification.sent_on == None)\
 
                .filter(UserNotification.read == False)\
 
                .filter(UserNotification.user_id == user_id).count()
 

	
 
    def get_unread_for_user(self, user_id):
 
        return [x.notification for x in UserNotification.query()\
 
                .filter(UserNotification.sent_on == None)\
 
                .filter(UserNotification.read == False)\
 
                .filter(UserNotification.user_id == user_id).all()]
 

	
 
    def get_user_notification(self, user, notification):
 
        user = self.__get_user(user)
 
        notification = self.__get_notification(notification)
 

	
 
        return UserNotification.query()\
 
            .filter(UserNotification.notification == notification)\
 
            .filter(UserNotification.user == user).scalar()
 

	
 
    def make_description(self, notification):
 
        """
 
        Creates a human readable description based on properties
 
        of notification object
 
        """
 

	
 
        _map = {notification.TYPE_CHANGESET_COMMENT:_('commented on commit'),
 
                notification.TYPE_MESSAGE:_('sent message'),
 
                notification.TYPE_MENTION:_('mentioned you')}
 

	
 
        tmpl = "%(user)s %(action)s %(when)s"
 
        data = dict(user=notification.created_by_user.username,
 
                    action=_map[notification.type_],
 
                    when=age(notification.created_on))
 
        return tmpl % data
rhodecode/public/css/style.css
Show inline comments
 
@@ -2603,7 +2603,8 @@ div.gravatar {
 
	border: 0px solid #D0D0D0;
 
	float: left;
 
	margin-right: 0.7em;
 
	padding: 2px 2px 0;
 
	padding: 2px 2px 2px 2px;
 
    line-height:0;
 
	-webkit-border-radius: 6px;
 
	-khtml-border-radius: 6px;
 
	-moz-border-radius: 6px;
 
@@ -3481,4 +3482,29 @@ form.comment-inline-form {
 
}
 
.notifications a:hover{
 
	text-decoration: none !important;
 
}
 
.notification-header{
 
	
 
}
 
.notification-header .desc{
 
	font-size: 16px;
 
    height: 24px;
 
    padding-top: 6px;
 
    float: left
 
}
 
 
.notification-header .desc.unread{
 
    font-weight: bold;
 
    font-size: 17px;
 
}
 
 
.notification-header .delete-notifications{
 
    float: right;
 
    padding-top: 8px;
 
    cursor: pointer;
 
}
 
.notification-subject{
 
    clear:both;
 
    border-bottom: 1px solid #eee;
 
    padding:5px 0px 5px 38px;
 
}
 
\ No newline at end of file
rhodecode/public/js/rhodecode.js
Show inline comments
 
@@ -563,3 +563,19 @@ var  getSelectionLink = function(selecti
 
	    }
 
	}
 
};
 

	
 
var deleteNotification = function(url, notification_id){
 
    var callback = { 
 
		success:function(o){
 
		    var obj = YUD.get(String("notification_"+notification_id));
 
			obj.parentNode.removeChild(obj);
 
		},
 
	    failure:function(o){
 
	        alert("error");
 
	    },
 
	};
 
    var postData = '_method=delete';
 
    var sUrl = url.replace('__NOTIFICATION_ID__',notification_id);
 
    var request = YAHOO.util.Connect.asyncRequest('POST', sUrl, 
 
    											  callback, postData);
 
};	
rhodecode/templates/admin/notifications/notifications.html
Show inline comments
 
file renamed from rhodecode/templates/admin/users/notifications.html to rhodecode/templates/admin/notifications/notifications.html
 
@@ -25,14 +25,36 @@
 
        </ul>            
 
    </div>
 
    % if c.notifications:
 
    <%
 
    unread = lambda n:{False:'unread'}.get(n)
 
    %>
 
    <div class="table">
 
      %for notification in c.notifications:
 
          <div class="table">
 
            <h4>${notification.subject}</h4>
 
            <div>${h.rst(notification.body)}</div>
 
        <div id="notification_${notification.notification_id}">
 
          <div class="notification-header">
 
            <div class="gravatar">
 
                <img alt="gravatar" src="${h.gravatar_url(h.email(notification.created_by_user.email),24)}"/>
 
            </div>
 
            <div class="desc">
 
            <a href="${url('notification', notification_id=notification.notification_id)}">${notification.description}</a>
 
            </div>
 
            <div class="delete-notifications">
 
              <span id="${notification.notification_id}" class="delete-notification delete_icon action"></span>
 
            </div>
 
          </div>
 
          <div class="notification-subject">${h.urlify_text(notification.subject)}</div>
 
        </div>
 
      %endfor
 
    </div>
 
    %else:
 
        <div class="table">${_('No notifications here yet')}</div>
 
    %endif
 
</div>    
 
</div>
 
<script type="text/javascript">
 
var url = "${url('notification', notification_id='__NOTIFICATION_ID__')}";
 
   YUE.on(YUQ('.delete-notification'),'click',function(e){
 
	   var notification_id = e.currentTarget.id;
 
	   deleteNotification(url,notification_id)
 
   })
 
</script>
 
</%def>  
rhodecode/templates/admin/notifications/show_notification.html
Show inline comments
 
new file 100644
 
## -*- coding: utf-8 -*-
 
<%inherit file="/base/base.html"/>
 

	
 
<%def name="title()">
 
    ${_('Show notification')} ${c.rhodecode_user.username} - ${c.rhodecode_name}
 
</%def>
 

	
 
<%def name="breadcrumbs_links()">
 
    ${h.link_to(_('Notifications'),h.url('notifications'))}
 
    &raquo; 
 
    ${_('Show notification')}
 
</%def>
 

	
 
<%def name="page_nav()">
 
    ${self.menu('admin')}
 
</%def>
 

	
 
<%def name="main()">
 
<div class="box">
 
    <!-- box / title -->
 
    <div class="title">
 
        ${self.breadcrumbs()}       
 
        <ul class="links">
 
            <li>
 
              <span style="text-transform: uppercase;"><a href="#">${_('Compose message')}</a></span>
 
            </li>          
 
        </ul>            
 
    </div>
 
    <div class="table">
 
      <div class="notification-header">
 
        <div class="gravatar">
 
            <img alt="gravatar" src="${h.gravatar_url(h.email(c.notification.created_by_user.email),24)}"/>
 
        </div>
 
        <div class="desc">
 
            ${c.notification.description}
 
        </div>
 
        <div class="delete-notifications">
 
          <span id="${c.notification.notification_id}" class="delete_icon action"></span>
 
        </div>
 
      </div>
 
      <div>${h.rst(c.notification.body)}</div>
 
    </div>
 
</div>
 
<script type="text/javascript">
 
var url = "${url('notification', notification_id='__NOTIFICATION_ID__')}";
 
   YUE.on(YUQ('.delete-notification'),'click',function(e){
 
       var notification_id = e.currentTarget.id;
 
       deleteNotification(url,notification_id)
 
   })
 
</script>
 
</%def>  
rhodecode/templates/base/base.html
Show inline comments
 
@@ -53,7 +53,7 @@
 
            ${h.link_to(c.rhodecode_user.username,h.url('admin_settings_my_account'),title='%s %s'%(c.rhodecode_user.name,c.rhodecode_user.lastname))}
 
            </div>
 
            <div class="notifications">
 
            <a href="${h.url('admin_settings_notifications')}">${c.unread_notifications}</a>
 
            <a href="${h.url('notifications')}">${c.unread_notifications}</a>
 
            </div>
 
          %endif
 
          </div>	
rhodecode/tests/__init__.py
Show inline comments
 
@@ -9,6 +9,7 @@ setup-app`) and provides the base testin
 
"""
 
import os
 
import time
 
import logging
 
from os.path import join as jn
 

	
 
from unittest import TestCase
 
@@ -20,7 +21,8 @@ from routes.util import URLGenerator
 
from webtest import TestApp
 

	
 
from rhodecode.model import meta
 
import logging
 
from rhodecode.model.db import User
 

	
 
import pylons.test
 

	
 
os.environ['TZ'] = 'UTC'
 
@@ -68,10 +70,11 @@ class TestController(TestCase):
 

	
 
    def log_user(self, username=TEST_USER_ADMIN_LOGIN,
 
                 password=TEST_USER_ADMIN_PASS):
 
        self._logged_username = username
 
        response = self.app.post(url(controller='login', action='index'),
 
                                 {'username':username,
 
                                  'password':password})
 
        
 

	
 
        if 'invalid user name' in response.body:
 
            self.fail('could not login using %s %s' % (username, password))
 

	
 
@@ -79,6 +82,10 @@ class TestController(TestCase):
 
        self.assertEqual(response.session['rhodecode_user'].username, username)
 
        return response.follow()
 

	
 
    def _get_logged_user(self):
 
        return User.get_by_username(self._logged_username)
 

	
 

	
 
    def checkSessionFlash(self, response, msg):
 
        self.assertTrue('flash' in response.session)
 
        self.assertTrue(msg in response.session['flash'][0][1])
rhodecode/tests/functional/test_admin_notifications.py
Show inline comments
 
new file 100644
 
from rhodecode.tests import *
 
from rhodecode.model.db import Notification, User, UserNotification
 

	
 
from rhodecode.model.user import UserModel
 
from rhodecode.model.notification import NotificationModel
 

	
 
class TestNotificationsController(TestController):
 

	
 
    def test_index(self):
 
        self.log_user()
 

	
 

	
 
        u1 = UserModel().create_or_update(username='u1', password='qweqwe',
 
                                               email='u1@rhodecode.org',
 
                                               name='u1', lastname='u1').user_id
 
        u2 = UserModel().create_or_update(username='u2', password='qweqwe',
 
                                               email='u2@rhodecode.org',
 
                                               name='u2', lastname='u2').user_id
 

	
 
        response = self.app.get(url('notifications'))
 
        self.assertTrue('''<div class="table">No notifications here yet</div>'''
 
                        in response.body)
 

	
 
        cur_user = self._get_logged_user()
 

	
 
        NotificationModel().create(created_by=u1, subject=u'test',
 
                                   body=u'notification_1',
 
                                   recipients=[cur_user])
 
        response = self.app.get(url('notifications'))
 

	
 
        self.assertTrue(u'notification_1' in response.body)
 

	
 
        User.delete(u1)
 
        User.delete(u2)
 

	
 
#    def test_index_as_xml(self):
 
#        response = self.app.get(url('formatted_notifications', format='xml'))
 
#
 
#    def test_create(self):
 
#        response = self.app.post(url('notifications'))
 
#
 
#    def test_new(self):
 
#        response = self.app.get(url('new_notification'))
 
#
 
#    def test_new_as_xml(self):
 
#        response = self.app.get(url('formatted_new_notification', format='xml'))
 
#
 
#    def test_update(self):
 
#        response = self.app.put(url('notification', notification_id=1))
 
#
 
#    def test_update_browser_fakeout(self):
 
#        response = self.app.post(url('notification', notification_id=1), params=dict(_method='put'))
 

	
 
    def test_delete(self):
 
        self.log_user()
 
        cur_user = self._get_logged_user()
 

	
 
        u1 = UserModel().create_or_update(username='u1', password='qweqwe',
 
                                               email='u1@rhodecode.org',
 
                                               name='u1', lastname='u1')
 
        u2 = UserModel().create_or_update(username='u2', password='qweqwe',
 
                                               email='u2@rhodecode.org',
 
                                               name='u2', lastname='u2')
 

	
 
        # make two notifications 
 
        notification = NotificationModel().create(created_by=cur_user,
 
                                                  subject=u'test',
 
                                                  body=u'hi there',
 
                                                  recipients=[cur_user, u1, u2])
 

	
 
        u1 = User.get(u1.user_id)
 
        u2 = User.get(u2.user_id)
 

	
 
        # check DB
 
        self.assertEqual(u1.notifications, [notification])
 
        self.assertEqual(u2.notifications, [notification])
 
        cur_usr_id = cur_user.user_id
 
        response = self.app.delete(url('notification',
 
                                       notification_id=cur_usr_id))
 

	
 
        cur_user = self._get_logged_user()
 
        self.assertEqual(cur_user.notifications, [])
 

	
 
        User.delete(u1.user_id)
 
        User.delete(u2.user_id)
 

	
 

	
 
#    def test_delete_browser_fakeout(self):
 
#        response = self.app.post(url('notification', notification_id=1), params=dict(_method='delete'))
 

	
 
    def test_show(self):
 
        self.log_user()
 
        cur_user = self._get_logged_user()
 
        u1 = UserModel().create_or_update(username='u1', password='qweqwe',
 
                                               email='u1@rhodecode.org',
 
                                               name='u1', lastname='u1')
 
        u2 = UserModel().create_or_update(username='u2', password='qweqwe',
 
                                               email='u2@rhodecode.org',
 
                                               name='u2', lastname='u2')
 

	
 
        notification = NotificationModel().create(created_by=cur_user,
 
                                                  subject='test',
 
                                                  body='hi there',
 
                                                  recipients=[cur_user, u1, u2])
 

	
 
        response = self.app.get(url('notification',
 
                                    notification_id=notification.notification_id))
 

	
 
#    def test_show_as_xml(self):
 
#        response = self.app.get(url('formatted_notification', notification_id=1, format='xml'))
 
#
 
#    def test_edit(self):
 
#        response = self.app.get(url('edit_notification', notification_id=1))
 
#
 
#    def test_edit_as_xml(self):
 
#        response = self.app.get(url('formatted_edit_notification', notification_id=1, format='xml'))
 

	
rhodecode/tests/test_models.py
Show inline comments
 
@@ -161,28 +161,35 @@ class TestNotifications(unittest.TestCas
 

	
 

	
 
    def setUp(self):
 
        self.u1 = UserModel().create_or_update(username='u1', password='qweqwe',
 
                                               email='u1@rhodecode.org',
 
                                               name='u1', lastname='u1')
 
        self.u2 = UserModel().create_or_update(username='u2', password='qweqwe',
 
                                               email='u2@rhodecode.org',
 
                                               name='u2', lastname='u3')
 
        self.u3 = UserModel().create_or_update(username='u3', password='qweqwe',
 
                                               email='u3@rhodecode.org',
 
                                               name='u3', lastname='u3')
 

	
 
        self.u1 = UserModel().create_or_update(username=u'u1', password=u'qweqwe',
 
                                               email=u'u1@rhodecode.org',
 
                                               name=u'u1', lastname=u'u1')
 
        self.u2 = UserModel().create_or_update(username=u'u2', password=u'qweqwe',
 
                                               email=u'u2@rhodecode.org',
 
                                               name=u'u2', lastname=u'u3')
 
        self.u3 = UserModel().create_or_update(username=u'u3', password=u'qweqwe',
 
                                               email=u'u3@rhodecode.org',
 
                                               name=u'u3', lastname=u'u3')
 
    def tearDown(self):
 
        User.delete(self.u1.user_id)
 
        User.delete(self.u2.user_id)
 
        User.delete(self.u3.user_id)
 

	
 

	
 
    def test_create_notification(self):
 
        usrs = [self.u1, self.u2]
 
        notification = Notification.create(created_by=self.u1,
 
                                           subject='subj', body='hi there',
 
                                           subject=u'subj', body=u'hi there',
 
                                           recipients=usrs)
 
        Session.commit()
 

	
 
        notifications = Session.query(Notification).all()
 

	
 
        notifications = Notification.query().all()
 
        self.assertEqual(len(notifications), 1)
 

	
 
        unotification = UserNotification.query()\
 
            .filter(UserNotification.notification == notification).all()
 
        self.assertEqual(len(notifications), 1)
 

	
 
        self.assertEqual(notifications[0].recipients, [self.u1, self.u2])
 
        self.assertEqual(notification.notification_id,
 
                         notifications[0].notification_id)
 
@@ -192,21 +199,23 @@ class TestNotifications(unittest.TestCas
 

	
 
    def test_user_notifications(self):
 
        notification1 = Notification.create(created_by=self.u1,
 
                                            subject='subj', body='hi there',
 
                                            subject=u'subj', body=u'hi there',
 
                                            recipients=[self.u3])
 
        notification2 = Notification.create(created_by=self.u1,
 
                                            subject='subj', body='hi there',
 
                                            subject=u'subj', body=u'hi there',
 
                                            recipients=[self.u3])
 
        self.assertEqual(self.u3.notifications, [notification1, notification2])
 

	
 
    def test_delete_notifications(self):
 
        notification = Notification.create(created_by=self.u1,
 
                                           subject='title', body='hi there3',
 
                                           subject=u'title', body=u'hi there3',
 
                                    recipients=[self.u3, self.u1, self.u2])
 
        Session.commit()
 
        notifications = Notification.query().all()
 
        self.assertTrue(notification in notifications)
 

	
 
        Notification.delete(notification.notification_id)
 
        Session.commit()
 

	
 
        notifications = Notification.query().all()
 
        self.assertFalse(notification in notifications)
 
@@ -214,8 +223,3 @@ class TestNotifications(unittest.TestCas
 
        un = UserNotification.query().filter(UserNotification.notification
 
                                             == notification).all()
 
        self.assertEqual(un, [])
 

	
 
    def tearDown(self):
 
        User.delete(self.u1.user_id)
 
        User.delete(self.u2.user_id)
 
        User.delete(self.u3.user_id)
0 comments (0 inline, 0 general)