From 525ef8d14ff1067b6ba1103d27e0aa28bb1a74b3 Mon Sep 17 00:00:00 2001 From: Joseph Schorr Date: Wed, 12 Mar 2014 19:00:24 -0400 Subject: [PATCH] Add support for targeting notifications to organizations and remove the password_required notification for new orbs --- data/database.py | 2 +- data/model.py | 42 +++++++++++++++++------ endpoints/api.py | 1 + static/css/quay.css | 16 ++++++++- static/directives/header-bar.html | 1 + static/directives/notification-view.html | 8 +++-- static/js/app.js | 7 +++- test/data/test.db | Bin 483328 -> 483328 bytes 8 files changed, 61 insertions(+), 16 deletions(-) diff --git a/data/database.py b/data/database.py index c98444ec6..a00078018 100644 --- a/data/database.py +++ b/data/database.py @@ -278,7 +278,7 @@ class NotificationKind(BaseModel): class Notification(BaseModel): uuid = CharField(default=uuid_generator, index=True) kind = ForeignKeyField(NotificationKind, index=True) - notification_user = ForeignKeyField(User, index=True) + target = ForeignKeyField(User, index=True) metadata_json = TextField(default='{}') created = DateTimeField(default=datetime.now, index=True) diff --git a/data/model.py b/data/model.py index 99e3e52f7..b47706703 100644 --- a/data/model.py +++ b/data/model.py @@ -59,7 +59,7 @@ class InvalidBuildTriggerException(DataModelException): pass -def create_user(username, password, email): +def create_user(username, password, email, is_organization=False): if not validate_email(email): raise InvalidEmailAddressException('Invalid email address: %s' % email) if not validate_username(username): @@ -96,7 +96,7 @@ def create_user(username, password, email): # If the password is None, then add a notification for the user to change # their password ASAP. - if not pw_hash: + if not pw_hash and not is_organization: create_notification('password_required', new_user) return new_user @@ -107,7 +107,7 @@ def create_user(username, password, email): def create_organization(name, email, creating_user): try: # Create the org - new_org = create_user(name, None, email) + new_org = create_user(name, None, email, is_organization=True) new_org.organization = True new_org.save() @@ -1546,24 +1546,44 @@ def list_trigger_builds(namespace_name, repository_name, trigger_uuid, .where(RepositoryBuildTrigger.uuid == trigger_uuid)) -def create_notification(kind, user, metadata={}): +def create_notification(kind, target, metadata={}): kind_ref = NotificationKind.get(name=kind) - notification = Notification.create(kind=kind_ref, notification_user=user, + notification = Notification.create(kind=kind_ref, target=target, metadata_json=json.dumps(metadata)) return notification def list_notifications(user, kind=None): + Org = User.alias() + AdminTeam = Team.alias() + AdminTeamMember = TeamMember.alias() + AdminUser = User.alias() + query = (Notification.select() - .join(User) - .where(Notification.notification_user == user)) + .join(User) + .switch(Notification) + .join(Org, JOIN_LEFT_OUTER, on=(Org.id == Notification.target)) + .join(AdminTeam, JOIN_LEFT_OUTER, on=(Org.id == + AdminTeam.organization)) + .join(TeamRole, JOIN_LEFT_OUTER, on=(AdminTeam.role == TeamRole.id)) + .switch(AdminTeam) + .join(AdminTeamMember, JOIN_LEFT_OUTER, on=(AdminTeam.id == + AdminTeamMember.team)) + .join(AdminUser, JOIN_LEFT_OUTER, on=(AdminTeamMember.user == + AdminUser.id))) + + where_clause = ((Notification.target == user) | + ((AdminUser.id == user) & + (TeamRole.name == 'admin'))) + if kind: - query = query.join(NotificationKind).where(NotificationKind.name == kind) + where_clause = where_clause & (NotificationKind.name == kind) - return query.order_by(Notification.created).desc() + return query.where(where_clause).order_by(Notification.created).desc() -def delete_notifications_by_kind(user, kind): +def delete_notifications_by_kind(target, kind): kind_ref = NotificationKind.get(name=kind) - Notification.delete().where(Notification.notification_user == user, Notification.kind == kind_ref).execute() + Notification.delete().where(Notification.target == target, + Notification.kind == kind_ref).execute() diff --git a/endpoints/api.py b/endpoints/api.py index 117ba4983..edd1a9b87 100644 --- a/endpoints/api.py +++ b/endpoints/api.py @@ -2521,6 +2521,7 @@ def get_logs(namespace, start_time, end_time, performer_name=None, def notification_view(notification): return { + 'organization': notification.target.username if notification.target.organization else None, 'kind': notification.kind.name, 'created': notification.created, 'metadata': json.loads(notification.metadata_json), diff --git a/static/css/quay.css b/static/css/quay.css index f706a8c5e..98d95fa90 100644 --- a/static/css/quay.css +++ b/static/css/quay.css @@ -18,6 +18,16 @@ max-width: 320px; } +.notification-view-element .orginfo { + margin-top: 8px; + float: left; +} + +.notification-view-element .orginfo .orgname { + font-size: 12px; + color: #aaa; +} + .notification-view-element .circle { position: absolute; top: 14px; @@ -30,12 +40,16 @@ } .notification-view-element .datetime { - margin-top: 10px; + margin-top: 16px; font-size: 12px; color: #aaa; text-align: right; } +.notification-view-element .message { + margin-bottom: 4px; +} + .notification-view-element .container { padding: 10px; border-radius: 6px; diff --git a/static/directives/header-bar.html b/static/directives/header-bar.html index bd534f13a..688eb8411 100644 --- a/static/directives/header-bar.html +++ b/static/directives/header-bar.html @@ -44,6 +44,7 @@ ng-class="notificationService.notificationClasses" bs-tooltip="" title="{{ notificationService.notificationSummaries }}" + data-html="true" data-placement="left" data-container="body"> {{ notificationService.notifications.length }} diff --git a/static/directives/notification-view.html b/static/directives/notification-view.html index 6d5751995..5adb81261 100644 --- a/static/directives/notification-view.html +++ b/static/directives/notification-view.html @@ -1,7 +1,11 @@
-
{{ getMessage(notification) }}
-
{{ parseDate(notification.created) | date:'medium'}}
+
{{ getMessage(notification) }}
+
+ + {{ notification.organization }} +
+
{{ parseDate(notification.created) | date:'medium'}}
diff --git a/static/js/app.js b/static/js/app.js index acf97b97d..62734d089 100644 --- a/static/js/app.js +++ b/static/js/app.js @@ -3356,11 +3356,16 @@ quayApp.directive('notificationView', function () { 'notification': '=notification', 'parent': '=parent' }, - controller: function($scope, $element, $location, NotificationService) { + controller: function($scope, $element, $location, UserService, NotificationService) { $scope.getMessage = function(notification) { return NotificationService.getMessage(notification); }; + $scope.getGravatar = function(orgname) { + var organization = UserService.getOrganization(orgname); + return organization['gravatar'] || ''; + }; + $scope.parseDate = function(dateString) { return Date.parse(dateString); }; diff --git a/test/data/test.db b/test/data/test.db index 168be98baff4437e0f89db90e86cf92e73f6cebe..e4f9f044f9453ed6c0cd3467f4a90b56f5babb2d 100644 GIT binary patch delta 5021 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