Merge branch 'master' into comewithmeifyouwanttowork
This commit is contained in:
commit
3b72b26836
62 changed files with 923 additions and 714 deletions
|
@ -473,6 +473,22 @@ i.toggle-icon:hover {
|
|||
|
||||
.docker-auth-dialog .token-dialog-body .well {
|
||||
margin-bottom: 0px;
|
||||
position: relative;
|
||||
padding-right: 24px;
|
||||
}
|
||||
|
||||
.docker-auth-dialog .token-dialog-body .well i.fa-refresh {
|
||||
position: absolute;
|
||||
top: 9px;
|
||||
right: 9px;
|
||||
font-size: 20px;
|
||||
color: gray;
|
||||
transition: all 0.5s ease-in-out;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.docker-auth-dialog .token-dialog-body .well i.fa-refresh:hover {
|
||||
color: black;
|
||||
}
|
||||
|
||||
.docker-auth-dialog .token-view {
|
||||
|
@ -738,7 +754,7 @@ i.toggle-icon:hover {
|
|||
}
|
||||
|
||||
.user-notification.notification-animated {
|
||||
width: 21px;
|
||||
min-width: 21px;
|
||||
|
||||
transform: scale(0);
|
||||
-moz-transform: scale(0);
|
||||
|
@ -832,7 +848,7 @@ i.toggle-icon:hover {
|
|||
background-color: red;
|
||||
}
|
||||
|
||||
.phase-icon.waiting, .phase-icon.starting, .phase-icon.initializing {
|
||||
.phase-icon.waiting, .phase-icon.unpacking, .phase-icon.starting, .phase-icon.initializing {
|
||||
background-color: #ddd;
|
||||
}
|
||||
|
||||
|
@ -2266,6 +2282,14 @@ p.editable:hover i {
|
|||
position: relative;
|
||||
}
|
||||
|
||||
.copy-box-element.disabled .input-group-addon {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.copy-box-element.disabled input {
|
||||
border-radius: 4px !important;
|
||||
}
|
||||
|
||||
.global-zeroclipboard-container embed {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<div class="copy-box-element">
|
||||
<div class="copy-box-element" ng-class="disabled ? 'disabled' : ''">
|
||||
<div class="id-container">
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control" value="{{ value }}" readonly>
|
||||
|
|
|
@ -10,19 +10,33 @@
|
|||
</div>
|
||||
<div class="modal-body token-dialog-body">
|
||||
<div class="alert alert-info">The docker <u>username</u> is <b>{{ username }}</b> and the <u>password</u> is the token below. You may use any value for email.</div>
|
||||
<div class="well well-sm">
|
||||
|
||||
<div class="well well-sm" ng-show="regenerating">
|
||||
Regenerating Token...
|
||||
<i class="fa fa-refresh fa-spin"></i>
|
||||
</div>
|
||||
|
||||
<div class="well well-sm" ng-show="!regenerating">
|
||||
<input id="token-view" class="token-view" type="text" value="{{ token }}" onClick="this.select();" readonly>
|
||||
<i class="fa fa-refresh" ng-show="supportsRegenerate" ng-click="askRegenerate()"
|
||||
data-title="Regenerate Token"
|
||||
data-placement="left"
|
||||
bs-tooltip></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<div class="modal-footer" ng-show="regenerating">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
||||
</div>
|
||||
<div class="modal-footer" ng-show="!regenerating">
|
||||
<span class="download-cfg" ng-show="isDownloadSupported()">
|
||||
<i class="fa fa-download"></i>
|
||||
<a href="javascript:void(0)" ng-click="downloadCfg(shownRobot)">Download .dockercfg file</a>
|
||||
</span>
|
||||
<div id="clipboardCopied" style="display: none">
|
||||
Copied to clipboard
|
||||
<div class="clipboard-copied-message" style="display: none">
|
||||
Copied
|
||||
</div>
|
||||
<button id="copyClipboard" type="button" class="btn btn-primary" data-clipboard-target="token-view">Copy to clipboard</button>
|
||||
<input type="hidden" name="command-data" id="command-data" value="{{ command }}">
|
||||
<button id="copyClipboard" type="button" class="btn btn-primary" data-clipboard-target="command-data">Copy Login Command</button>
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
||||
</div>
|
||||
</div><!-- /.modal-content -->
|
||||
|
|
|
@ -37,15 +37,7 @@
|
|||
<a href="javascript:void(0)" class="dropdown-toggle user-dropdown user-view" data-toggle="dropdown">
|
||||
<img src="//www.gravatar.com/avatar/{{ user.gravatar }}?s=32&d=identicon" />
|
||||
{{ user.username }}
|
||||
<span class="badge user-notification notification-animated"
|
||||
ng-show="notificationService.notifications.length"
|
||||
ng-class="notificationService.notificationClasses"
|
||||
bs-tooltip=""
|
||||
data-title="User Notifications"
|
||||
data-placement="left"
|
||||
data-container="body">
|
||||
{{ notificationService.notifications.length }}
|
||||
</span>
|
||||
<span class="notifications-bubble"></span>
|
||||
<b class="caret"></b>
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
|
@ -58,11 +50,7 @@
|
|||
<a href="javascript:void(0)" data-template="/static/directives/notification-bar.html"
|
||||
data-animation="am-slide-right" bs-aside="aside" data-container="body">
|
||||
Notifications
|
||||
<span class="badge user-notification"
|
||||
ng-class="notificationService.notificationClasses"
|
||||
ng-show="notificationService.notifications.length">
|
||||
{{ notificationService.notifications.length }}
|
||||
</span>
|
||||
<span class="notifications-bubble"></span>
|
||||
</a>
|
||||
</li>
|
||||
<li><a ng-href="/organizations/" target="{{ appLinkTarget() }}">Organizations</a></li>
|
||||
|
|
|
@ -3,7 +3,10 @@
|
|||
<div class="aside-content">
|
||||
<div class="aside-header">
|
||||
<button type="button" class="close" ng-click="$hide()">×</button>
|
||||
<h4 class="aside-title">Notifications</h4>
|
||||
<h4 class="aside-title">
|
||||
Notifications
|
||||
<span class="notifications-bubble"></span>
|
||||
</h4>
|
||||
</div>
|
||||
<div class="aside-body">
|
||||
<div ng-repeat="notification in notificationService.notifications">
|
||||
|
|
7
static/directives/notifications-bubble.html
Normal file
7
static/directives/notifications-bubble.html
Normal file
|
@ -0,0 +1,7 @@
|
|||
<span class="notifications-bubble-element">
|
||||
<span class="badge user-notification notification-animated"
|
||||
ng-show="notificationService.notifications.length"
|
||||
ng-class="notificationService.notificationClasses">
|
||||
{{ notificationService.notifications.length }}<span ng-if="notificationService.additionalNotifications">+</span>
|
||||
</span>
|
||||
</span>
|
|
@ -31,7 +31,7 @@
|
|||
</div>
|
||||
|
||||
<div class="docker-auth-dialog" username="shownRobot.name" token="shownRobot.token"
|
||||
shown="!!shownRobot" counter="showRobotCounter">
|
||||
shown="!!shownRobot" counter="showRobotCounter" supports-regenerate="true" regenerate="regenerateToken(username)">
|
||||
<i class="fa fa-wrench"></i> {{ shownRobot.name }}
|
||||
</div>
|
||||
</div>
|
||||
|
|
351
static/js/app.js
351
static/js/app.js
|
@ -1,6 +1,46 @@
|
|||
var TEAM_PATTERN = '^[a-zA-Z][a-zA-Z0-9]+$';
|
||||
var ROBOT_PATTERN = '^[a-zA-Z][a-zA-Z0-9]{3,29}$';
|
||||
|
||||
$.fn.clipboardCopy = function() {
|
||||
if (zeroClipboardSupported) {
|
||||
(new ZeroClipboard($(this)));
|
||||
return true;
|
||||
}
|
||||
|
||||
this.hide();
|
||||
return false;
|
||||
};
|
||||
|
||||
var zeroClipboardSupported = true;
|
||||
ZeroClipboard.config({
|
||||
'swfPath': 'static/lib/ZeroClipboard.swf'
|
||||
});
|
||||
|
||||
ZeroClipboard.on("error", function(e) {
|
||||
zeroClipboardSupported = false;
|
||||
});
|
||||
|
||||
ZeroClipboard.on('aftercopy', function(e) {
|
||||
var container = e.target.parentNode.parentNode.parentNode;
|
||||
var message = $(container).find('.clipboard-copied-message')[0];
|
||||
|
||||
// Resets the animation.
|
||||
var elem = message;
|
||||
elem.style.display = 'none';
|
||||
elem.classList.remove('animated');
|
||||
|
||||
// Show the notification.
|
||||
setTimeout(function() {
|
||||
elem.style.display = 'inline-block';
|
||||
elem.classList.add('animated');
|
||||
}, 10);
|
||||
|
||||
// Reset the notification.
|
||||
setTimeout(function() {
|
||||
elem.style.display = 'none';
|
||||
}, 5000);
|
||||
});
|
||||
|
||||
function getRestUrl(args) {
|
||||
var url = '';
|
||||
for (var i = 0; i < arguments.length; ++i) {
|
||||
|
@ -59,18 +99,8 @@ function getFirstTextLine(commentString) {
|
|||
}
|
||||
|
||||
function createRobotAccount(ApiService, is_org, orgname, name, callback) {
|
||||
ApiService.createRobot(is_org ? orgname : null, null, {'robot_shortname': name}).then(callback, function(resp) {
|
||||
bootbox.dialog({
|
||||
"message": resp.data ? resp.data['message'] : 'The robot account could not be created',
|
||||
"title": "Cannot create robot account",
|
||||
"buttons": {
|
||||
"close": {
|
||||
"label": "Close",
|
||||
"className": "btn-primary"
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
ApiService.createRobot(is_org ? orgname : null, null, {'robot_shortname': name})
|
||||
.then(callback, ApiService.errorDisplay('Cannot create robot account'));
|
||||
}
|
||||
|
||||
function createOrganizationTeam(ApiService, orgname, teamname, callback) {
|
||||
|
@ -84,18 +114,8 @@ function createOrganizationTeam(ApiService, orgname, teamname, callback) {
|
|||
'teamname': teamname
|
||||
};
|
||||
|
||||
ApiService.updateOrganizationTeam(data, params).then(callback, function(resp) {
|
||||
bootbox.dialog({
|
||||
"message": resp.data ? resp.data : 'The team could not be created',
|
||||
"title": "Cannot create team",
|
||||
"buttons": {
|
||||
"close": {
|
||||
"label": "Close",
|
||||
"className": "btn-primary"
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
ApiService.updateOrganizationTeam(data, params)
|
||||
.then(callback, ApiService.errorDisplay('Cannot create team'));
|
||||
}
|
||||
|
||||
function getMarkedDown(string) {
|
||||
|
@ -870,6 +890,38 @@ quayApp = angular.module('quay', quayDependencies, function($provide, cfpLoading
|
|||
buildMethodsForEndpointResource(endpointResource, resourceMap);
|
||||
}
|
||||
|
||||
apiService.getErrorMessage = function(resp, defaultMessage) {
|
||||
var message = defaultMessage;
|
||||
if (resp['data']) {
|
||||
message = resp['data']['error_message'] || resp['data']['message'] || resp['data']['error_description'] || message;
|
||||
}
|
||||
|
||||
return message;
|
||||
};
|
||||
|
||||
apiService.errorDisplay = function(defaultMessage, opt_handler) {
|
||||
return function(resp) {
|
||||
var message = apiService.getErrorMessage(resp, defaultMessage);
|
||||
if (opt_handler) {
|
||||
var handlerMessage = opt_handler(resp);
|
||||
if (handlerMessage) {
|
||||
message = handlerMessage;
|
||||
}
|
||||
}
|
||||
|
||||
bootbox.dialog({
|
||||
"message": message,
|
||||
"title": defaultMessage,
|
||||
"buttons": {
|
||||
"close": {
|
||||
"label": "Close",
|
||||
"className": "btn-primary"
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
return apiService;
|
||||
}]);
|
||||
|
||||
|
@ -1126,7 +1178,8 @@ quayApp = angular.module('quay', quayDependencies, function($provide, cfpLoading
|
|||
'user': null,
|
||||
'notifications': [],
|
||||
'notificationClasses': [],
|
||||
'notificationSummaries': []
|
||||
'notificationSummaries': [],
|
||||
'additionalNotifications': false
|
||||
};
|
||||
|
||||
var pollTimerHandle = null;
|
||||
|
@ -1244,7 +1297,9 @@ quayApp = angular.module('quay', quayDependencies, function($provide, cfpLoading
|
|||
'uuid': notification.id
|
||||
};
|
||||
|
||||
ApiService.updateUserNotification(notification, params);
|
||||
ApiService.updateUserNotification(notification, params, function() {
|
||||
notificationService.update();
|
||||
}, ApiService.errorDisplay('Could not update notification'));
|
||||
|
||||
var index = $.inArray(notification, notificationService.notifications);
|
||||
if (index >= 0) {
|
||||
|
@ -1310,6 +1365,7 @@ quayApp = angular.module('quay', quayDependencies, function($provide, cfpLoading
|
|||
|
||||
ApiService.listUserNotifications().then(function(resp) {
|
||||
notificationService.notifications = resp['notifications'];
|
||||
notificationService.additionalNotifications = resp['additional'];
|
||||
notificationService.notificationClasses = notificationService.getClasses(notificationService.notifications);
|
||||
});
|
||||
};
|
||||
|
@ -1541,7 +1597,7 @@ quayApp = angular.module('quay', quayDependencies, function($provide, cfpLoading
|
|||
});
|
||||
};
|
||||
|
||||
planService.changePlan = function($scope, orgname, planId, callbacks) {
|
||||
planService.changePlan = function($scope, orgname, planId, callbacks, opt_async) {
|
||||
if (!Features.BILLING) { return; }
|
||||
|
||||
if (callbacks['started']) {
|
||||
|
@ -1554,7 +1610,7 @@ quayApp = angular.module('quay', quayDependencies, function($provide, cfpLoading
|
|||
planService.getCardInfo(orgname, function(cardInfo) {
|
||||
if (plan.price > 0 && (previousSubscribeFailure || !cardInfo.last4)) {
|
||||
var title = cardInfo.last4 ? 'Subscribe' : 'Start Trial ({{amount}} plan)';
|
||||
planService.showSubscribeDialog($scope, orgname, planId, callbacks, title);
|
||||
planService.showSubscribeDialog($scope, orgname, planId, callbacks, title, /* async */true);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1627,9 +1683,34 @@ quayApp = angular.module('quay', quayDependencies, function($provide, cfpLoading
|
|||
return email;
|
||||
};
|
||||
|
||||
planService.showSubscribeDialog = function($scope, orgname, planId, callbacks, opt_title) {
|
||||
planService.showSubscribeDialog = function($scope, orgname, planId, callbacks, opt_title, opt_async) {
|
||||
if (!Features.BILLING) { return; }
|
||||
|
||||
// If the async parameter is true and this is a browser that does not allow async popup of the
|
||||
// Stripe dialog (such as Mobile Safari or IE), show a bootbox to show the dialog instead.
|
||||
var isIE = navigator.appName.indexOf("Internet Explorer") != -1;
|
||||
var isMobileSafari = navigator.userAgent.match(/(iPod|iPhone|iPad)/) && navigator.userAgent.match(/AppleWebKit/);
|
||||
|
||||
if (opt_async && (isIE || isMobileSafari)) {
|
||||
bootbox.dialog({
|
||||
"message": "Please click 'Subscribe' to continue",
|
||||
"buttons": {
|
||||
"subscribe": {
|
||||
"label": "Subscribe",
|
||||
"className": "btn-primary",
|
||||
"callback": function() {
|
||||
planService.showSubscribeDialog($scope, orgname, planId, callbacks, opt_title, false);
|
||||
}
|
||||
},
|
||||
"close": {
|
||||
"label": "Cancel",
|
||||
"className": "btn-default"
|
||||
}
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (callbacks['opening']) {
|
||||
callbacks['opening']();
|
||||
}
|
||||
|
@ -2084,18 +2165,7 @@ quayApp.directive('applicationReference', function () {
|
|||
template: '/static/directives/application-reference-dialog.html',
|
||||
show: true
|
||||
});
|
||||
}, function() {
|
||||
bootbox.dialog({
|
||||
"message": 'The application could not be found; it might have been deleted.',
|
||||
"title": "Cannot find application",
|
||||
"buttons": {
|
||||
"close": {
|
||||
"label": "Close",
|
||||
"className": "btn-primary"
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}, ApiService.errorDisplay('Application could not be found'));
|
||||
};
|
||||
}
|
||||
};
|
||||
|
@ -2176,6 +2246,8 @@ quayApp.directive('copyBox', function () {
|
|||
'hoveringMessage': '=hoveringMessage'
|
||||
},
|
||||
controller: function($scope, $element, $rootScope) {
|
||||
$scope.disabled = false;
|
||||
|
||||
var number = $rootScope.__copyBoxIdCounter || 0;
|
||||
$rootScope.__copyBoxIdCounter = number + 1;
|
||||
$scope.inputId = "copy-box-input-" + number;
|
||||
|
@ -2185,27 +2257,7 @@ quayApp.directive('copyBox', function () {
|
|||
|
||||
input.attr('id', $scope.inputId);
|
||||
button.attr('data-clipboard-target', $scope.inputId);
|
||||
|
||||
var clip = new ZeroClipboard($(button), { 'moviePath': 'static/lib/ZeroClipboard.swf' });
|
||||
clip.on('complete', function(e) {
|
||||
var message = $(this.parentNode.parentNode.parentNode).find('.clipboard-copied-message')[0];
|
||||
|
||||
// Resets the animation.
|
||||
var elem = message;
|
||||
elem.style.display = 'none';
|
||||
elem.classList.remove('animated');
|
||||
|
||||
// Show the notification.
|
||||
setTimeout(function() {
|
||||
elem.style.display = 'inline-block';
|
||||
elem.classList.add('animated');
|
||||
}, 10);
|
||||
|
||||
// Reset the notification.
|
||||
setTimeout(function() {
|
||||
elem.style.display = 'none';
|
||||
}, 5000);
|
||||
});
|
||||
$scope.disabled = !button.clipboardCopy();
|
||||
}
|
||||
};
|
||||
return directiveDefinitionObject;
|
||||
|
@ -2439,11 +2491,38 @@ quayApp.directive('dockerAuthDialog', function (Config) {
|
|||
'username': '=username',
|
||||
'token': '=token',
|
||||
'shown': '=shown',
|
||||
'counter': '=counter'
|
||||
'counter': '=counter',
|
||||
'supportsRegenerate': '@supportsRegenerate',
|
||||
'regenerate': '®enerate'
|
||||
},
|
||||
controller: function($scope, $element) {
|
||||
controller: function($scope, $element) {
|
||||
var updateCommand = function() {
|
||||
$scope.command = 'docker login -e="." -u="' + $scope.username +
|
||||
'" -p="' + $scope.token + '" ' + Config['SERVER_HOSTNAME'];
|
||||
};
|
||||
|
||||
$scope.$watch('username', updateCommand);
|
||||
$scope.$watch('token', updateCommand);
|
||||
|
||||
$scope.regenerating = true;
|
||||
|
||||
$scope.askRegenerate = function() {
|
||||
bootbox.confirm('Are you sure you want to regenerate the token? All existing login credentials will become invalid', function(resp) {
|
||||
if (resp) {
|
||||
$scope.regenerating = true;
|
||||
$scope.regenerate({'username': $scope.username, 'token': $scope.token});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$scope.isDownloadSupported = function() {
|
||||
try { return !!new Blob(); } catch(e){}
|
||||
var isSafari = /^((?!chrome).)*safari/i.test(navigator.userAgent);
|
||||
if (isSafari) {
|
||||
// Doesn't work properly in Safari, sadly.
|
||||
return false;
|
||||
}
|
||||
|
||||
try { return !!new Blob(); } catch(e) {}
|
||||
return false;
|
||||
};
|
||||
|
||||
|
@ -2461,6 +2540,8 @@ quayApp.directive('dockerAuthDialog', function (Config) {
|
|||
};
|
||||
|
||||
var show = function(r) {
|
||||
$scope.regenerating = false;
|
||||
|
||||
if (!$scope.shown || !$scope.username || !$scope.token) {
|
||||
$('#dockerauthmodal').modal('hide');
|
||||
return;
|
||||
|
@ -2706,6 +2787,8 @@ quayApp.directive('logsView', function () {
|
|||
return 'Delete notification of event "' + eventData['title'] + '" for repository {repo}';
|
||||
},
|
||||
|
||||
'regenerate_robot_token': 'Regenerated token for robot {robot}',
|
||||
|
||||
// Note: These are deprecated.
|
||||
'add_repo_webhook': 'Add webhook in repository {repo}',
|
||||
'delete_repo_webhook': 'Delete webhook in repository {repo}'
|
||||
|
@ -2752,6 +2835,7 @@ quayApp.directive('logsView', function () {
|
|||
'reset_application_client_secret': 'Reset Client Secret',
|
||||
'add_repo_notification': 'Add repository notification',
|
||||
'delete_repo_notification': 'Delete repository notification',
|
||||
'regenerate_robot_token': 'Regenerate Robot Token',
|
||||
|
||||
// Note: these are deprecated.
|
||||
'add_repo_webhook': 'Add webhook',
|
||||
|
@ -2878,18 +2962,7 @@ quayApp.directive('applicationManager', function () {
|
|||
|
||||
ApiService.createOrganizationApplication(data, params).then(function(resp) {
|
||||
$scope.applications.push(resp);
|
||||
}, function(resp) {
|
||||
bootbox.dialog({
|
||||
"message": resp['message'] || 'The application could not be created',
|
||||
"title": "Cannot create application",
|
||||
"buttons": {
|
||||
"close": {
|
||||
"label": "Close",
|
||||
"className": "btn-primary"
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}, ApiService.errorDisplay('Cannot create application'));
|
||||
};
|
||||
|
||||
var update = function() {
|
||||
|
@ -2934,6 +3007,20 @@ quayApp.directive('robotsManager', function () {
|
|||
$scope.shownRobot = null;
|
||||
$scope.showRobotCounter = 0;
|
||||
|
||||
$scope.regenerateToken = function(username) {
|
||||
if (!username) { return; }
|
||||
|
||||
var shortName = $scope.getShortenedName(username);
|
||||
ApiService.regenerateRobotToken($scope.organization, null, {'robot_shortname': shortName}).then(function(updated) {
|
||||
var index = $scope.findRobotIndexByName(username);
|
||||
if (index >= 0) {
|
||||
$scope.robots.splice(index, 1);
|
||||
$scope.robots.push(updated);
|
||||
}
|
||||
$scope.shownRobot = updated;
|
||||
}, ApiService.errorDisplay('Cannot regenerate robot account token'));
|
||||
};
|
||||
|
||||
$scope.showRobot = function(info) {
|
||||
$scope.shownRobot = info;
|
||||
$scope.showRobotCounter++;
|
||||
|
@ -2974,18 +3061,7 @@ quayApp.directive('robotsManager', function () {
|
|||
if (index >= 0) {
|
||||
$scope.robots.splice(index, 1);
|
||||
}
|
||||
}, function() {
|
||||
bootbox.dialog({
|
||||
"message": 'The selected robot account could not be deleted',
|
||||
"title": "Cannot delete robot account",
|
||||
"buttons": {
|
||||
"close": {
|
||||
"label": "Close",
|
||||
"className": "btn-primary"
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}, ApiService.errorDisplay('Cannot delete robot account'));
|
||||
};
|
||||
|
||||
var update = function() {
|
||||
|
@ -3050,18 +3126,7 @@ quayApp.directive('prototypeManager', function () {
|
|||
|
||||
ApiService.updateOrganizationPrototypePermission(data, params).then(function(resp) {
|
||||
prototype.role = role;
|
||||
}, function(resp) {
|
||||
bootbox.dialog({
|
||||
"message": resp.data ? resp.data : 'The permission could not be modified',
|
||||
"title": "Cannot modify permission",
|
||||
"buttons": {
|
||||
"close": {
|
||||
"label": "Close",
|
||||
"className": "btn-primary"
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}, ApiService.errorDisplay('Cannot modify permission'));
|
||||
};
|
||||
|
||||
$scope.comparePrototypes = function(p) {
|
||||
|
@ -3101,23 +3166,16 @@ quayApp.directive('prototypeManager', function () {
|
|||
data['activating_user'] = $scope.activatingForNew;
|
||||
}
|
||||
|
||||
var errorHandler = ApiService.errorDisplay('Cannot create permission',
|
||||
function(resp) {
|
||||
$('#addPermissionDialogModal').modal('hide');
|
||||
});
|
||||
|
||||
ApiService.createOrganizationPrototypePermission(data, params).then(function(resp) {
|
||||
$scope.prototypes.push(resp);
|
||||
$scope.loading = false;
|
||||
$('#addPermissionDialogModal').modal('hide');
|
||||
}, function(resp) {
|
||||
$('#addPermissionDialogModal').modal('hide');
|
||||
bootbox.dialog({
|
||||
"message": resp.data ? resp.data : 'The permission could not be created',
|
||||
"title": "Cannot create permission",
|
||||
"buttons": {
|
||||
"close": {
|
||||
"label": "Close",
|
||||
"className": "btn-primary"
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}, errorHandler);
|
||||
};
|
||||
|
||||
$scope.deletePrototype = function(prototype) {
|
||||
|
@ -3131,18 +3189,7 @@ quayApp.directive('prototypeManager', function () {
|
|||
ApiService.deleteOrganizationPrototypePermission(null, params).then(function(resp) {
|
||||
$scope.prototypes.splice($scope.prototypes.indexOf(prototype), 1);
|
||||
$scope.loading = false;
|
||||
}, function(resp) {
|
||||
bootbox.dialog({
|
||||
"message": resp.data ? resp.data : 'The permission could not be deleted',
|
||||
"title": "Cannot delete permission",
|
||||
"buttons": {
|
||||
"close": {
|
||||
"label": "Close",
|
||||
"className": "btn-primary"
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}, ApiService.errorDisplay('Cannot delete permission'));
|
||||
};
|
||||
|
||||
var update = function() {
|
||||
|
@ -3990,7 +4037,7 @@ quayApp.directive('planManager', function () {
|
|||
return true;
|
||||
};
|
||||
|
||||
$scope.changeSubscription = function(planId) {
|
||||
$scope.changeSubscription = function(planId, opt_async) {
|
||||
if ($scope.planChanging) { return; }
|
||||
|
||||
var callbacks = {
|
||||
|
@ -4004,7 +4051,7 @@ quayApp.directive('planManager', function () {
|
|||
}
|
||||
};
|
||||
|
||||
PlanService.changePlan($scope, $scope.organization, planId, callbacks);
|
||||
PlanService.changePlan($scope, $scope.organization, planId, callbacks, opt_async);
|
||||
};
|
||||
|
||||
$scope.cancelSubscription = function() {
|
||||
|
@ -4067,7 +4114,7 @@ quayApp.directive('planManager', function () {
|
|||
if ($scope.readyForPlan) {
|
||||
var planRequested = $scope.readyForPlan();
|
||||
if (planRequested && planRequested != PlanService.getFreePlan()) {
|
||||
$scope.changeSubscription(planRequested);
|
||||
$scope.changeSubscription(planRequested, /* async */true);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -4485,26 +4532,17 @@ quayApp.directive('setupTriggerDialog', function () {
|
|||
|
||||
$scope.activating = true;
|
||||
|
||||
var errorHandler = ApiService.errorDisplay('Cannot activate build trigger', function(resp) {
|
||||
$scope.hide();
|
||||
$scope.canceled({'trigger': $scope.trigger});
|
||||
});
|
||||
|
||||
ApiService.activateBuildTrigger(data, params).then(function(resp) {
|
||||
$scope.hide();
|
||||
$scope.trigger['is_active'] = true;
|
||||
$scope.trigger['pull_robot'] = resp['pull_robot'];
|
||||
$scope.activated({'trigger': $scope.trigger});
|
||||
}, function(resp) {
|
||||
$scope.hide();
|
||||
$scope.canceled({'trigger': $scope.trigger});
|
||||
|
||||
bootbox.dialog({
|
||||
"message": resp['data']['message'] || 'The build trigger setup could not be completed',
|
||||
"title": "Could not activate build trigger",
|
||||
"buttons": {
|
||||
"close": {
|
||||
"label": "Close",
|
||||
"className": "btn-primary"
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}, errorHandler);
|
||||
};
|
||||
|
||||
var check = function() {
|
||||
|
@ -4844,6 +4882,9 @@ quayApp.directive('buildMessage', function () {
|
|||
case 'waiting':
|
||||
return 'Waiting for available build worker';
|
||||
|
||||
case 'unpacking':
|
||||
return 'Unpacking build package';
|
||||
|
||||
case 'pulling':
|
||||
return 'Pulling base image';
|
||||
|
||||
|
@ -4899,6 +4940,7 @@ quayApp.directive('buildProgress', function () {
|
|||
case 'starting':
|
||||
case 'waiting':
|
||||
case 'cannot_load':
|
||||
case 'unpacking':
|
||||
return 0;
|
||||
break;
|
||||
}
|
||||
|
@ -5118,6 +5160,23 @@ quayApp.directive('twitterView', function () {
|
|||
});
|
||||
|
||||
|
||||
quayApp.directive('notificationsBubble', function () {
|
||||
var directiveDefinitionObject = {
|
||||
priority: 0,
|
||||
templateUrl: '/static/directives/notifications-bubble.html',
|
||||
replace: false,
|
||||
transclude: false,
|
||||
restrict: 'C',
|
||||
scope: {
|
||||
},
|
||||
controller: function($scope, UserService, NotificationService) {
|
||||
$scope.notificationService = NotificationService;
|
||||
}
|
||||
};
|
||||
return directiveDefinitionObject;
|
||||
});
|
||||
|
||||
|
||||
quayApp.directive('notificationView', function () {
|
||||
var directiveDefinitionObject = {
|
||||
priority: 0,
|
||||
|
@ -5705,8 +5764,8 @@ quayApp.run(['$location', '$rootScope', 'Restangular', 'UserService', 'PlanServi
|
|||
}
|
||||
}
|
||||
|
||||
if (!Features.BILLING && response.status == 402) {
|
||||
$('#overlicenseModal').modal({});
|
||||
if (response.status == 503) {
|
||||
$('#cannotContactService').modal({});
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,34 +1,4 @@
|
|||
$.fn.clipboardCopy = function() {
|
||||
var clip = new ZeroClipboard($(this), { 'moviePath': 'static/lib/ZeroClipboard.swf' });
|
||||
|
||||
clip.on('complete', function() {
|
||||
// Resets the animation.
|
||||
var elem = $('#clipboardCopied')[0];
|
||||
if (!elem) {
|
||||
return;
|
||||
}
|
||||
|
||||
elem.style.display = 'none';
|
||||
elem.classList.remove('animated');
|
||||
|
||||
// Show the notification.
|
||||
setTimeout(function() {
|
||||
if (!elem) { return; }
|
||||
elem.style.display = 'inline-block';
|
||||
elem.classList.add('animated');
|
||||
}, 10);
|
||||
});
|
||||
};
|
||||
|
||||
function SignInCtrl($scope, $location) {
|
||||
var redirect = $location.search()['redirect'];
|
||||
if (redirect && redirect.indexOf('/') < 0) {
|
||||
delete $location.search()['redirect'];
|
||||
$scope.redirectUrl = '/' + redirect;
|
||||
return;
|
||||
}
|
||||
|
||||
$scope.redirectUrl = '/';
|
||||
}
|
||||
|
||||
function GuideCtrl() {
|
||||
|
@ -543,23 +513,15 @@ function RepoCtrl($scope, $sanitize, Restangular, ImageMetadataService, ApiServi
|
|||
'image': image.id
|
||||
};
|
||||
|
||||
var errorHandler = ApiService.errorDisplay('Cannot create or move tag', function(resp) {
|
||||
$('#addTagModal').modal('hide');
|
||||
});
|
||||
|
||||
ApiService.changeTagImage(data, params).then(function(resp) {
|
||||
$scope.creatingTag = false;
|
||||
loadViewInfo();
|
||||
$('#addTagModal').modal('hide');
|
||||
}, function(resp) {
|
||||
$('#addTagModal').modal('hide');
|
||||
bootbox.dialog({
|
||||
"message": resp.data ? resp.data : 'Could not create or move tag',
|
||||
"title": "Cannot create or move tag",
|
||||
"buttons": {
|
||||
"close": {
|
||||
"label": "Close",
|
||||
"className": "btn-primary"
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}, errorHandler);
|
||||
};
|
||||
|
||||
$scope.deleteTag = function(tagName) {
|
||||
|
@ -573,18 +535,7 @@ function RepoCtrl($scope, $sanitize, Restangular, ImageMetadataService, ApiServi
|
|||
|
||||
ApiService.deleteFullTag(null, params).then(function() {
|
||||
loadViewInfo();
|
||||
}, function(resp) {
|
||||
bootbox.dialog({
|
||||
"message": resp.data ? resp.data : 'Could not delete tag',
|
||||
"title": "Cannot delete tag",
|
||||
"buttons": {
|
||||
"close": {
|
||||
"label": "Close",
|
||||
"className": "btn-primary"
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}, ApiService.errorDisplay('Cannot delete tag'));
|
||||
};
|
||||
|
||||
$scope.getImagesForTagBySize = function(tag) {
|
||||
|
@ -763,8 +714,6 @@ function RepoCtrl($scope, $sanitize, Restangular, ImageMetadataService, ApiServi
|
|||
|
||||
// Load the builds for this repository. If none are active it will cancel the poll.
|
||||
startBuildInfoTimer(repo);
|
||||
|
||||
$('#copyClipboard').clipboardCopy();
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -1373,17 +1322,16 @@ function RepoAdminCtrl($scope, Restangular, ApiService, KeyService, $routeParams
|
|||
};
|
||||
|
||||
$scope.deleteRole = function(entityName, kind) {
|
||||
var errorHandler = ApiService.errorDisplay('Cannot change permission', function(resp) {
|
||||
if (resp.status == 409) {
|
||||
return 'Cannot change permission as you do not have the authority';
|
||||
}
|
||||
});
|
||||
|
||||
var permissionDelete = Restangular.one(getRestUrl('repository', namespace, name, 'permissions', kind, entityName));
|
||||
permissionDelete.customDELETE().then(function() {
|
||||
delete $scope.permissions[kind][entityName];
|
||||
}, function(resp) {
|
||||
if (resp.status == 409) {
|
||||
$scope.changePermError = resp.data || '';
|
||||
$('#channgechangepermModal').modal({});
|
||||
} else {
|
||||
$('#cannotchangeModal').modal({});
|
||||
}
|
||||
});
|
||||
}, errorHandler);
|
||||
};
|
||||
|
||||
$scope.addRole = function(entityName, role, kind) {
|
||||
|
@ -1394,9 +1342,7 @@ function RepoAdminCtrl($scope, Restangular, ApiService, KeyService, $routeParams
|
|||
var permissionPost = Restangular.one(getRestUrl('repository', namespace, name, 'permissions', kind, entityName));
|
||||
permissionPost.customPUT(permission).then(function(result) {
|
||||
$scope.permissions[kind][entityName] = result;
|
||||
}, function(result) {
|
||||
$('#cannotchangeModal').modal({});
|
||||
});
|
||||
}, ApiService.errorDisplay('Cannot change permission'));
|
||||
};
|
||||
|
||||
$scope.roles = [
|
||||
|
@ -1611,18 +1557,7 @@ function RepoAdminCtrl($scope, Restangular, ApiService, KeyService, $routeParams
|
|||
window.console.log(resp);
|
||||
var url = '/repository/' + namespace + '/' + name + '/build?current=' + resp['id'];
|
||||
document.location = url;
|
||||
}, function(resp) {
|
||||
bootbox.dialog({
|
||||
"message": resp['message'] || 'The build could not be started',
|
||||
"title": "Could not start build",
|
||||
"buttons": {
|
||||
"close": {
|
||||
"label": "Close",
|
||||
"className": "btn-primary"
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}, ApiService.errorDisplay('Could not start build'));
|
||||
};
|
||||
|
||||
$scope.deleteTrigger = function(trigger) {
|
||||
|
@ -1750,18 +1685,7 @@ function UserAdminCtrl($scope, $timeout, $location, ApiService, PlanService, Use
|
|||
|
||||
ApiService.deleteUserAuthorization(null, params).then(function(resp) {
|
||||
$scope.authorizedApps.splice($scope.authorizedApps.indexOf(accessTokenInfo), 1);
|
||||
}, function(resp) {
|
||||
bootbox.dialog({
|
||||
"message": resp.message || 'Could not revoke authorization',
|
||||
"title": "Cannot revoke authorization",
|
||||
"buttons": {
|
||||
"close": {
|
||||
"label": "Close",
|
||||
"className": "btn-primary"
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}, ApiService.errorDisplay('Could not revoke authorization'));
|
||||
};
|
||||
|
||||
$scope.loadLogs = function() {
|
||||
|
@ -1770,7 +1694,6 @@ function UserAdminCtrl($scope, $timeout, $location, ApiService, PlanService, Use
|
|||
};
|
||||
|
||||
$scope.loadInvoices = function() {
|
||||
if (!$scope.hasPaidBusinessPlan) { return; }
|
||||
$scope.invoicesShown++;
|
||||
};
|
||||
|
||||
|
@ -1956,9 +1879,6 @@ function ImageViewCtrl($scope, $routeParams, $rootScope, $timeout, ApiService, I
|
|||
|
||||
// Fetch the image's changes.
|
||||
fetchChanges();
|
||||
|
||||
$('#copyClipboard').clipboardCopy();
|
||||
|
||||
return image;
|
||||
});
|
||||
};
|
||||
|
@ -2226,13 +2146,14 @@ function OrgViewCtrl($rootScope, $scope, ApiService, $routeParams) {
|
|||
'teamname': teamname
|
||||
};
|
||||
|
||||
var errorHandler = ApiService.errorDisplay('Cannot delete team', function() {
|
||||
$scope.currentDeleteTeam = null;
|
||||
});
|
||||
|
||||
ApiService.deleteOrganizationTeam(null, params).then(function() {
|
||||
delete $scope.organization.teams[teamname];
|
||||
$scope.currentDeleteTeam = null;
|
||||
}, function() {
|
||||
$('#cannotchangeModal').modal({});
|
||||
$scope.currentDeleteTeam = null;
|
||||
});
|
||||
}, errorHandler);
|
||||
};
|
||||
|
||||
var loadOrganization = function() {
|
||||
|
@ -2575,9 +2496,9 @@ function NewOrgCtrl($scope, $routeParams, $timeout, $location, UserService, Plan
|
|||
};
|
||||
|
||||
PlanService.changePlan($scope, org.name, $scope.holder.currentPlan.stripeId, callbacks);
|
||||
}, function(result) {
|
||||
}, function(resp) {
|
||||
$scope.creating = false;
|
||||
$scope.createError = result.data.error_description || result.data;
|
||||
$scope.createError = ApiService.getErrorMessage(resp);
|
||||
$timeout(function() {
|
||||
$('#orgName').popover('show');
|
||||
});
|
||||
|
@ -2654,18 +2575,7 @@ function ManageApplicationCtrl($scope, $routeParams, $rootScope, $location, $tim
|
|||
$timeout(function() {
|
||||
$location.path('/organization/' + orgname + '/admin');
|
||||
}, 500);
|
||||
}, function(resp) {
|
||||
bootbox.dialog({
|
||||
"message": resp.message || 'Could not delete application',
|
||||
"title": "Cannot delete application",
|
||||
"buttons": {
|
||||
"close": {
|
||||
"label": "Close",
|
||||
"className": "btn-primary"
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}, ApiService.errorDisplay('Could not delete application'));
|
||||
};
|
||||
|
||||
$scope.updateApplication = function() {
|
||||
|
@ -2683,22 +2593,13 @@ function ManageApplicationCtrl($scope, $routeParams, $rootScope, $location, $tim
|
|||
delete $scope.application['gravatar_email'];
|
||||
}
|
||||
|
||||
var errorHandler = ApiService.errorDisplay('Could not update application', function(resp) {
|
||||
$scope.updating = false;
|
||||
});
|
||||
|
||||
ApiService.updateOrganizationApplication($scope.application, params).then(function(resp) {
|
||||
$scope.application = resp;
|
||||
$scope.updating = false;
|
||||
}, function(resp) {
|
||||
$scope.updating = false;
|
||||
bootbox.dialog({
|
||||
"message": resp.message || 'Could not update application',
|
||||
"title": "Cannot update application",
|
||||
"buttons": {
|
||||
"close": {
|
||||
"label": "Close",
|
||||
"className": "btn-primary"
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}, errorHandler);
|
||||
};
|
||||
|
||||
$scope.resetClientSecret = function() {
|
||||
|
@ -2711,18 +2612,7 @@ function ManageApplicationCtrl($scope, $routeParams, $rootScope, $location, $tim
|
|||
|
||||
ApiService.resetOrganizationApplicationClientSecret(null, params).then(function(resp) {
|
||||
$scope.application = resp;
|
||||
}, function(resp) {
|
||||
bootbox.dialog({
|
||||
"message": resp.message || 'Could not reset client secret',
|
||||
"title": "Cannot reset client secret",
|
||||
"buttons": {
|
||||
"close": {
|
||||
"label": "Close",
|
||||
"className": "btn-primary"
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}, ApiService.errorDisplay('Could not reset client secret'));
|
||||
};
|
||||
|
||||
var loadOrganization = function() {
|
||||
|
@ -2818,18 +2708,7 @@ function SuperUserAdminCtrl($scope, ApiService, Features, UserService) {
|
|||
|
||||
ApiService.changeInstallUser(data, params).then(function(resp) {
|
||||
$scope.loadUsersInternal();
|
||||
}, function(resp) {
|
||||
bootbox.dialog({
|
||||
"message": resp.data ? resp.data.message : 'Could not change user',
|
||||
"title": "Cannot change user",
|
||||
"buttons": {
|
||||
"close": {
|
||||
"label": "Close",
|
||||
"className": "btn-primary"
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}, ApiService.errorDisplay('Could not change user'));
|
||||
};
|
||||
|
||||
$scope.deleteUser = function(user) {
|
||||
|
@ -2841,49 +2720,10 @@ function SuperUserAdminCtrl($scope, ApiService, Features, UserService) {
|
|||
|
||||
ApiService.deleteInstallUser(null, params).then(function(resp) {
|
||||
$scope.loadUsersInternal();
|
||||
}, function(resp) {
|
||||
bootbox.dialog({
|
||||
"message": resp.data ? resp.data.message : 'Could not delete user',
|
||||
"title": "Cannot delete user",
|
||||
"buttons": {
|
||||
"close": {
|
||||
"label": "Close",
|
||||
"className": "btn-primary"
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}, ApiService.errorDisplay('Cannot delete user'));
|
||||
};
|
||||
|
||||
var seatUsageLoaded = function(usage) {
|
||||
$scope.usageLoading = false;
|
||||
|
||||
if (usage.count > usage.allowed) {
|
||||
$scope.limit = 'over';
|
||||
} else if (usage.count == usage.allowed) {
|
||||
$scope.limit = 'at';
|
||||
} else if (usage.count >= usage.allowed * 0.7) {
|
||||
$scope.limit = 'near';
|
||||
} else {
|
||||
$scope.limit = 'none';
|
||||
}
|
||||
|
||||
if (!$scope.chart) {
|
||||
$scope.chart = new UsageChart();
|
||||
$scope.chart.draw('seat-usage-chart');
|
||||
}
|
||||
|
||||
$scope.chart.update(usage.count, usage.allowed);
|
||||
};
|
||||
|
||||
var loadSeatUsage = function() {
|
||||
$scope.usageLoading = true;
|
||||
ApiService.getSeatCount().then(function(resp) {
|
||||
seatUsageLoaded(resp);
|
||||
});
|
||||
};
|
||||
|
||||
loadSeatUsage();
|
||||
$scope.loadUsers();
|
||||
}
|
||||
|
||||
function TourCtrl($scope, $location) {
|
||||
|
@ -2913,4 +2753,4 @@ function ConfirmInviteCtrl($scope, $location, UserService, ApiService, Notificat
|
|||
});
|
||||
|
||||
$scope.redirectUrl = 'confirminvite?code=' + $location.search()['code'];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -148,6 +148,8 @@ ImageHistoryTree.prototype.updateDimensions_ = function() {
|
|||
var ch = dimensions.ch;
|
||||
|
||||
// Set the height of the container so that it never goes offscreen.
|
||||
if (!$('#' + container).removeOverscroll) { return; }
|
||||
|
||||
$('#' + container).removeOverscroll();
|
||||
var viewportHeight = $(window).height();
|
||||
var boundingBox = document.getElementById(container).getBoundingClientRect();
|
||||
|
|
17
static/lib/ZeroClipboard.min.js
vendored
Executable file → Normal file
17
static/lib/ZeroClipboard.min.js
vendored
Executable file → Normal file
File diff suppressed because one or more lines are too long
BIN
static/lib/ZeroClipboard.swf
Executable file → Normal file
BIN
static/lib/ZeroClipboard.swf
Executable file → Normal file
Binary file not shown.
|
@ -53,7 +53,7 @@
|
|||
<label for="orgName">Organization Name</label>
|
||||
<input id="orgName" name="orgName" type="text" class="form-control" placeholder="Organization Name"
|
||||
ng-model="org.name" required autofocus data-trigger="manual" data-content="{{ createError }}"
|
||||
data-placement="right" data-container="body" ng-pattern="/^[a-z0-9_]{4,30}$/">
|
||||
data-placement="bottom" data-container="body" ng-pattern="/^[a-z0-9_]{4,30}$/">
|
||||
<span class="description">This will also be the namespace for your repositories</span>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -34,6 +34,13 @@
|
|||
</span>
|
||||
<i class="fa fa-upload visible-lg"></i>
|
||||
</div>
|
||||
<div class="feature">
|
||||
<span class="context-tooltip" bs-tooltip="tooltip.title" data-container="body" data-placement="right"
|
||||
data-title="Administrators can view and download the full invoice history for their organization">
|
||||
Invoice History
|
||||
</span>
|
||||
<i class="fa fa-calendar visible-lg"></i>
|
||||
</div>
|
||||
<div class="feature">
|
||||
<span class="context-tooltip" bs-tooltip="tooltip.title" data-container="body" data-placement="right"
|
||||
data-title="Grant subsets of users in an organization their own permissions, either on a global basis or a per-repository basis">
|
||||
|
@ -48,13 +55,6 @@
|
|||
</span>
|
||||
<i class="fa fa-bar-chart-o visible-lg"></i>
|
||||
</div>
|
||||
<div class="feature">
|
||||
<span class="context-tooltip" bs-tooltip="tooltip.title" data-container="body" data-placement="right"
|
||||
data-title="Administrators can view and download the full invoice history for their organization">
|
||||
Invoice History
|
||||
</span>
|
||||
<i class="fa fa-calendar visible-lg"></i>
|
||||
</div>
|
||||
<div class="feature">
|
||||
<span class="context-tooltip" bs-tooltip="tooltip.title" data-container="body" data-placement="right"
|
||||
data-title="All plans have a free trial">
|
||||
|
@ -81,7 +81,7 @@
|
|||
<div class="feature present"></div>
|
||||
<div class="feature present"></div>
|
||||
<div class="feature present"></div>
|
||||
<div class="feature" ng-class="plan.bus_features ? 'present' : ''"></div>
|
||||
<div class="feature present"></div>
|
||||
<div class="feature" ng-class="plan.bus_features ? 'present' : ''"></div>
|
||||
<div class="feature" ng-class="plan.bus_features ? 'present' : ''"></div>
|
||||
<div class="feature present"></div>
|
||||
|
@ -93,9 +93,9 @@
|
|||
<div class="feature present">SSL Encryption</div>
|
||||
<div class="feature present">Robot accounts</div>
|
||||
<div class="feature present">Dockerfile Build</div>
|
||||
<div class="feature present">Invoice History</div>
|
||||
<div class="feature" ng-class="plan.bus_features ? 'present' : 'notpresent'">Teams</div>
|
||||
<div class="feature" ng-class="plan.bus_features ? 'present' : 'notpresent'">Logging</div>
|
||||
<div class="feature" ng-class="plan.bus_features ? 'present' : 'notpresent'">Invoice History</div>
|
||||
<div class="feature present">Free Trial</div>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -18,7 +18,8 @@
|
|||
<div class="col-md-2">
|
||||
<ul class="nav nav-pills nav-stacked">
|
||||
<li class="active"><a href="javascript:void(0)" data-toggle="tab" data-target="#permissions">Permissions</a></li>
|
||||
<li><a href="javascript:void(0)" data-toggle="tab" data-target="#trigger" ng-click="loadTriggers()">Build Triggers</a></li>
|
||||
<li><a href="javascript:void(0)" data-toggle="tab" data-target="#trigger" ng-click="loadTriggers()"
|
||||
quay-require="['BUILD_SUPPORT']">Build Triggers</a></li>
|
||||
<li><a href="javascript:void(0)" data-toggle="tab" data-target="#badge">Status Badge</a></li>
|
||||
<li><a href="javascript:void(0)" data-toggle="tab" data-target="#notification" ng-click="loadNotifications()">Notifications</a></li>
|
||||
<li><a href="javascript:void(0)" data-toggle="tab" data-target="#publicprivate">Public/Private</a></li>
|
||||
|
@ -225,7 +226,7 @@
|
|||
</div>
|
||||
|
||||
<!-- Triggers tab -->
|
||||
<div id="trigger" class="tab-pane">
|
||||
<div id="trigger" class="tab-pane" quay-require="['BUILD_SUPPORT']">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">Build Triggers
|
||||
<i class="info-icon fa fa-info-circle" data-placement="left" data-content="Triggers from various services (such as GitHub) which tell the repository to be built and updated."></i>
|
||||
|
@ -377,24 +378,6 @@
|
|||
counter="showNewNotificationCounter"
|
||||
notification-created="handleNotificationCreated(notification)"></div>
|
||||
|
||||
<!-- Modal message dialog -->
|
||||
<div class="modal fade" id="cannotchangeModal">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h4 class="modal-title">Cannot change</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
The selected action could not be performed because you do not have that authority.
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
||||
</div>
|
||||
</div><!-- /.modal-content -->
|
||||
</div><!-- /.modal-dialog -->
|
||||
</div><!-- /.modal -->
|
||||
|
||||
<!-- Modal message dialog -->
|
||||
<div class="modal fade" id="makepublicModal">
|
||||
<div class="modal-dialog">
|
||||
|
@ -441,26 +424,6 @@
|
|||
</div><!-- /.modal -->
|
||||
|
||||
|
||||
<!-- Modal message dialog -->
|
||||
<div class="modal fade" id="channgechangepermModal">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h4 class="modal-title">Cannot change permissions</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<span ng-show="!changePermError">You do not have permission to change the permissions on the repository.</span>
|
||||
<span ng-show="changePermError">{{ changePermError }}</span>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
||||
</div>
|
||||
</div><!-- /.modal-content -->
|
||||
</div><!-- /.modal-dialog -->
|
||||
</div><!-- /.modal -->
|
||||
|
||||
|
||||
<!-- Modal message dialog -->
|
||||
<div class="modal fade" id="confirmdeleteModal">
|
||||
<div class="modal-dialog">
|
||||
|
|
|
@ -8,9 +8,6 @@
|
|||
<div class="col-md-2">
|
||||
<ul class="nav nav-pills nav-stacked">
|
||||
<li class="active">
|
||||
<a href="javascript:void(0)" data-toggle="tab" data-target="#license">License and Usage</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="javascript:void(0)" data-toggle="tab" data-target="#users" ng-click="loadUsers()">Users</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
@ -19,19 +16,8 @@
|
|||
<!-- Content -->
|
||||
<div class="col-md-10">
|
||||
<div class="tab-content">
|
||||
<!-- License tab -->
|
||||
<div id="license" class="tab-pane active">
|
||||
<div class="quay-spinner 3x" ng-show="usageLoading"></div>
|
||||
<!-- Chart -->
|
||||
<div>
|
||||
<div id="seat-usage-chart" class="usage-chart limit-{{limit}}"></div>
|
||||
<span class="usage-caption" ng-show="chart">Seat Usage</span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Users tab -->
|
||||
<div id="users" class="tab-pane">
|
||||
<div id="users" class="tab-pane active">
|
||||
<div class="quay-spinner" ng-show="!users"></div>
|
||||
<div class="alert alert-error" ng-show="usersError">
|
||||
{{ usersError }}
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
<li ng-show="hasPaidPlan" quay-require="['BILLING']">
|
||||
<a href="javascript:void(0)" data-toggle="tab" data-target="#billingoptions">Billing Options</a>
|
||||
</li>
|
||||
<li ng-show="hasPaidBusinessPlan" quay-require="['BILLING']">
|
||||
<li ng-show="hasPaidPlan" quay-require="['BILLING']">
|
||||
<a href="javascript:void(0)" data-toggle="tab" data-target="#billing" ng-click="loadInvoices()">Billing History</a>
|
||||
</li>
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
<div class="dropdown" data-placement="top" style="display: inline-block"
|
||||
bs-tooltip=""
|
||||
data-title="{{ runningBuilds.length ? 'Dockerfile Builds Running: ' + (runningBuilds.length) : 'Dockerfile Build' }}"
|
||||
ng-show="repo.can_write || buildHistory.length">
|
||||
quay-show="Features.BUILD_SUPPORT && (repo.can_write || buildHistory.length)">
|
||||
<button class="btn btn-default dropdown-toggle" data-toggle="dropdown">
|
||||
<i class="fa fa-tasks fa-lg"></i>
|
||||
<span class="count" ng-class="runningBuilds.length ? 'visible' : ''"><span>{{ runningBuilds.length ? runningBuilds.length : '' }}</span></span>
|
||||
|
@ -58,16 +58,9 @@
|
|||
<span class="pull-command visible-md-inline">
|
||||
<div class="pull-container" data-title="Pull repository" bs-tooltip="tooltip.title">
|
||||
<div class="input-group">
|
||||
<input id="pull-text" type="text" class="form-control" value="{{ 'docker pull ' + Config.getDomain() + '/' + repo.namespace + '/' + repo.name }}" readonly>
|
||||
<span id="copyClipboard" class="input-group-addon" data-title="Copy to Clipboard" data-clipboard-target="pull-text">
|
||||
<i class="fa fa-copy"></i>
|
||||
</span>
|
||||
<div class="copy-box" hovering-message="true" value="'docker pull ' + Config.getDomain() + '/' + repo.namespace + '/' + repo.name"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="clipboardCopied" class="hovering" style="display: none">
|
||||
Copied to clipboard
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
|
Reference in a new issue