- Add support for orgs in the entity search and the notification system

- Fix the titles/names of the different notification types
- Fix the styling of the options buttons on the notifications
This commit is contained in:
Joseph Schorr 2014-07-22 13:39:41 -04:00
parent 1ffbc77106
commit 54ee94754e
6 changed files with 55 additions and 13 deletions

View file

@ -2,9 +2,11 @@ from endpoints.api import (ApiResource, parse_args, query_param, truthy_bool, ni
require_scope)
from data import model
from auth.permissions import (OrganizationMemberPermission, ViewTeamPermission,
ReadRepositoryPermission, UserAdminPermission)
ReadRepositoryPermission, UserAdminPermission,
AdministerOrganizationPermission)
from auth.auth_context import get_authenticated_user
from auth import scopes
from util.gravatar import compute_hash
@resource('/v1/entities/<prefix>')
@ -14,10 +16,12 @@ class EntitySearch(ApiResource):
@query_param('namespace', 'Namespace to use when querying for org entities.', type=str,
default='')
@query_param('includeTeams', 'Whether to include team names.', type=truthy_bool, default=False)
@query_param('includeOrgs', 'Whether to include orgs names.', type=truthy_bool, default=False)
@nickname('getMatchingEntities')
def get(self, args, prefix):
""" Get a list of entities that match the specified prefix. """
teams = []
org_data = []
namespace_name = args['namespace']
robot_namespace = None
@ -34,6 +38,15 @@ class EntitySearch(ApiResource):
if args['includeTeams']:
teams = model.get_matching_teams(prefix, organization)
if args['includeOrgs'] and AdministerOrganizationPermission(namespace_name) \
and namespace_name.startswith(prefix):
org_data = [{
'name': namespace_name,
'kind': 'org',
'is_org_member': True,
'gravatar': compute_hash(organization.email),
}]
except model.InvalidOrganizationException:
# namespace name was a user
user = get_authenticated_user()
@ -69,7 +82,7 @@ class EntitySearch(ApiResource):
user_data = [user_view(user) for user in users]
return {
'results': team_data + user_data
'results': team_data + user_data + org_data
}
@ -113,4 +126,4 @@ class FindRepositories(ApiResource):
'repositories': [repo_view(repo) for repo in matching
if (repo.visibility.name == 'public' or
ReadRepositoryPermission(repo.namespace, repo.name).can())]
}
}

View file

@ -67,6 +67,17 @@ class QuayNotificationMethod(NotificationMethod):
# Just to be safe.
return True
target_users.append(target)
elif target_info['kind'] == 'org':
target = model.get_organization(target_info['name'])
if not target:
# Just to be safe.
return True
# Only repositories under the organization can cause notifications to that org.
if target_info['name'] != repository.namespace:
return False
target_users.append(target)
elif target_info['kind'] == 'team':
# Lookup the team.

View file

@ -4469,11 +4469,11 @@ i.quay-icon {
width: 50px;
}
.external-notification-view-element .side-controls {
opacity: 0;
transition: opacity 300ms ease-in-out;
.external-notification-view-element .side-controls button {
border: 1px solid transparent;
transition: all 300ms ease-in-out;
}
.external-notification-view-element:hover .side-controls {
opacity: 1;
.external-notification-view-element:hover .side-controls button {
border: 1px solid #eee;
}

View file

@ -66,7 +66,7 @@
placeholder="''"
current-entity="currentConfig[field.name]"
ng-model="currentConfig[field.name]"
allowed-entities="['user', 'team']"
allowed-entities="['user', 'team', 'org']"
ng-switch-when="entity">
</div>
</td>
@ -89,7 +89,7 @@
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-primary"
ng-disabled="createForm.$invalid || !currentMethod || !currentEvent || creating">
ng-disabled="createForm.$invalid || !currentMethod.id || !currentEvent.id || creating">
Create Notification
</button>
<button type="button" class="btn btn-default" data-dismiss="modal" ng-disabled="creating">Cancel</button>

View file

@ -6,7 +6,14 @@
<span ng-if="getIsAdmin(namespace)"><a href="/organization/{{ namespace }}/teams/{{ entity.name }}">{{entity.name}}</a></span>
</span>
</span>
<span ng-if="entity.kind != 'team'">
<span ng-if="entity.kind == 'org'">
<img src="//www.gravatar.com/avatar/{{ entity.gravatar }}?s=16&amp;d=identicon">
<span class="entity-name">
<span ng-if="!getIsAdmin(entity.name)">{{entity.name}}</span>
<span ng-if="getIsAdmin(entity.name)"><a href="/organization/{{ entity.name }}">{{entity.name}}</a></span>
</span>
</span>
<span ng-if="entity.kind != 'team' && entity.kind != 'org'">
<i class="fa fa-user" ng-show="!entity.is_robot" data-title="User" bs-tooltip="tooltip.title" data-container="body"></i>
<i class="fa fa-wrench" ng-show="entity.is_robot" data-title="Robot Account" bs-tooltip="tooltip.title" data-container="body"></i>
<span class="entity-name" ng-if="entity.is_robot">

View file

@ -1015,7 +1015,7 @@ quayApp = angular.module('quay', quayDependencies, function($provide, cfpLoading
},
{
'id': 'email',
'title': 'E-mail notification',
'title': 'E-mail',
'icon': 'fa-envelope',
'fields': [
{
@ -1027,7 +1027,7 @@ quayApp = angular.module('quay', quayDependencies, function($provide, cfpLoading
},
{
'id': 'webhook',
'title': 'Webhook invoke',
'title': 'Webhook POST',
'icon': 'fa-link',
'fields': [
{
@ -3304,6 +3304,7 @@ quayApp.directive('entitySearch', function () {
$scope.includeTeams = true;
$scope.includeRobots = true;
$scope.includeOrgs = false;
$scope.currentEntityInternal = $scope.currentEntity;
@ -3436,6 +3437,9 @@ quayApp.directive('entitySearch', function () {
if ($scope.isOrganization && isSupported('team')) {
url += '&includeTeams=true'
}
if (isSupported('org')) {
url += '&includeOrgs=true'
}
return url;
},
filter: function(data) {
@ -3448,6 +3452,8 @@ quayApp.directive('entitySearch', function () {
found = entity.is_robot ? 'robot' : 'user';
} else if (entity.kind == 'team') {
found = 'team';
} else if (entity.kind == 'org') {
found = 'org';
}
if (!isSupported(found)) {
@ -3491,6 +3497,7 @@ quayApp.directive('entitySearch', function () {
var classes = [];
if (isSupported('user')) { classes.push('users'); }
if (isSupported('org')) { classes.push('organizations'); }
if ($scope.isAdmin && isSupported('robot')) { classes.push('robot accounts'); }
if ($scope.isOrganization && isSupported('team')) { classes.push('teams'); }
@ -3524,7 +3531,11 @@ quayApp.directive('entitySearch', function () {
template += '<i class="fa fa-wrench fa-lg"></i>';
} else if (datum.entity.kind == 'team') {
template += '<i class="fa fa-group fa-lg"></i>';
} else if (datum.entity.kind == 'org') {
template += '<i class="fa"><img src="//www.gravatar.com/avatar/' +
datum.entity.gravatar + '?s=16&amp;d=identicon"></i>';
}
template += '<span class="name">' + datum.value + '</span>';
if (datum.entity.is_org_member === false && datum.entity.kind == 'user') {