Merge branch 'master' into git
This commit is contained in:
commit
ba2cb08904
268 changed files with 7008 additions and 1535 deletions
|
@ -12,7 +12,7 @@ angular.module('quay').directive('focusablePopoverContent', ['$timeout', '$popov
|
|||
|
||||
if (!scope) { return; }
|
||||
scope.$apply(function() {
|
||||
if (!scope || !$scope.$hide) { return; }
|
||||
if (!scope || !scope.$hide) { return; }
|
||||
scope.$hide();
|
||||
});
|
||||
};
|
||||
|
|
20
static/js/directives/ng-image-watch.js
Normal file
20
static/js/directives/ng-image-watch.js
Normal file
|
@ -0,0 +1,20 @@
|
|||
/**
|
||||
* Adds a ng-image-watch attribute, which is a callback invoked when the image is loaded or fails.
|
||||
*/
|
||||
angular.module('quay').directive('ngImageWatch', function ($parse) {
|
||||
return {
|
||||
restrict: 'A',
|
||||
compile: function($element, attr) {
|
||||
var fn = $parse(attr['ngImageWatch']);
|
||||
return function(scope, element) {
|
||||
element.bind('error', function() {
|
||||
fn(scope, {result: false});
|
||||
});
|
||||
|
||||
element.bind('load', function() {
|
||||
fn(scope, {result: true});
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
|
@ -120,6 +120,8 @@ angular.module('quay').directive('quayClasses', function(Features, Config) {
|
|||
/**
|
||||
* Adds a quay-include attribtue that adds a template solely if the expression evaluates to true.
|
||||
* Automatically adds the Features and Config services to the scope.
|
||||
*
|
||||
Usage: quay-include="{'Features.BILLING': 'partials/landing-normal.html', '!Features.BILLING': 'partials/landing-login.html'}"
|
||||
*/
|
||||
angular.module('quay').directive('quayInclude', function($compile, $templateCache, $http, Features, Config) {
|
||||
return {
|
||||
|
@ -127,7 +129,7 @@ angular.module('quay').directive('quayInclude', function($compile, $templateCach
|
|||
restrict: 'A',
|
||||
link: function($scope, $element, $attr, ctrl) {
|
||||
var getTemplate = function(templateName) {
|
||||
var templateUrl = '/static/partials/' + templateName;
|
||||
var templateUrl = '/static/' + templateName;
|
||||
return $http.get(templateUrl, {cache: $templateCache});
|
||||
};
|
||||
|
||||
|
|
|
@ -12,11 +12,13 @@ angular.module('quay').directive('repoPanelBuilds', function () {
|
|||
'repository': '=repository',
|
||||
'builds': '=builds'
|
||||
},
|
||||
controller: function($scope, $element, $filter, $routeParams, ApiService, TriggerService) {
|
||||
controller: function($scope, $element, $filter, $routeParams, ApiService, TriggerService, UserService) {
|
||||
var orderBy = $filter('orderBy');
|
||||
|
||||
$scope.TriggerService = TriggerService;
|
||||
|
||||
UserService.updateUserIn($scope);
|
||||
|
||||
$scope.options = {
|
||||
'filter': 'recent',
|
||||
'reverse': false,
|
||||
|
@ -66,18 +68,22 @@ angular.module('quay').directive('repoPanelBuilds', function () {
|
|||
if ($scope.buildsResource && filter == $scope.currentFilter) { return; }
|
||||
|
||||
var since = null;
|
||||
var limit = 10;
|
||||
|
||||
if ($scope.options.filter == '48hour') {
|
||||
since = Math.floor(moment().subtract(2, 'days').valueOf() / 1000);
|
||||
limit = 100;
|
||||
} else if ($scope.options.filter == '30day') {
|
||||
since = Math.floor(moment().subtract(30, 'days').valueOf() / 1000);
|
||||
limit = 100;
|
||||
} else {
|
||||
since = null;
|
||||
limit = 10;
|
||||
}
|
||||
|
||||
var params = {
|
||||
'repository': $scope.repository.namespace + '/' + $scope.repository.name,
|
||||
'limit': 100,
|
||||
'limit': limit,
|
||||
'since': since
|
||||
};
|
||||
|
||||
|
@ -175,6 +181,12 @@ angular.module('quay').directive('repoPanelBuilds', function () {
|
|||
};
|
||||
|
||||
$scope.askRunTrigger = function(trigger) {
|
||||
if ($scope.user.username != trigger.connected_user) {
|
||||
bootbox.alert('For security reasons, only user "' + trigger.connected_user +
|
||||
'" can manually invoke this trigger');
|
||||
return;
|
||||
}
|
||||
|
||||
$scope.currentStartTrigger = trigger;
|
||||
$scope.showTriggerStartDialogCounter++;
|
||||
};
|
||||
|
|
|
@ -12,7 +12,16 @@ angular.module('quay').directive('repoPanelInfo', function () {
|
|||
'repository': '=repository',
|
||||
'builds': '=builds'
|
||||
},
|
||||
controller: function($scope, $element, ApiService) {
|
||||
controller: function($scope, $element, ApiService, Config) {
|
||||
$scope.$watch('repository', function(repository) {
|
||||
if (!$scope.repository) { return; }
|
||||
|
||||
var namespace = $scope.repository.namespace;
|
||||
var name = $scope.repository.name;
|
||||
|
||||
$scope.pullCommand = 'docker pull ' + Config.getDomain() + '/' + namespace + '/' + name;
|
||||
});
|
||||
|
||||
$scope.updateDescription = function(content) {
|
||||
$scope.repository.description = content;
|
||||
$scope.repository.put();
|
||||
|
|
|
@ -25,6 +25,7 @@ angular.module('quay').directive('repoPanelTags', function () {
|
|||
|
||||
$scope.iterationState = {};
|
||||
$scope.tagActionHandler = null;
|
||||
$scope.showingHistory = false;
|
||||
|
||||
var setTagState = function() {
|
||||
if (!$scope.repository || !$scope.selectedTags) { return; }
|
||||
|
@ -118,8 +119,142 @@ angular.module('quay').directive('repoPanelTags', function () {
|
|||
|
||||
// Process each of the tags.
|
||||
setTagState();
|
||||
|
||||
if ($scope.showingHistory) {
|
||||
loadTimeline();
|
||||
}
|
||||
});
|
||||
|
||||
var loadTimeline = function() {
|
||||
var params = {
|
||||
'repository': $scope.repository.namespace + '/' + $scope.repository.name
|
||||
};
|
||||
|
||||
ApiService.listRepoTags(null, params).then(function(resp) {
|
||||
var tagData = [];
|
||||
var currentTags = {};
|
||||
|
||||
resp.tags.forEach(function(tag) {
|
||||
var tagName = tag.name;
|
||||
var imageId = tag.docker_image_id;
|
||||
var oldImageId = null;
|
||||
|
||||
if (tag.end_ts) {
|
||||
var action = 'delete';
|
||||
|
||||
// If the end time matches the existing start time for this tag, then this is a move
|
||||
// instead of a delete.
|
||||
var currentTime = tag.end_ts * 1000;
|
||||
if (currentTags[tagName] && currentTags[tagName].start_ts == tag.end_ts) {
|
||||
action = 'move';
|
||||
|
||||
// Remove the create.
|
||||
var index = tagData.indexOf(currentTags[tagName]);
|
||||
var createEntry = tagData.splice(index, 1)[0];
|
||||
|
||||
imageId = createEntry.docker_image_id;
|
||||
oldImageId = tag.docker_image_id;
|
||||
}
|
||||
|
||||
// Add the delete/move.
|
||||
tagData.push({
|
||||
'tag_name': tagName,
|
||||
'action': action,
|
||||
'start_ts': tag.start_ts,
|
||||
'end_ts': tag.end_ts,
|
||||
'time': currentTime,
|
||||
'docker_image_id': imageId,
|
||||
'old_docker_image_id': oldImageId
|
||||
})
|
||||
}
|
||||
|
||||
if (tag.start_ts) {
|
||||
var currentTime = tag.start_ts * 1000;
|
||||
var create = {
|
||||
'tag_name': tagName,
|
||||
'action': 'create',
|
||||
'start_ts': tag.start_ts,
|
||||
'end_ts': tag.end_ts,
|
||||
'time': currentTime,
|
||||
'docker_image_id': tag.docker_image_id,
|
||||
'old_docker_image_id': ''
|
||||
};
|
||||
|
||||
tagData.push(create);
|
||||
currentTags[tagName] = create;
|
||||
}
|
||||
});
|
||||
|
||||
tagData.sort(function(a, b) {
|
||||
return b.time - a.time;
|
||||
});
|
||||
|
||||
for (var i = tagData.length - 1; i >= 1; --i) {
|
||||
var current = tagData[i];
|
||||
var next = tagData[i - 1];
|
||||
|
||||
if (new Date(current.time).getDate() != new Date(next.time).getDate()) {
|
||||
tagData.splice(i - 1, 0, {
|
||||
'date_break': true,
|
||||
'date': new Date(current.time)
|
||||
});
|
||||
i--;
|
||||
}
|
||||
}
|
||||
|
||||
if (tagData.length > 0) {
|
||||
tagData.splice(0, 0, {
|
||||
'date_break': true,
|
||||
'date': new Date(tagData[0].time)
|
||||
});
|
||||
}
|
||||
|
||||
$scope.tagHistoryData = tagData;
|
||||
});
|
||||
};
|
||||
|
||||
$scope.getEntryClasses = function(entry, historyFilter) {
|
||||
var classes = entry.action + ' ';
|
||||
if (!historyFilter || !entry.action) {
|
||||
return classes;
|
||||
}
|
||||
|
||||
var parts = (historyFilter || '').split(',');
|
||||
var isMatch = parts.some(function(part) {
|
||||
if (part && entry.tag_name) {
|
||||
isMatch = entry.tag_name.indexOf(part) >= 0;
|
||||
isMatch = isMatch || entry.docker_image_id.indexOf(part) >= 0;
|
||||
isMatch = isMatch || entry.old_docker_image_id.indexOf(part) >= 0;
|
||||
return isMatch;
|
||||
}
|
||||
});
|
||||
|
||||
classes += isMatch ? 'filtered-match' : 'filtered-mismatch';
|
||||
return classes;
|
||||
};
|
||||
|
||||
$scope.showHistory = function(value, opt_tagname) {
|
||||
if (opt_tagname) {
|
||||
$scope.options.historyFilter = opt_tagname;
|
||||
} else {
|
||||
$scope.options.historyFilter = '';
|
||||
}
|
||||
|
||||
if ($scope.showingHistory == value) {
|
||||
return;
|
||||
}
|
||||
|
||||
$scope.showingHistory = value;
|
||||
|
||||
if ($scope.showingHistory) {
|
||||
loadTimeline();
|
||||
}
|
||||
};
|
||||
|
||||
$scope.toggleHistory = function() {
|
||||
$scope.showHistory(!$scope.showingHistory);
|
||||
};
|
||||
|
||||
$scope.trackLineClass = function(index, track_info) {
|
||||
var startIndex = $.inArray(track_info.tags[0], $scope.tags);
|
||||
var endIndex = $.inArray(track_info.tags[track_info.tags.length - 1], $scope.tags);
|
||||
|
@ -166,6 +301,10 @@ angular.module('quay').directive('repoPanelTags', function () {
|
|||
$scope.tagActionHandler.askDeleteMultipleTags(tags);
|
||||
};
|
||||
|
||||
$scope.askAddTag = function(tag) {
|
||||
$scope.tagActionHandler.askAddTag(tag.image_id);
|
||||
};
|
||||
|
||||
$scope.orderBy = function(predicate) {
|
||||
if (predicate == $scope.options.predicate) {
|
||||
$scope.options.reverse = !$scope.options.reverse;
|
||||
|
@ -202,6 +341,22 @@ angular.module('quay').directive('repoPanelTags', function () {
|
|||
return $scope.imageIDFilter(it.image_id, tag);
|
||||
});
|
||||
};
|
||||
|
||||
$scope.getTagNames = function(checked) {
|
||||
var names = checked.map(function(tag) {
|
||||
return tag.name;
|
||||
});
|
||||
|
||||
return names.join(',');
|
||||
};
|
||||
|
||||
$scope.isChecked = function(tagName, checked) {
|
||||
return checked.some(function(tag) {
|
||||
if (tag.name == tagName) {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
};
|
||||
return directiveDefinitionObject;
|
||||
|
|
19
static/js/directives/ui/anchor.js
Normal file
19
static/js/directives/ui/anchor.js
Normal file
|
@ -0,0 +1,19 @@
|
|||
/**
|
||||
* An element which displays its contents wrapped in an <a> tag, but only if the href is not null.
|
||||
*/
|
||||
angular.module('quay').directive('anchor', function () {
|
||||
var directiveDefinitionObject = {
|
||||
priority: 0,
|
||||
templateUrl: '/static/directives/anchor.html',
|
||||
replace: false,
|
||||
transclude: true,
|
||||
restrict: 'C',
|
||||
scope: {
|
||||
'href': '@href',
|
||||
'isOnlyText': '=isOnlyText'
|
||||
},
|
||||
controller: function($scope, $element) {
|
||||
}
|
||||
};
|
||||
return directiveDefinitionObject;
|
||||
});
|
41
static/js/directives/ui/authorized-apps-manager.js
Normal file
41
static/js/directives/ui/authorized-apps-manager.js
Normal file
|
@ -0,0 +1,41 @@
|
|||
/**
|
||||
* Element for managing the applications authorized by a user.
|
||||
*/
|
||||
angular.module('quay').directive('authorizedAppsManager', function () {
|
||||
var directiveDefinitionObject = {
|
||||
priority: 0,
|
||||
templateUrl: '/static/directives/authorized-apps-manager.html',
|
||||
replace: false,
|
||||
transclude: false,
|
||||
restrict: 'C',
|
||||
scope: {
|
||||
'user': '=user',
|
||||
'isEnabled': '=isEnabled'
|
||||
},
|
||||
controller: function($scope, $element, ApiService) {
|
||||
$scope.$watch('isEnabled', function(enabled) {
|
||||
if (!enabled) { return; }
|
||||
loadAuthedApps();
|
||||
});
|
||||
|
||||
var loadAuthedApps = function() {
|
||||
if ($scope.authorizedAppsResource) { return; }
|
||||
|
||||
$scope.authorizedAppsResource = ApiService.listUserAuthorizationsAsResource().get(function(resp) {
|
||||
$scope.authorizedApps = resp['authorizations'];
|
||||
});
|
||||
};
|
||||
|
||||
$scope.deleteAccess = function(accessTokenInfo) {
|
||||
var params = {
|
||||
'access_token_uuid': accessTokenInfo['uuid']
|
||||
};
|
||||
|
||||
ApiService.deleteUserAuthorization(null, params).then(function(resp) {
|
||||
$scope.authorizedApps.splice($scope.authorizedApps.indexOf(accessTokenInfo), 1);
|
||||
}, ApiService.errorDisplay('Could not revoke authorization'));
|
||||
};
|
||||
}
|
||||
};
|
||||
return directiveDefinitionObject;
|
||||
});
|
|
@ -1,5 +1,5 @@
|
|||
/**
|
||||
* An element which displays an avatar for the given {email,name} or hash.
|
||||
* An element which displays an avatar for the given avatar data.
|
||||
*/
|
||||
angular.module('quay').directive('avatar', function () {
|
||||
var directiveDefinitionObject = {
|
||||
|
@ -9,25 +9,38 @@ angular.module('quay').directive('avatar', function () {
|
|||
transclude: true,
|
||||
restrict: 'C',
|
||||
scope: {
|
||||
'hash': '=hash',
|
||||
'email': '=email',
|
||||
'name': '=name',
|
||||
'data': '=data',
|
||||
'size': '=size'
|
||||
},
|
||||
controller: function($scope, $element, AvatarService) {
|
||||
controller: function($scope, $element, AvatarService, Config, UIService, $timeout) {
|
||||
$scope.AvatarService = AvatarService;
|
||||
$scope.Config = Config;
|
||||
$scope.isLoading = true;
|
||||
$scope.hasGravatar = false;
|
||||
$scope.loadGravatar = false;
|
||||
|
||||
var refreshHash = function() {
|
||||
if (!$scope.name && !$scope.email) { return; }
|
||||
$scope._hash = AvatarService.computeHash($scope.email, $scope.name);
|
||||
$scope.imageCallback = function(r) {
|
||||
$timeout(function() {
|
||||
$scope.isLoading = false;
|
||||
$scope.hasGravatar = r;
|
||||
}, 1);
|
||||
};
|
||||
|
||||
$scope.$watch('hash', function(hash) {
|
||||
$scope._hash = hash;
|
||||
$scope.$watch('size', function(size) {
|
||||
size = size * 1 || 16;
|
||||
$scope.fontSize = (size - 4) + 'px';
|
||||
$scope.lineHeight = size + 'px';
|
||||
});
|
||||
|
||||
$scope.$watch('name', refreshHash);
|
||||
$scope.$watch('email', refreshHash);
|
||||
$scope.$watch('data', function(data) {
|
||||
if (!data) { return; }
|
||||
|
||||
$scope.loadGravatar = Config.AVATAR_KIND == 'gravatar' &&
|
||||
(data.kind == 'user' || data.kind == 'org');
|
||||
|
||||
$scope.isLoading = $scope.loadGravatar;
|
||||
$scope.hasGravatar = false;
|
||||
});
|
||||
}
|
||||
};
|
||||
return directiveDefinitionObject;
|
||||
|
|
|
@ -15,11 +15,6 @@ angular.module('quay').directive('billingInvoices', function () {
|
|||
},
|
||||
controller: function($scope, $element, $sce, ApiService) {
|
||||
$scope.loading = false;
|
||||
$scope.invoiceExpanded = {};
|
||||
|
||||
$scope.toggleInvoice = function(id) {
|
||||
$scope.invoiceExpanded[id] = !$scope.invoiceExpanded[id];
|
||||
};
|
||||
|
||||
var update = function() {
|
||||
var hasValidUser = !!$scope.user;
|
||||
|
@ -35,6 +30,9 @@ angular.module('quay').directive('billingInvoices', function () {
|
|||
ApiService.listInvoices($scope.organization).then(function(resp) {
|
||||
$scope.invoices = resp.invoices;
|
||||
$scope.loading = false;
|
||||
}, function() {
|
||||
$scope.invoices = [];
|
||||
$scope.loading = false;
|
||||
});
|
||||
};
|
||||
|
|
@ -9,7 +9,8 @@ angular.module('quay').directive('buildMiniStatus', function () {
|
|||
transclude: false,
|
||||
restrict: 'C',
|
||||
scope: {
|
||||
'build': '=build'
|
||||
'build': '=build',
|
||||
'isAdmin': '=isAdmin'
|
||||
},
|
||||
controller: function($scope, $element) {
|
||||
$scope.isBuilding = function(build) {
|
||||
|
|
62
static/js/directives/ui/convert-user-to-org.js
Normal file
62
static/js/directives/ui/convert-user-to-org.js
Normal file
|
@ -0,0 +1,62 @@
|
|||
/**
|
||||
* Displays a panel for converting the current user to an organization.
|
||||
*/
|
||||
angular.module('quay').directive('convertUserToOrg', function () {
|
||||
var directiveDefinitionObject = {
|
||||
priority: 0,
|
||||
templateUrl: '/static/directives/convert-user-to-org.html',
|
||||
replace: false,
|
||||
transclude: true,
|
||||
restrict: 'C',
|
||||
scope: {
|
||||
'user': '=user'
|
||||
},
|
||||
controller: function($scope, $element, Features, PlanService, Config) {
|
||||
$scope.convertStep = 0;
|
||||
|
||||
$scope.showConvertForm = function() {
|
||||
if (Features.BILLING) {
|
||||
PlanService.getMatchingBusinessPlan(function(plan) {
|
||||
$scope.org.plan = plan;
|
||||
});
|
||||
|
||||
PlanService.getPlans(function(plans) {
|
||||
$scope.orgPlans = plans;
|
||||
});
|
||||
}
|
||||
|
||||
$scope.convertStep = 1;
|
||||
};
|
||||
|
||||
$scope.convertToOrg = function() {
|
||||
$('#reallyconvertModal').modal({});
|
||||
};
|
||||
|
||||
$scope.reallyConvert = function() {
|
||||
if (Config.AUTHENTICATION_TYPE != 'Database') { return; }
|
||||
|
||||
$scope.loading = true;
|
||||
|
||||
var data = {
|
||||
'adminUser': $scope.org.adminUser,
|
||||
'adminPassword': $scope.org.adminPassword,
|
||||
'plan': $scope.org.plan ? $scope.org.plan.stripeId : ''
|
||||
};
|
||||
|
||||
ApiService.convertUserToOrganization(data).then(function(resp) {
|
||||
CookieService.putPermanent('quay.namespace', $scope.cuser.username);
|
||||
UserService.load();
|
||||
$location.path('/');
|
||||
}, function(resp) {
|
||||
$scope.loading = false;
|
||||
if (resp.data.reason == 'invaliduser') {
|
||||
$('#invalidadminModal').modal({});
|
||||
} else {
|
||||
$('#cannotconvertModal').modal({});
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
};
|
||||
return directiveDefinitionObject;
|
||||
});
|
|
@ -39,6 +39,21 @@ angular.module('quay').directive('entityReference', function () {
|
|||
return '/organization/' + org['name'] + '/admin?tab=robots&showRobot=' + UtilService.textToSafeHtml(name);
|
||||
};
|
||||
|
||||
$scope.getTitle = function(entity) {
|
||||
if (!entity) { return ''; }
|
||||
|
||||
switch (entity.kind) {
|
||||
case 'org':
|
||||
return 'Organization';
|
||||
|
||||
case 'team':
|
||||
return 'Team';
|
||||
|
||||
case 'user':
|
||||
return entity.is_robot ? 'Robot Account' : 'User';
|
||||
}
|
||||
};
|
||||
|
||||
$scope.getPrefix = function(name) {
|
||||
if (!name) { return ''; }
|
||||
var plus = name.indexOf('+');
|
||||
|
|
|
@ -56,6 +56,8 @@ angular.module('quay').directive('entitySearch', function () {
|
|||
|
||||
$scope.currentEntityInternal = $scope.currentEntity;
|
||||
|
||||
$scope.Config = Config;
|
||||
|
||||
var isSupported = function(kind, opt_array) {
|
||||
return $.inArray(kind, opt_array || $scope.allowedEntities || ['user', 'team', 'robot']) >= 0;
|
||||
};
|
||||
|
@ -90,48 +92,25 @@ angular.module('quay').directive('entitySearch', function () {
|
|||
};
|
||||
|
||||
$scope.createTeam = function() {
|
||||
if (!$scope.isAdmin) { return; }
|
||||
|
||||
bootbox.prompt('Enter the name of the new team', function(teamname) {
|
||||
if (!teamname) { return; }
|
||||
|
||||
var regex = new RegExp(TEAM_PATTERN);
|
||||
if (!regex.test(teamname)) {
|
||||
bootbox.alert('Invalid team name');
|
||||
return;
|
||||
}
|
||||
|
||||
CreateService.createOrganizationTeam(ApiService, $scope.namespace, teamname, function(created) {
|
||||
$scope.setEntity(created.name, 'team', false);
|
||||
$scope.teams[teamname] = created;
|
||||
});
|
||||
CreateService.askCreateTeam($scope.namespace, function(created) {
|
||||
$scope.setEntity(created.name, 'team', false, created.avatar);
|
||||
$scope.teams[teamname] = created;
|
||||
});
|
||||
};
|
||||
|
||||
$scope.createRobot = function() {
|
||||
if (!$scope.isAdmin) { return; }
|
||||
|
||||
bootbox.prompt('Enter the name of the new robot account', function(robotname) {
|
||||
if (!robotname) { return; }
|
||||
|
||||
var regex = new RegExp(ROBOT_PATTERN);
|
||||
if (!regex.test(robotname)) {
|
||||
bootbox.alert('Invalid robot account name');
|
||||
return;
|
||||
}
|
||||
|
||||
CreateService.createRobotAccount(ApiService, $scope.isOrganization, $scope.namespace, robotname, function(created) {
|
||||
$scope.setEntity(created.name, 'user', true);
|
||||
$scope.robots.push(created);
|
||||
});
|
||||
CreateService.askCreateRobot($scope.namespace, function(created) {
|
||||
$scope.setEntity(created.name, 'user', true, created.avatar);
|
||||
$scope.robots.push(created);
|
||||
});
|
||||
};
|
||||
|
||||
$scope.setEntity = function(name, kind, is_robot) {
|
||||
$scope.setEntity = function(name, kind, is_robot, avatar) {
|
||||
var entity = {
|
||||
'name': name,
|
||||
'kind': kind,
|
||||
'is_robot': is_robot
|
||||
'is_robot': is_robot,
|
||||
'avatar': avatar
|
||||
};
|
||||
|
||||
if ($scope.isOrganization) {
|
||||
|
|
|
@ -11,6 +11,7 @@ angular.module('quay').directive('externalLoginButton', function () {
|
|||
scope: {
|
||||
'signInStarted': '&signInStarted',
|
||||
'redirectUrl': '=redirectUrl',
|
||||
'isLink': '=isLink',
|
||||
'provider': '@provider',
|
||||
'action': '@action'
|
||||
},
|
||||
|
|
55
static/js/directives/ui/external-logins-manager.js
Normal file
55
static/js/directives/ui/external-logins-manager.js
Normal file
|
@ -0,0 +1,55 @@
|
|||
/**
|
||||
* Element for managing the applications authorized by a user.
|
||||
*/
|
||||
angular.module('quay').directive('externalLoginsManager', function () {
|
||||
var directiveDefinitionObject = {
|
||||
priority: 0,
|
||||
templateUrl: '/static/directives/external-logins-manager.html',
|
||||
replace: false,
|
||||
transclude: false,
|
||||
restrict: 'C',
|
||||
scope: {
|
||||
'user': '=user',
|
||||
},
|
||||
controller: function($scope, $element, ApiService, UserService, Features, Config, KeyService) {
|
||||
$scope.Features = Features;
|
||||
$scope.Config = Config;
|
||||
$scope.KeyService = KeyService;
|
||||
|
||||
UserService.updateUserIn($scope, function(user) {
|
||||
$scope.cuser = jQuery.extend({}, user);
|
||||
|
||||
if ($scope.cuser.logins) {
|
||||
for (var i = 0; i < $scope.cuser.logins.length; i++) {
|
||||
var login = $scope.cuser.logins[i];
|
||||
login.metadata = login.metadata || {};
|
||||
|
||||
if (login.service == 'github') {
|
||||
$scope.hasGithubLogin = true;
|
||||
$scope.githubLogin = login.metadata['service_username'];
|
||||
$scope.githubEndpoint = KeyService['githubEndpoint'];
|
||||
}
|
||||
|
||||
if (login.service == 'google') {
|
||||
$scope.hasGoogleLogin = true;
|
||||
$scope.googleLogin = login.metadata['service_username'];
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$scope.detachExternalLogin = function(kind) {
|
||||
var params = {
|
||||
'servicename': kind
|
||||
};
|
||||
|
||||
ApiService.detachExternalLogin(null, params).then(function() {
|
||||
$scope.hasGithubLogin = false;
|
||||
$scope.hasGoogleLogin = false;
|
||||
UserService.load();
|
||||
}, ApiService.errorDisplay('Count not detach service'));
|
||||
};
|
||||
}
|
||||
};
|
||||
return directiveDefinitionObject;
|
||||
});
|
114
static/js/directives/ui/fetch-tag-dialog.js
Normal file
114
static/js/directives/ui/fetch-tag-dialog.js
Normal file
|
@ -0,0 +1,114 @@
|
|||
/**
|
||||
* An element which adds a of dialog for fetching a tag.
|
||||
*/
|
||||
angular.module('quay').directive('fetchTagDialog', function () {
|
||||
var directiveDefinitionObject = {
|
||||
priority: 0,
|
||||
templateUrl: '/static/directives/fetch-tag-dialog.html',
|
||||
replace: false,
|
||||
transclude: false,
|
||||
restrict: 'C',
|
||||
scope: {
|
||||
'repository': '=repository',
|
||||
'actionHandler': '=actionHandler'
|
||||
},
|
||||
controller: function($scope, $element, $timeout, ApiService, UserService, Config) {
|
||||
$scope.clearCounter = 0;
|
||||
$scope.currentFormat = null;
|
||||
$scope.currentEntity = null;
|
||||
$scope.currentRobot = null;
|
||||
$scope.formats = [];
|
||||
|
||||
UserService.updateUserIn($scope, updateFormats);
|
||||
|
||||
var updateFormats = function() {
|
||||
$scope.formats = [];
|
||||
|
||||
if ($scope.repository && UserService.isNamespaceAdmin($scope.repository.namespace)) {
|
||||
$scope.formats.push({
|
||||
'title': 'Squashed Docker Image',
|
||||
'icon': 'ci-squashed',
|
||||
'command': 'curl -L -f {http}://{pull_user}:{pull_password}@{hostname}/c1/squash/{namespace}/{name}/{tag} | docker load',
|
||||
'require_creds': true
|
||||
});
|
||||
}
|
||||
|
||||
$scope.formats.push({
|
||||
'title': 'Basic Docker Pull',
|
||||
'icon': 'docker-icon',
|
||||
'command': 'docker pull {hostname}/{namespace}/{name}:{tag}'
|
||||
});
|
||||
};
|
||||
|
||||
$scope.$watch('currentEntity', function(entity) {
|
||||
if (!entity) {
|
||||
$scope.currentRobot = null;
|
||||
return;
|
||||
}
|
||||
|
||||
if ($scope.currentRobot && $scope.currentRobot.name == entity.name) {
|
||||
return;
|
||||
}
|
||||
|
||||
$scope.currentRobot = null;
|
||||
|
||||
var parts = entity.name.split('+');
|
||||
var namespace = parts[0];
|
||||
var shortname = parts[1];
|
||||
|
||||
var params = {
|
||||
'robot_shortname': shortname
|
||||
};
|
||||
|
||||
var orgname = UserService.isOrganization(namespace) ? namespace : '';
|
||||
ApiService.getRobot(orgname, null, params).then(function(resp) {
|
||||
$scope.currentRobot = resp;
|
||||
}, ApiService.errorDisplay('Cannot download robot token'));
|
||||
});
|
||||
|
||||
$scope.getCommand = function(format, robot) {
|
||||
if (!format || !format.command) { return ''; }
|
||||
if (format.require_creds && !robot) { return ''; }
|
||||
|
||||
var params = {
|
||||
'pull_user': robot ? robot.name : '',
|
||||
'pull_password': robot ? robot.token : '',
|
||||
'hostname': Config.getDomain(),
|
||||
'http': Config.getHttp(),
|
||||
'namespace': $scope.repository.namespace,
|
||||
'name': $scope.repository.name,
|
||||
'tag': $scope.currentTag.name
|
||||
};
|
||||
|
||||
var value = format.command;
|
||||
for (var param in params) {
|
||||
if (!params.hasOwnProperty(param)) { continue; }
|
||||
value = value.replace('{' + param + '}', params[param]);
|
||||
}
|
||||
|
||||
return value;
|
||||
};
|
||||
|
||||
$scope.setFormat = function(format) {
|
||||
$scope.currentFormat = format;
|
||||
};
|
||||
|
||||
$scope.actionHandler = {
|
||||
'askFetchTag': function(tag) {
|
||||
$scope.currentTag = tag;
|
||||
$scope.currentFormat = null;
|
||||
$scope.currentEntity = null;
|
||||
$scope.currentRobot = null;
|
||||
|
||||
$scope.clearCounter++;
|
||||
|
||||
updateFormats();
|
||||
|
||||
$element.find('#copyClipboard').clipboardCopy();
|
||||
$element.find('#fetchTagDialog').modal({});
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
return directiveDefinitionObject;
|
||||
});
|
|
@ -12,12 +12,81 @@ angular.module('quay').directive('headerBar', function () {
|
|||
restrict: 'C',
|
||||
scope: {
|
||||
},
|
||||
controller: function($scope, $element, $location, UserService, PlanService, ApiService, NotificationService, Config) {
|
||||
controller: function($rootScope, $scope, $element, $location, $timeout, hotkeys, UserService, PlanService, ApiService, NotificationService, Config, CreateService) {
|
||||
$scope.isNewLayout = Config.isNewLayout();
|
||||
|
||||
if ($scope.isNewLayout) {
|
||||
// Register hotkeys:
|
||||
hotkeys.add({
|
||||
combo: '/',
|
||||
description: 'Show search',
|
||||
callback: function(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
$scope.toggleSearch();
|
||||
}
|
||||
});
|
||||
|
||||
hotkeys.add({
|
||||
combo: 'alt+c',
|
||||
description: 'Create new repository',
|
||||
callback: function(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
$location.url('/new');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$scope.notificationService = NotificationService;
|
||||
$scope.searchVisible = false;
|
||||
$scope.currentSearchQuery = null;
|
||||
$scope.searchResultState = null;
|
||||
$scope.showBuildDialogCounter = 0;
|
||||
|
||||
// Monitor any user changes and place the current user into the scope.
|
||||
UserService.updateUserIn($scope);
|
||||
|
||||
$scope.currentPageContext = {};
|
||||
|
||||
$rootScope.$watch('currentPage.scope.viewuser', function(u) {
|
||||
$scope.currentPageContext['viewuser'] = u;
|
||||
});
|
||||
|
||||
$rootScope.$watch('currentPage.scope.organization', function(o) {
|
||||
$scope.currentPageContext['organization'] = o;
|
||||
});
|
||||
|
||||
$rootScope.$watch('currentPage.scope.repository', function(r) {
|
||||
$scope.currentPageContext['repository'] = r;
|
||||
});
|
||||
|
||||
var conductSearch = function(query) {
|
||||
if (!query) { $scope.searchResultState = null; return; }
|
||||
|
||||
$scope.searchResultState = {
|
||||
'state': 'loading'
|
||||
};
|
||||
|
||||
var params = {
|
||||
'query': query
|
||||
};
|
||||
|
||||
ApiService.conductSearch(null, params).then(function(resp) {
|
||||
if (!$scope.searchVisible || query != $scope.currentSearchQuery) { return; }
|
||||
|
||||
$scope.searchResultState = {
|
||||
'state': resp.results.length ? 'results' : 'no-results',
|
||||
'results': resp.results,
|
||||
'current': resp.results.length ? 0 : -1
|
||||
};
|
||||
}, function(resp) {
|
||||
$scope.searchResultState = null;
|
||||
}, /* background */ true);
|
||||
};
|
||||
|
||||
$scope.$watch('currentSearchQuery', conductSearch);
|
||||
|
||||
$scope.signout = function() {
|
||||
ApiService.logout().then(function() {
|
||||
UserService.load();
|
||||
|
@ -39,6 +108,126 @@ angular.module('quay').directive('headerBar', function () {
|
|||
|
||||
return Config.ENTERPRISE_LOGO_URL;
|
||||
};
|
||||
|
||||
$scope.toggleSearch = function() {
|
||||
$scope.searchVisible = !$scope.searchVisible;
|
||||
if ($scope.searchVisible) {
|
||||
$('#search-box-input').focus();
|
||||
if ($scope.currentSearchQuery) {
|
||||
conductSearch($scope.currentSearchQuery);
|
||||
}
|
||||
} else {
|
||||
$('#search-box-input').blur()
|
||||
$scope.searchResultState = null;
|
||||
}
|
||||
};
|
||||
|
||||
$scope.getSearchBoxClasses = function(searchVisible, searchResultState) {
|
||||
var classes = searchVisible ? 'search-visible ' : '';
|
||||
if (searchResultState) {
|
||||
classes += 'results-visible';
|
||||
}
|
||||
return classes;
|
||||
};
|
||||
|
||||
$scope.handleSearchKeyDown = function(e) {
|
||||
if (e.keyCode == 27) {
|
||||
$scope.toggleSearch();
|
||||
return;
|
||||
}
|
||||
|
||||
var state = $scope.searchResultState;
|
||||
if (!state || !state['results']) { return; }
|
||||
|
||||
if (e.keyCode == 40) {
|
||||
state['current']++;
|
||||
e.preventDefault();
|
||||
} else if (e.keyCode == 38) {
|
||||
state['current']--;
|
||||
e.preventDefault();
|
||||
} else if (e.keyCode == 13) {
|
||||
var current = state['current'];
|
||||
if (current >= 0 && current < state['results'].length) {
|
||||
$scope.showResult(state['results'][current]);
|
||||
}
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
if (state['current'] < -1) {
|
||||
state['current'] = state['results'].length - 1;
|
||||
} else if (state['current'] >= state['results'].length) {
|
||||
state['current'] = 0;
|
||||
}
|
||||
};
|
||||
|
||||
$scope.showResult = function(result) {
|
||||
$scope.toggleSearch();
|
||||
$timeout(function() {
|
||||
$scope.currentSearchQuery = '';
|
||||
$location.url(result['href'])
|
||||
}, 500);
|
||||
};
|
||||
|
||||
$scope.setCurrentResult = function(result) {
|
||||
if (!$scope.searchResultState) { return; }
|
||||
$scope.searchResultState['current'] = result;
|
||||
};
|
||||
|
||||
$scope.getNamespace = function(context) {
|
||||
if (!context) { return null; }
|
||||
|
||||
if (context.repository && context.repository.namespace) {
|
||||
return context.repository.namespace;
|
||||
}
|
||||
|
||||
if (context.organization && context.organization.name) {
|
||||
return context.organization.name;
|
||||
}
|
||||
|
||||
if (context.viewuser && context.viewuser.username) {
|
||||
return context.viewuser.username;
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
$scope.canAdmin = function(namespace) {
|
||||
if (!namespace) { return false; }
|
||||
return UserService.isNamespaceAdmin(namespace);
|
||||
};
|
||||
|
||||
$scope.isOrganization = function(namespace) {
|
||||
if (!namespace) { return false; }
|
||||
return UserService.isOrganization(namespace);
|
||||
};
|
||||
|
||||
$scope.startBuild = function(context) {
|
||||
$scope.showBuildDialogCounter++;
|
||||
};
|
||||
|
||||
$scope.handleBuildStarted = function(build, context) {
|
||||
$location.url('/repository/' + context.repository.namespace + '/' + context.repository.name + '/build/' + build.id);
|
||||
};
|
||||
|
||||
$scope.createRobot = function(context) {
|
||||
var namespace = $scope.getNamespace(context);
|
||||
CreateService.askCreateRobot(function(created) {
|
||||
if (isorg) {
|
||||
$location.url('/organization/' + namespace + '?tab=robots');
|
||||
} else {
|
||||
$location.url('/user/' + namespace + '?tab=robots');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$scope.createTeam = function(context) {
|
||||
var namespace = $scope.getNamespace(context);
|
||||
if (!namespace || !UserService.isNamespaceAdmin(namespace)) { return; }
|
||||
|
||||
CreateService.askCreateTeam(function(created) {
|
||||
$location.url('/organization/' + namespace + '/teams/' + teamname);
|
||||
});
|
||||
};
|
||||
}
|
||||
};
|
||||
return directiveDefinitionObject;
|
||||
|
|
19
static/js/directives/ui/image-link.js
Normal file
19
static/js/directives/ui/image-link.js
Normal file
|
@ -0,0 +1,19 @@
|
|||
/**
|
||||
* An element which displays a link to a repository image.
|
||||
*/
|
||||
angular.module('quay').directive('imageLink', function () {
|
||||
var directiveDefinitionObject = {
|
||||
priority: 0,
|
||||
templateUrl: '/static/directives/image-link.html',
|
||||
replace: false,
|
||||
transclude: true,
|
||||
restrict: 'C',
|
||||
scope: {
|
||||
'repository': '=repository',
|
||||
'imageId': '=imageId'
|
||||
},
|
||||
controller: function($scope, $element) {
|
||||
}
|
||||
};
|
||||
return directiveDefinitionObject;
|
||||
});
|
49
static/js/directives/ui/image-view-layer.js
Normal file
49
static/js/directives/ui/image-view-layer.js
Normal file
|
@ -0,0 +1,49 @@
|
|||
/**
|
||||
* An element which displays a single layer representing an image in the image view.
|
||||
*/
|
||||
angular.module('quay').directive('imageViewLayer', function () {
|
||||
var directiveDefinitionObject = {
|
||||
priority: 0,
|
||||
templateUrl: '/static/directives/image-view-layer.html',
|
||||
replace: false,
|
||||
transclude: true,
|
||||
restrict: 'C',
|
||||
scope: {
|
||||
'repository': '=repository',
|
||||
'image': '=image',
|
||||
'images': '=images'
|
||||
},
|
||||
controller: function($scope, $element) {
|
||||
$scope.getDockerfileCommand = function(command) {
|
||||
if (!command) { return ''; }
|
||||
|
||||
// ["/bin/sh", "-c", "#(nop) RUN foo"]
|
||||
var commandPrefix = '#(nop)'
|
||||
|
||||
if (command.length != 3) { return ''; }
|
||||
if (command[0] != '/bin/sh' || command[1] != '-c') { return ''; }
|
||||
|
||||
var cmd = command[2];
|
||||
if (cmd.substring(0, commandPrefix.length) != commandPrefix) {
|
||||
return 'RUN ' + cmd;
|
||||
}
|
||||
|
||||
return command[2].substr(commandPrefix.length + 1);
|
||||
};
|
||||
|
||||
$scope.getClass = function() {
|
||||
var index = $.inArray($scope.image, $scope.images);
|
||||
if (index < 0) {
|
||||
return 'first';
|
||||
}
|
||||
|
||||
if (index == $scope.images.length - 1) {
|
||||
return 'last';
|
||||
}
|
||||
|
||||
return '';
|
||||
};
|
||||
}
|
||||
};
|
||||
return directiveDefinitionObject;
|
||||
});
|
|
@ -11,9 +11,9 @@ angular.module('quay').directive('repoListGrid', function () {
|
|||
scope: {
|
||||
repositoriesResource: '=repositoriesResource',
|
||||
starred: '=starred',
|
||||
user: "=user",
|
||||
namespace: '=namespace',
|
||||
starToggled: '&starToggled'
|
||||
starToggled: '&starToggled',
|
||||
hideTitle: '=hideTitle'
|
||||
},
|
||||
controller: function($scope, $element, UserService) {
|
||||
$scope.isOrganization = function(namespace) {
|
||||
|
|
|
@ -2,6 +2,21 @@
|
|||
* An element which displays a table of permissions on a repository and allows them to be
|
||||
* edited.
|
||||
*/
|
||||
angular.module('quay').filter('objectFilter', function() {
|
||||
return function(obj, filterFn) {
|
||||
if (!obj) { return []; }
|
||||
|
||||
var result = [];
|
||||
angular.forEach(obj, function(value) {
|
||||
if (filterFn(value)) {
|
||||
result.push(value);
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
};
|
||||
});
|
||||
|
||||
angular.module('quay').directive('repositoryPermissionsTable', function () {
|
||||
var directiveDefinitionObject = {
|
||||
priority: 0,
|
||||
|
@ -13,6 +28,7 @@ angular.module('quay').directive('repositoryPermissionsTable', function () {
|
|||
'repository': '=repository'
|
||||
},
|
||||
controller: function($scope, $element, ApiService, Restangular, UtilService) {
|
||||
// TODO(jschorr): move this to a service.
|
||||
$scope.roles = [
|
||||
{ 'id': 'read', 'title': 'Read', 'kind': 'success' },
|
||||
{ 'id': 'write', 'title': 'Write', 'kind': 'success' },
|
||||
|
@ -58,21 +74,50 @@ angular.module('quay').directive('repositoryPermissionsTable', function () {
|
|||
return Restangular.one(url);
|
||||
};
|
||||
|
||||
$scope.buildEntityForPermission = function(name, permission, kind) {
|
||||
var key = name + ':' + kind;
|
||||
$scope.buildEntityForPermission = function(permission, kind) {
|
||||
var key = permission.name + ':' + kind;
|
||||
if ($scope.permissionCache[key]) {
|
||||
return $scope.permissionCache[key];
|
||||
}
|
||||
|
||||
return $scope.permissionCache[key] = {
|
||||
'kind': kind,
|
||||
'name': name,
|
||||
'name': permission.name,
|
||||
'is_robot': permission.is_robot,
|
||||
'is_org_member': permission.is_org_member
|
||||
'is_org_member': permission.is_org_member,
|
||||
'avatar': permission.avatar
|
||||
};
|
||||
};
|
||||
|
||||
$scope.addPermission = function() {
|
||||
$scope.hasPermissions = function(teams, users) {
|
||||
if (teams && teams.value) {
|
||||
if (Object.keys(teams.value).length > 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (users && users.value) {
|
||||
if (Object.keys(users.value).length > 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
$scope.allEntries = function() {
|
||||
return true;
|
||||
};
|
||||
|
||||
$scope.onlyRobot = function(permission) {
|
||||
return permission.is_robot == true;
|
||||
};
|
||||
|
||||
$scope.onlyUser = function(permission) {
|
||||
return !permission.is_robot;
|
||||
};
|
||||
|
||||
$scope.addPermission = function() {
|
||||
$scope.addPermissionInfo['working'] = true;
|
||||
$scope.addNewPermission($scope.addPermissionInfo.entity, $scope.addPermissionInfo.role)
|
||||
};
|
||||
|
|
|
@ -12,12 +12,38 @@ angular.module('quay').directive('robotsManager', function () {
|
|||
'organization': '=organization',
|
||||
'user': '=user'
|
||||
},
|
||||
controller: function($scope, $element, ApiService, $routeParams, CreateService) {
|
||||
controller: function($scope, $element, ApiService, $routeParams, CreateService, Config) {
|
||||
$scope.ROBOT_PATTERN = ROBOT_PATTERN;
|
||||
|
||||
// TODO(jschorr): move this to a service.
|
||||
$scope.roles = [
|
||||
{ 'id': 'read', 'title': 'Read', 'kind': 'success' },
|
||||
{ 'id': 'write', 'title': 'Write', 'kind': 'success' },
|
||||
{ 'id': 'admin', 'title': 'Admin', 'kind': 'primary' }
|
||||
];
|
||||
|
||||
$scope.robots = null;
|
||||
$scope.loading = false;
|
||||
$scope.shownRobot = null;
|
||||
$scope.showRobotCounter = 0;
|
||||
$scope.Config = Config;
|
||||
|
||||
var loadRobotPermissions = function(info) {
|
||||
var shortName = $scope.getShortenedName(info.name);
|
||||
info.loading_permissions = true;
|
||||
ApiService.getRobotPermissions($scope.organization, null, {'robot_shortname': shortName}).then(function(resp) {
|
||||
info.permissions = resp.permissions;
|
||||
info.loading_permissions = false;
|
||||
}, ApiService.errorDisplay('Could not load robot permissions'));
|
||||
};
|
||||
|
||||
$scope.showPermissions = function(robotInfo) {
|
||||
robotInfo.showing_permissions = !robotInfo.showing_permissions;
|
||||
|
||||
if (robotInfo.showing_permissions) {
|
||||
loadRobotPermissions(robotInfo);
|
||||
}
|
||||
};
|
||||
|
||||
$scope.regenerateToken = function(username) {
|
||||
if (!username) { return; }
|
||||
|
@ -47,6 +73,10 @@ angular.module('quay').directive('robotsManager', function () {
|
|||
return -1;
|
||||
};
|
||||
|
||||
$scope.getShortenedRobotName = function(info) {
|
||||
return $scope.getShortenedName(info.name);
|
||||
};
|
||||
|
||||
$scope.getShortenedName = function(name) {
|
||||
var plus = name.indexOf('+');
|
||||
return name.substr(plus + 1);
|
||||
|
|
|
@ -12,6 +12,7 @@ angular.module('quay').directive('roleGroup', function () {
|
|||
scope: {
|
||||
'roles': '=roles',
|
||||
'currentRole': '=currentRole',
|
||||
'readOnly': '=readOnly',
|
||||
'roleChanged': '&roleChanged'
|
||||
},
|
||||
controller: function($scope, $element) {
|
||||
|
|
131
static/js/directives/ui/teams-manager.js
Normal file
131
static/js/directives/ui/teams-manager.js
Normal file
|
@ -0,0 +1,131 @@
|
|||
/**
|
||||
* Element for managing the teams of an organization.
|
||||
*/
|
||||
angular.module('quay').directive('teamsManager', function () {
|
||||
var directiveDefinitionObject = {
|
||||
priority: 0,
|
||||
templateUrl: '/static/directives/teams-manager.html',
|
||||
replace: false,
|
||||
transclude: false,
|
||||
restrict: 'C',
|
||||
scope: {
|
||||
'organization': '=organization'
|
||||
},
|
||||
controller: function($scope, $element, ApiService, CreateService) {
|
||||
$scope.TEAM_PATTERN = TEAM_PATTERN;
|
||||
$scope.teamRoles = [
|
||||
{ 'id': 'member', 'title': 'Member', 'kind': 'default' },
|
||||
{ 'id': 'creator', 'title': 'Creator', 'kind': 'success' },
|
||||
{ 'id': 'admin', 'title': 'Admin', 'kind': 'primary' }
|
||||
];
|
||||
|
||||
$scope.members = {};
|
||||
$scope.orderedTeams = [];
|
||||
|
||||
var loadTeamMembers = function() {
|
||||
if (!$scope.organization) { return; }
|
||||
|
||||
for (var name in $scope.organization.teams) {
|
||||
if (!$scope.organization.teams.hasOwnProperty(name)) { continue; }
|
||||
loadMembersOfTeam(name);
|
||||
}
|
||||
};
|
||||
|
||||
var loadMembersOfTeam = function(name) {
|
||||
var params = {
|
||||
'orgname': $scope.organization.name,
|
||||
'teamname': name
|
||||
};
|
||||
|
||||
$scope.members[name] = {};
|
||||
|
||||
ApiService.getOrganizationTeamMembers(null, params).then(function(resp) {
|
||||
$scope.members[name].members = resp.members;
|
||||
}, function() {
|
||||
delete $scope.members[name];
|
||||
});
|
||||
};
|
||||
|
||||
var loadOrderedTeams = function() {
|
||||
if (!$scope.organization || !$scope.organization.ordered_teams) { return; }
|
||||
|
||||
$scope.orderedTeams = [];
|
||||
$scope.organization.ordered_teams.map(function(name) {
|
||||
$scope.orderedTeams.push($scope.organization.teams[name]);
|
||||
});
|
||||
};
|
||||
|
||||
$scope.$watch('organization', loadOrderedTeams);
|
||||
$scope.$watch('organization', loadTeamMembers);
|
||||
|
||||
$scope.setRole = function(role, teamname) {
|
||||
var previousRole = $scope.organization.teams[teamname].role;
|
||||
$scope.organization.teams[teamname].role = role;
|
||||
|
||||
var params = {
|
||||
'orgname': $scope.organization.name,
|
||||
'teamname': teamname
|
||||
};
|
||||
|
||||
var data = $scope.organization.teams[teamname];
|
||||
|
||||
var errorHandler = ApiService.errorDisplay('Cannot update team', function(resp) {
|
||||
$scope.organization.teams[teamname].role = previousRole;
|
||||
});
|
||||
|
||||
ApiService.updateOrganizationTeam(data, params).then(function(resp) {
|
||||
}, errorHandler);
|
||||
};
|
||||
|
||||
$scope.createTeam = function(teamname) {
|
||||
if (!teamname) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($scope.organization.teams[teamname]) {
|
||||
$('#team-' + teamname).removeClass('highlight');
|
||||
setTimeout(function() {
|
||||
$('#team-' + teamname).addClass('highlight');
|
||||
}, 10);
|
||||
return;
|
||||
}
|
||||
|
||||
var orgname = $scope.organization.name;
|
||||
CreateService.createOrganizationTeam(ApiService, orgname, teamname, function(created) {
|
||||
$scope.organization.teams[teamname] = created;
|
||||
$scope.members[teamname] = {};
|
||||
$scope.members[teamname].members = [];
|
||||
$scope.organization.ordered_teams.push(teamname);
|
||||
$scope.orderedTeams.push(created);
|
||||
});
|
||||
};
|
||||
|
||||
$scope.askDeleteTeam = function(teamname) {
|
||||
bootbox.confirm('Are you sure you want to delete team ' + teamname + '?', function(resp) {
|
||||
if (resp) {
|
||||
$scope.deleteTeam(teamname);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$scope.deleteTeam = function(teamname) {
|
||||
var params = {
|
||||
'orgname': $scope.organization.name,
|
||||
'teamname': teamname
|
||||
};
|
||||
|
||||
ApiService.deleteOrganizationTeam(null, params).then(function() {
|
||||
var index = $scope.organization.ordered_teams.indexOf(teamname);
|
||||
if (index >= 0) {
|
||||
$scope.organization.ordered_teams.splice(index, 1);
|
||||
}
|
||||
|
||||
loadOrderedTeams();
|
||||
delete $scope.organization.teams[teamname];
|
||||
}, ApiService.errorDisplay('Cannot delete team'));
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
return directiveDefinitionObject;
|
||||
});
|
Reference in a new issue