Merge remote-tracking branch 'origin/redalert'

Conflicts:
	app.py
This commit is contained in:
Jake Moshenko 2014-08-04 16:56:34 -04:00
commit 0372013f70
46 changed files with 2432 additions and 432 deletions

View file

@ -969,6 +969,106 @@ quayApp = angular.module('quay', quayDependencies, function($provide, cfpLoading
return userService;
}]);
$provide.factory('ExternalNotificationData', [function() {
var externalNotificationData = {};
var events = [
{
'id': 'repo_push',
'title': 'Push to Repository',
'icon': 'fa-upload'
},
{
'id': 'build_queued',
'title': 'Dockerfile Build Queued',
'icon': 'fa-tasks'
},
{
'id': 'build_start',
'title': 'Dockerfile Build Started',
'icon': 'fa-circle-o-notch'
},
{
'id': 'build_success',
'title': 'Dockerfile Build Successfully Completed',
'icon': 'fa-check-circle-o'
},
{
'id': 'build_failure',
'title': 'Dockerfile Build Failed',
'icon': 'fa-times-circle-o'
}
];
var methods = [
{
'id': 'quay_notification',
'title': 'Quay.io Notification',
'icon': 'quay-icon',
'fields': [
{
'name': 'target',
'type': 'entity',
'title': 'Recipient'
}
]
},
{
'id': 'email',
'title': 'E-mail',
'icon': 'fa-envelope',
'fields': [
{
'name': 'email',
'type': 'email',
'title': 'E-mail address'
}
]
},
{
'id': 'webhook',
'title': 'Webhook POST',
'icon': 'fa-link',
'fields': [
{
'name': 'url',
'type': 'url',
'title': 'Webhook URL'
}
]
}
];
var methodMap = {};
var eventMap = {};
for (var i = 0; i < methods.length; ++i) {
methodMap[methods[i].id] = methods[i];
}
for (var i = 0; i < events.length; ++i) {
eventMap[events[i].id] = events[i];
}
externalNotificationData.getSupportedEvents = function() {
return events;
};
externalNotificationData.getSupportedMethods = function() {
return methods;
};
externalNotificationData.getEventInfo = function(event) {
return eventMap[event];
};
externalNotificationData.getMethodInfo = function(method) {
return methodMap[method];
};
return externalNotificationData;
}]);
$provide.factory('NotificationService', ['$rootScope', '$interval', 'UserService', 'ApiService', 'StringBuilderService', 'PlanService', 'UserService', 'Config',
function($rootScope, $interval, UserService, ApiService, StringBuilderService, PlanService, UserService, Config) {
var notificationService = {
@ -984,7 +1084,8 @@ quayApp = angular.module('quay', quayDependencies, function($provide, cfpLoading
'test_notification': {
'level': 'primary',
'message': 'This notification is a long message for testing',
'page': '/about/'
'page': '/about/',
'dismissable': true
},
'password_required': {
'level': 'error',
@ -1015,9 +1116,59 @@ quayApp = angular.module('quay', quayDependencies, function($provide, cfpLoading
'message': 'We will be down for schedule maintenance from {from_date} to {to_date} ' +
'for {reason}. We are sorry about any inconvenience.',
'page': 'http://status.quay.io/'
},
'repo_push': {
'level': 'info',
'message': 'Repository {repository} has been pushed with the following tags updated: {updated_tags}',
'page': function(metadata) {
return '/repository/' + metadata.repository;
},
'dismissable': true
},
'build_queued': {
'level': 'info',
'message': 'A build has been queued for repository {repository}',
'page': function(metadata) {
return '/repository/' + metadata.repository + '/build?current=' + metadata.build_id;
},
'dismissable': true
},
'build_start': {
'level': 'info',
'message': 'A build has been started for repository {repository}',
'page': function(metadata) {
return '/repository/' + metadata.repository + '/build?current=' + metadata.build_id;
},
'dismissable': true
},
'build_failure': {
'level': 'error',
'message': 'A build has failed for repository {repository}',
'page': function(metadata) {
return '/repository/' + metadata.repository + '/build?current=' + metadata.build_id;
},
'dismissable': true
}
};
notificationService.dismissNotification = function(notification) {
notification.dismissed = true;
var params = {
'uuid': notification.id
};
ApiService.updateUserNotification(notification, params);
var index = $.inArray(notification, notificationService.notifications);
if (index >= 0) {
notificationService.notifications.splice(index, 1);
}
};
notificationService.canDismiss = function(notification) {
return !!notificationKinds[notification['kind']]['dismissable'];
};
notificationService.getPage = function(notification) {
var page = notificationKinds[notification['kind']]['page'];
if (typeof page != 'string') {
@ -2274,7 +2425,8 @@ quayApp.directive('logsView', function () {
'repository': '=repository',
'performer': '=performer'
},
controller: function($scope, $element, $sce, Restangular, ApiService, TriggerDescriptionBuilder, StringBuilderService) {
controller: function($scope, $element, $sce, Restangular, ApiService, TriggerDescriptionBuilder,
StringBuilderService, ExternalNotificationData) {
$scope.loading = true;
$scope.logs = null;
$scope.kindsAllowed = null;
@ -2335,8 +2487,6 @@ quayApp.directive('logsView', function () {
'change_repo_visibility': 'Change visibility for repository {repo} to {visibility}',
'add_repo_accesstoken': 'Create access token {token} in repository {repo}',
'delete_repo_accesstoken': 'Delete access token {token} in repository {repo}',
'add_repo_webhook': 'Add webhook in repository {repo}',
'delete_repo_webhook': 'Delete webhook in repository {repo}',
'set_repo_description': 'Change description for repository {repo}: {description}',
'build_dockerfile': function(metadata) {
if (metadata.trigger_id) {
@ -2387,7 +2537,21 @@ quayApp.directive('logsView', function () {
'update_application': 'Update application to {application_name} for client ID {client_id}',
'delete_application': 'Delete application {application_name} with client ID {client_id}',
'reset_application_client_secret': 'Reset the Client Secret of application {application_name} ' +
'with client ID {client_id}'
'with client ID {client_id}',
'add_repo_notification': function(metadata) {
var eventData = ExternalNotificationData.getEventInfo(metadata.event);
return 'Add notification of event "' + eventData['title'] + '" for repository {repo}';
},
'delete_repo_notification': function(metadata) {
var eventData = ExternalNotificationData.getEventInfo(metadata.event);
return 'Delete notification of event "' + eventData['title'] + '" for repository {repo}';
},
// Note: These are deprecated.
'add_repo_webhook': 'Add webhook in repository {repo}',
'delete_repo_webhook': 'Delete webhook in repository {repo}'
};
var logKinds = {
@ -2406,8 +2570,6 @@ quayApp.directive('logsView', function () {
'change_repo_visibility': 'Change repository visibility',
'add_repo_accesstoken': 'Create access token',
'delete_repo_accesstoken': 'Delete access token',
'add_repo_webhook': 'Add webhook',
'delete_repo_webhook': 'Delete webhook',
'set_repo_description': 'Change repository description',
'build_dockerfile': 'Build image from Dockerfile',
'delete_tag': 'Delete Tag',
@ -2427,7 +2589,13 @@ quayApp.directive('logsView', function () {
'create_application': 'Create Application',
'update_application': 'Update Application',
'delete_application': 'Delete Application',
'reset_application_client_secret': 'Reset Client Secret'
'reset_application_client_secret': 'Reset Client Secret',
'add_repo_notification': 'Add repository notification',
'delete_repo_notification': 'Delete repository notification',
// Note: these are deprecated.
'add_repo_webhook': 'Add webhook',
'delete_repo_webhook': 'Delete webhook'
};
var getDateString = function(date) {
@ -3127,48 +3295,73 @@ quayApp.directive('entitySearch', function () {
replace: false,
transclude: false,
restrict: 'C',
require: '?ngModel',
link: function(scope, element, attr, ctrl) {
scope.ngModel = ctrl;
},
scope: {
'namespace': '=namespace',
'inputTitle': '=inputTitle',
'entitySelected': '=entitySelected',
'includeTeams': '=includeTeams',
'isOrganization': '=isOrganization',
'isPersistent': '=isPersistent',
'placeholder': '=placeholder',
// Default: ['user', 'team', 'robot']
'allowedEntities': '=allowedEntities',
'currentEntity': '=currentEntity',
'clearNow': '=clearNow',
'filter': '=filter',
'entitySelected': '&entitySelected',
// When set to true, the contents of the control will be cleared as soon
// as an entity is selected.
'autoClear': '=autoClear',
// Set this property to immediately clear the contents of the control.
'clearValue': '=clearValue',
},
controller: function($scope, $element, Restangular, UserService, ApiService) {
controller: function($rootScope, $scope, $element, Restangular, UserService, ApiService) {
$scope.lazyLoading = true;
$scope.teams = null;
$scope.robots = null;
$scope.isAdmin = false;
$scope.isOrganization = false;
$scope.includeTeams = true;
$scope.includeRobots = true;
$scope.includeOrgs = false;
$scope.currentEntityInternal = $scope.currentEntity;
var isSupported = function(kind, opt_array) {
return $.inArray(kind, opt_array || $scope.allowedEntities || ['user', 'team', 'robot']) >= 0;
};
$scope.lazyLoad = function() {
if (!$scope.namespace || !$scope.lazyLoading) { return; }
// Determine whether we can admin this namespace.
$scope.isAdmin = UserService.isNamespaceAdmin($scope.namespace);
// If the scope is an organization and we are not part of it, then nothing more we can do.
if (!$scope.isAdmin && $scope.isOrganization && !UserService.getOrganization($scope.namespace)) {
$scope.teams = null;
$scope.robots = null;
$scope.lazyLoading = false;
return;
}
if ($scope.isOrganization && $scope.includeTeams) {
// Reset the cached teams and robots.
$scope.teams = null;
$scope.robots = null;
// Load the organization's teams (if applicable).
if ($scope.isOrganization && isSupported('team')) {
// Note: We load the org here again so that we always have the fully up-to-date
// teams list.
ApiService.getOrganization(null, {'orgname': $scope.namespace}).then(function(resp) {
$scope.teams = resp.teams;
});
}
ApiService.getRobots($scope.isOrganization ? $scope.namespace : null).then(function(resp) {
$scope.robots = resp.robots;
// Load the user/organization's robots (if applicable).
if ($scope.isAdmin && isSupported('robot')) {
ApiService.getRobots($scope.isOrganization ? $scope.namespace : null).then(function(resp) {
$scope.robots = resp.robots;
$scope.lazyLoading = false;
}, function() {
$scope.lazyLoading = false;
});
} else {
$scope.lazyLoading = false;
}, function() {
$scope.lazyLoading = false;
});
}
};
$scope.createTeam = function() {
@ -3216,7 +3409,7 @@ quayApp.directive('entitySearch', function () {
'is_robot': is_robot
};
if ($scope.is_organization) {
if ($scope.isOrganization) {
entity['is_org_member'] = true;
}
@ -3226,146 +3419,194 @@ quayApp.directive('entitySearch', function () {
$scope.clearEntityInternal = function() {
$scope.currentEntityInternal = null;
$scope.currentEntity = null;
if ($scope.entitySelected) {
$scope.entitySelected(null);
$scope.entitySelected({'entity': null});
if ($scope.ngModel) {
$scope.ngModel.$setValidity('entity', false);
}
};
$scope.setEntityInternal = function(entity, updateTypeahead) {
if (updateTypeahead) {
$(input).typeahead('val', $scope.isPersistent ? entity.name : '');
$(input).typeahead('val', $scope.autoClear ? '' : entity.name);
} else {
$(input).val($scope.isPersistent ? entity.name : '');
$(input).val($scope.autoClear ? '' : entity.name);
}
if ($scope.isPersistent) {
if (!$scope.autoClear) {
$scope.currentEntityInternal = entity;
$scope.currentEntity = entity;
}
if ($scope.entitySelected) {
$scope.entitySelected(entity);
$scope.entitySelected({'entity': entity});
if ($scope.ngModel) {
$scope.ngModel.$setValidity('entity', !!entity);
}
};
number++;
// Setup the typeahead.
var input = $element[0].firstChild.firstChild;
(function() {
// Create the bloodhound search query system.
$rootScope.__entity_search_counter = (($rootScope.__entity_search_counter || 0) + 1);
var entitySearchB = new Bloodhound({
name: 'entities' + $rootScope.__entity_search_counter,
remote: {
url: '/api/v1/entities/%QUERY',
replace: function (url, uriEncodedQuery) {
var namespace = $scope.namespace || '';
url = url.replace('%QUERY', uriEncodedQuery);
url += '?namespace=' + encodeURIComponent(namespace);
if ($scope.isOrganization && isSupported('team')) {
url += '&includeTeams=true'
}
if (isSupported('org')) {
url += '&includeOrgs=true'
}
return url;
},
filter: function(data) {
var datums = [];
for (var i = 0; i < data.results.length; ++i) {
var entity = data.results[i];
var entitySearchB = new Bloodhound({
name: 'entities' + number,
remote: {
url: '/api/v1/entities/%QUERY',
replace: function (url, uriEncodedQuery) {
var namespace = $scope.namespace || '';
url = url.replace('%QUERY', uriEncodedQuery);
url += '?namespace=' + encodeURIComponent(namespace);
if ($scope.includeTeams) {
url += '&includeTeams=true'
}
return url;
},
filter: function(data) {
var datums = [];
for (var i = 0; i < data.results.length; ++i) {
var entity = data.results[i];
if ($scope.filter) {
var allowed = $scope.filter;
var found = 'user';
if (entity.kind == 'user') {
found = entity.is_robot ? 'robot' : 'user';
} else if (entity.kind == 'team') {
found = 'team';
} else if (entity.kind == 'org') {
found = 'org';
}
if (allowed.indexOf(found)) {
if (!isSupported(found)) {
continue;
}
datums.push({
'value': entity.name,
'tokens': [entity.name],
'entity': entity
});
}
return datums;
}
},
datumTokenizer: function(d) {
return Bloodhound.tokenizers.whitespace(d.val);
},
queryTokenizer: Bloodhound.tokenizers.whitespace
});
entitySearchB.initialize();
// Setup the typeahead.
$(input).typeahead({
'highlight': true
}, {
source: entitySearchB.ttAdapter(),
templates: {
'empty': function(info) {
// Only display the empty dialog if the server load has finished.
if (info.resultKind == 'remote') {
var val = $(input).val();
if (!val) {
return null;
}
if (val.indexOf('@') > 0) {
return '<div class="tt-empty">A Quay.io username (not an e-mail address) must be specified</div>';
}
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'); }
if (classes.length > 1) {
classes[classes.length - 1] = 'or ' + classes[classes.length - 1];
}
var class_string = '';
for (var i = 0; i < classes.length; ++i) {
if (i > 0) {
if (i == classes.length - 1) {
class_string += ' or ';
} else {
class_string += ', ';
}
}
class_string += classes[i];
}
return '<div class="tt-empty">No matching Quay.io ' + class_string + ' found</div>';
}
datums.push({
'value': entity.name,
'tokens': [entity.name],
'entity': entity
});
}
return datums;
}
},
datumTokenizer: function(d) {
return Bloodhound.tokenizers.whitespace(d.val);
},
queryTokenizer: Bloodhound.tokenizers.whitespace
});
entitySearchB.initialize();
var counter = 0;
var input = $element[0].firstChild.firstChild;
$(input).typeahead({
'highlight': true
}, {
source: entitySearchB.ttAdapter(),
templates: {
'empty': function(info) {
// Only display the empty dialog if the server load has finished.
if (info.resultKind == 'remote') {
var val = $(input).val();
if (!val) {
return null;
return null;
},
'suggestion': function (datum) {
template = '<div class="entity-mini-listing">';
if (datum.entity.kind == 'user' && !datum.entity.is_robot) {
template += '<i class="fa fa-user fa-lg"></i>';
} else if (datum.entity.kind == 'user' && datum.entity.is_robot) {
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>';
}
if (val.indexOf('@') > 0) {
return '<div class="tt-empty">A Quay.io username (not an e-mail address) must be specified</div>';
template += '<span class="name">' + datum.value + '</span>';
if (datum.entity.is_org_member === false && datum.entity.kind == 'user') {
template += '<i class="fa fa-exclamation-triangle" title="User is outside the organization"></i>';
}
var robots = $scope.isOrganization ? ', robot accounts' : '';
var teams = ($scope.includeTeams && $scope.isOrganization) ? ' or teams' : '';
return '<div class="tt-empty">No matching Quay.io users' + robots + teams + ' found</div>';
}
template += '</div>';
return template;
}}
});
return null;
},
'suggestion': function (datum) {
template = '<div class="entity-mini-listing">';
if (datum.entity.kind == 'user' && !datum.entity.is_robot) {
template += '<i class="fa fa-user fa-lg"></i>';
} else if (datum.entity.kind == 'user' && datum.entity.is_robot) {
template += '<i class="fa fa-wrench fa-lg"></i>';
} else if (datum.entity.kind == 'team') {
template += '<i class="fa fa-group fa-lg"></i>';
}
template += '<span class="name">' + datum.value + '</span>';
if (datum.entity.is_org_member === false && datum.entity.kind == 'user') {
template += '<i class="fa fa-exclamation-triangle" title="User is outside the organization"></i>';
}
template += '</div>';
return template;
}}
});
$(input).on('input', function(e) {
$scope.$apply(function() {
if ($scope.isPersistent) {
$(input).on('input', function(e) {
$scope.$apply(function() {
$scope.clearEntityInternal();
}
});
});
});
$(input).on('typeahead:selected', function(e, datum) {
$scope.$apply(function() {
$scope.setEntityInternal(datum.entity, true);
$(input).on('typeahead:selected', function(e, datum) {
$scope.$apply(function() {
$scope.setEntityInternal(datum.entity, true);
});
});
});
})();
$scope.$watch('clearValue', function() {
if (!input) { return; }
$scope.$watch('clearNow', function() {
$(input).typeahead('val', '');
$scope.clearEntityInternal();
});
$scope.$watch('inputTitle', function(title) {
$scope.$watch('placeholder', function(title) {
input.setAttribute('placeholder', title);
});
$scope.$watch('allowedEntities', function(allowed) {
if (!allowed) { return; }
$scope.includeTeams = isSupported('team', allowed);
$scope.includeRobots = isSupported('robot', allowed);
});
$scope.$watch('namespace', function(namespace) {
if (!namespace) { return; }
$scope.isAdmin = UserService.isNamespaceAdmin(namespace);
$scope.isOrganization = !!UserService.getOrganization(namespace);
});
$scope.$watch('currentEntity', function(entity) {
if ($scope.currentEntityInternal != entity) {
if (entity) {
@ -3778,7 +4019,9 @@ quayApp.directive('dropdownSelect', function ($compile) {
'placeholder': '=placeholder',
'lookaheadItems': '=lookaheadItems',
'handleItemSelected': '&handleItemSelected',
'handleInput': '&handleInput'
'handleInput': '&handleInput',
'clearValue': '=clearValue'
},
controller: function($scope, $element, $rootScope) {
if (!$rootScope.__dropdownSelectCounter) {
@ -3791,6 +4034,13 @@ quayApp.directive('dropdownSelect', function ($compile) {
// Setup lookahead.
var input = $($element).find('.lookahead-input');
$scope.$watch('clearValue', function(cv) {
if (cv) {
$scope.selectedItem = null;
$(input).val('');
}
});
$scope.$watch('selectedItem', function(item) {
if ($scope.selectedItem == $scope.internalItem) {
// The item has already been set due to an internal action.
@ -4462,6 +4712,192 @@ quayApp.directive('buildProgress', function () {
});
quayApp.directive('externalNotificationView', function () {
var directiveDefinitionObject = {
priority: 0,
templateUrl: '/static/directives/external-notification-view.html',
replace: false,
transclude: false,
restrict: 'C',
scope: {
'repository': '=repository',
'notification': '=notification',
'notificationDeleted': '&notificationDeleted'
},
controller: function($scope, $element, ExternalNotificationData, ApiService) {
$scope.deleteNotification = function() {
var params = {
'repository': $scope.repository.namespace + '/' + $scope.repository.name,
'uuid': $scope.notification.uuid
};
ApiService.deleteRepoNotification(null, params).then(function() {
$scope.notificationDeleted({'notification': $scope.notification});
});
};
$scope.testNotification = function() {
var params = {
'repository': $scope.repository.namespace + '/' + $scope.repository.name,
'uuid': $scope.notification.uuid
};
ApiService.testRepoNotification(null, params).then(function() {
bootbox.dialog({
"title": "Test Notification Queued",
"message": "A test version of this notification has been queued and should appear shortly",
"buttons": {
"close": {
"label": "Close",
"className": "btn-primary"
}
}
});
});
};
$scope.$watch('notification', function(notification) {
if (notification) {
$scope.eventInfo = ExternalNotificationData.getEventInfo(notification.event);
$scope.methodInfo = ExternalNotificationData.getMethodInfo(notification.method);
$scope.config = notification.config;
}
});
}
};
return directiveDefinitionObject;
});
quayApp.directive('createExternalNotificationDialog', function () {
var directiveDefinitionObject = {
priority: 0,
templateUrl: '/static/directives/create-external-notification-dialog.html',
replace: false,
transclude: false,
restrict: 'C',
scope: {
'repository': '=repository',
'counter': '=counter',
'notificationCreated': '&notificationCreated'
},
controller: function($scope, $element, ExternalNotificationData, ApiService, $timeout) {
$scope.currentEvent = null;
$scope.currentMethod = null;
$scope.status = '';
$scope.currentConfig = {};
$scope.clearCounter = 0;
$scope.unauthorizedEmail = false;
$scope.events = ExternalNotificationData.getSupportedEvents();
$scope.methods = ExternalNotificationData.getSupportedMethods();
$scope.setEvent = function(event) {
$scope.currentEvent = event;
};
$scope.setMethod = function(method) {
$scope.currentConfig = {};
$scope.currentMethod = method;
$scope.unauthorizedEmail = false;
};
$scope.createNotification = function() {
if (!$scope.currentConfig.email) {
$scope.performCreateNotification();
return;
}
$scope.status = 'checking-email';
$scope.checkEmailAuthorization();
};
$scope.checkEmailAuthorization = function() {
var params = {
'repository': $scope.repository.namespace + '/' + $scope.repository.name,
'email': $scope.currentConfig.email
};
ApiService.checkRepoEmailAuthorized(null, params).then(function(resp) {
$scope.handleEmailCheck(resp.confirmed);
}, function(resp) {
$scope.handleEmailCheck(false);
});
};
$scope.performCreateNotification = function() {
$scope.status = 'creating';
var params = {
'repository': $scope.repository.namespace + '/' + $scope.repository.name
};
var data = {
'event': $scope.currentEvent.id,
'method': $scope.currentMethod.id,
'config': $scope.currentConfig
};
ApiService.createRepoNotification(data, params).then(function(resp) {
$scope.status = '';
$scope.notificationCreated({'notification': resp});
$('#createNotificationModal').modal('hide');
});
};
$scope.handleEmailCheck = function(isAuthorized) {
if (isAuthorized) {
$scope.performCreateNotification();
return;
}
if ($scope.status == 'authorizing-email-sent') {
$scope.watchEmail();
} else {
$scope.status = 'unauthorized-email';
}
$scope.unauthorizedEmail = true;
};
$scope.sendAuthEmail = function() {
$scope.status = 'authorizing-email';
var params = {
'repository': $scope.repository.namespace + '/' + $scope.repository.name,
'email': $scope.currentConfig.email
};
ApiService.sendAuthorizeRepoEmail(null, params).then(function(resp) {
$scope.status = 'authorizing-email-sent';
$scope.watchEmail();
});
};
$scope.watchEmail = function() {
// TODO: change this to SSE?
$timeout(function() {
$scope.checkEmailAuthorization();
}, 1000);
};
$scope.$watch('counter', function(counter) {
if (counter) {
$scope.clearCounter++;
$scope.status = '';
$scope.currentEvent = null;
$scope.currentMethod = null;
$scope.unauthorizedEmail = false;
$('#createNotificationModal').modal({});
}
});
}
};
return directiveDefinitionObject;
});
quayApp.directive('twitterView', function () {
var directiveDefinitionObject = {
priority: 0,
@ -4494,7 +4930,7 @@ quayApp.directive('notificationView', function () {
'notification': '=notification',
'parent': '=parent'
},
controller: function($scope, $element, $window, $location, UserService, NotificationService) {
controller: function($scope, $element, $window, $location, UserService, NotificationService, ApiService) {
var stringStartsWith = function (str, prefix) {
return str.slice(0, prefix.length) == prefix;
};
@ -4530,6 +4966,14 @@ quayApp.directive('notificationView', function () {
}
};
$scope.dismissNotification = function(notification) {
NotificationService.dismissNotification(notification);
};
$scope.canDismiss = function(notification) {
return NotificationService.canDismiss(notification);
};
$scope.getClass = function(notification) {
return NotificationService.getClass(notification);
};

View file

@ -181,7 +181,7 @@ function TutorialCtrl($scope, AngularTour, AngularTourSignals, UserService, Conf
},
{
'title': 'Repository Admin',
'content': "The repository admin panel allows for modification of a repository's permissions, webhooks, visibility and other settings",
'content': "The repository admin panel allows for modification of a repository's permissions, notifications, visibility and other settings",
'overlayable': true,
'mixpanelEvent': 'tutorial_view_admin'
},
@ -1258,7 +1258,7 @@ function RepoBuildCtrl($scope, Restangular, ApiService, $routeParams, $rootScope
fetchRepository();
}
function RepoAdminCtrl($scope, Restangular, ApiService, KeyService, $routeParams, $rootScope, $location, UserService, Config, Features) {
function RepoAdminCtrl($scope, Restangular, ApiService, KeyService, $routeParams, $rootScope, $location, UserService, Config, Features, ExternalNotificationData) {
var namespace = $routeParams.namespace;
var name = $routeParams.name;
@ -1481,43 +1481,32 @@ function RepoAdminCtrl($scope, Restangular, ApiService, KeyService, $routeParams
});
};
$scope.loadWebhooks = function() {
$scope.showNewNotificationCounter = 0;
$scope.showNewNotificationDialog = function() {
$scope.showNewNotificationCounter++;
};
$scope.handleNotificationCreated = function(notification) {
$scope.notifications.push(notification);
};
$scope.handleNotificationDeleted = function(notification) {
var index = $.inArray(notification, $scope.notifications);
if (index < 0) { return; }
$scope.notifications.splice(index, 1);
};
$scope.loadNotifications = function() {
var params = {
'repository': namespace + '/' + name
};
$scope.newWebhook = {};
$scope.webhooksResource = ApiService.listWebhooksAsResource(params).get(function(resp) {
$scope.webhooks = resp.webhooks;
return $scope.webhooks;
});
};
$scope.createWebhook = function() {
if (!$scope.newWebhook.url) {
return;
}
var params = {
'repository': namespace + '/' + name
};
ApiService.createWebhook($scope.newWebhook, params).then(function(resp) {
$scope.webhooks.push(resp);
$scope.newWebhook.url = '';
$scope.createWebhookForm.$setPristine();
});
};
$scope.deleteWebhook = function(webhook) {
var params = {
'repository': namespace + '/' + name,
'public_id': webhook.public_id
};
ApiService.deleteWebhook(null, params).then(function(resp) {
$scope.webhooks.splice($scope.webhooks.indexOf(webhook), 1);
});
$scope.notificationsResource = ApiService.listRepoNotificationsAsResource(params).get(
function(resp) {
$scope.notifications = resp.notifications;
return $scope.notifications;
});
};
$scope.showBuild = function(buildInfo) {
@ -1649,7 +1638,7 @@ function RepoAdminCtrl($scope, Restangular, ApiService, KeyService, $routeParams
$scope.repo = repo;
$rootScope.title = 'Settings - ' + namespace + '/' + name;
$rootScope.description = 'Administrator settings for ' + namespace + '/' + name +
': Permissions, webhooks and other settings';
': Permissions, notifications and other settings';
// Fetch all the permissions and token info for the repository.
fetchPermissions('user');