Merge remote-tracking branch 'origin/master' into ncc1701

Conflicts:
	endpoints/web.py
	static/directives/signup-form.html
	static/js/app.js
	static/js/controllers.js
	static/partials/landing.html
	static/partials/view-repo.html
	test/data/test.db
This commit is contained in:
jakedt 2014-04-14 19:37:22 -04:00
commit 0827e0fbac
45 changed files with 1149 additions and 306 deletions

View file

@ -2406,12 +2406,19 @@ p.editable:hover i {
text-align: center;
}
#image-history-container .tags .tag, #confirmdeleteTagModal .tag {
.tags .tag, #confirmdeleteTagModal .tag {
border-radius: 10px;
margin-right: 4px;
cursor: pointer;
}
.tooltip-tags {
display: block;
margin-top: 10px;
border-top: 1px dotted #aaa;
padding-top: 10px;
}
#changes-tree-container {
overflow: hidden;
}
@ -3451,7 +3458,7 @@ pre.command:before {
position: relative;
height: 100px;
height: 75px;
opacity: 1;
}
@ -3602,10 +3609,10 @@ pre.command:before {
margin-right: 34px;
}
.trigger-option-section:not(:last-child) {
border-bottom: 1px solid #eee;
padding-bottom: 16px;
margin-bottom: 16px;
.trigger-option-section:not(:first-child) {
border-top: 1px solid #eee;
padding-top: 16px;
margin-top: 10px;
}
.trigger-option-section .entity-search-element .twitter-typeahead {

View file

@ -30,7 +30,7 @@
</td>
<td>
<a ng-show="invoice.paid" href="/receipt?id={{ invoice.id }}" download="receipt.pdf" target="_new">
<i class="fa fa-download" title="Download Receipt" bs-tooltip="tooltip.title"></i>
<i class="fa fa-download" data-title="Download Receipt" bs-tooltip="tooltip.title"></i>
</a>
</td>
</tr>

View file

@ -2,7 +2,7 @@
<div class="id-container">
<div class="input-group">
<input type="text" class="form-control" value="{{ value }}" readonly>
<span class="input-group-addon" title="Copy to Clipboard">
<span class="input-group-addon" data-title="Copy to Clipboard">
<i class="fa fa-copy"></i>
</span>
</div>

View file

@ -1,4 +1,4 @@
<span class="delete-ui-element" ng-click="focus()">
<span class="delete-ui-button" ng-click="performDelete()"><button class="btn btn-danger">{{ buttonTitleInternal }}</button></span>
<i class="fa fa-times" bs-tooltip="tooltip.title" data-placement="left" title="{{ deleteTitle }}"></i>
<i class="fa fa-times" bs-tooltip="tooltip.title" data-placement="left" data-title="{{ deleteTitle }}"></i>
</span>

View file

@ -1,14 +1,14 @@
<span class="entity-reference-element">
<span ng-if="entity.kind == 'team'">
<i class="fa fa-group" title="Team" bs-tooltip="tooltip.title" data-container="body"></i>
<i class="fa fa-group" data-title="Team" bs-tooltip="tooltip.title" data-container="body"></i>
<span class="entity-name">
<span ng-if="!getIsAdmin(namespace)">{{entity.name}}</span>
<span ng-if="getIsAdmin(namespace)"><a href="/organization/{{ namespace }}/teams/{{ entity.name }}">{{entity.name}}</a></span>
</span>
</span>
<span ng-if="entity.kind != 'team'">
<i class="fa fa-user" ng-show="!entity.is_robot" title="User" bs-tooltip="tooltip.title" data-container="body"></i>
<i class="fa fa-wrench" ng-show="entity.is_robot" title="Robot Account" bs-tooltip="tooltip.title" data-container="body"></i>
<i class="fa fa-user" ng-show="!entity.is_robot" data-title="User" bs-tooltip="tooltip.title" data-container="body"></i>
<i class="fa fa-wrench" ng-show="entity.is_robot" data-title="Robot Account" bs-tooltip="tooltip.title" data-container="body"></i>
<span class="entity-name" ng-if="entity.is_robot">
<a href="{{ getRobotUrl(entity.name) }}" ng-if="getIsAdmin(getPrefix(entity.name))">
<span class="prefix">{{ getPrefix(entity.name) }}+</span><span>{{ getShortenedName(entity.name) }}</span>
@ -22,6 +22,6 @@
</span>
</span>
<i class="fa fa-exclamation-triangle" ng-if="entity.is_org_member === false"
title="This user is not a member of the organization" bs-tooltip="tooltip.title" data-container="body">
data-title="This user is not a member of the organization" bs-tooltip="tooltip.title" data-container="body">
</i>
</span>

View file

@ -1,5 +1,5 @@
<span class="entity-search-element" ng-class="isPersistent ? 'persistent' : ''"><input class="entity-search-control form-control">
<span class="entity-reference block-reference" ng-show="isPersistent && currentEntity" entity="currentEntity"></span>
<span class="entity-reference block-reference" ng-show="isPersistent && currentEntityInternal" entity="currentEntityInternal"></span>
<div class="dropdown">
<button class="btn btn-default dropdown-toggle" type="button" id="entityDropdownMenu" data-toggle="dropdown"
ng-click="lazyLoad()">

View file

@ -28,7 +28,7 @@
</form>
<span class="navbar-left user-tools" ng-show="!user.anonymous">
<a href="/new/"><i class="fa fa-upload user-tool" bs-tooltip="tooltip.title" data-placement="bottom" title="Create new repository"></i></a>
<a href="/new/"><i class="fa fa-upload user-tool" bs-tooltip="tooltip.title" data-placement="bottom" data-title="Create new repository"></i></a>
</span>
</li>
@ -40,7 +40,7 @@
ng-show="notificationService.notifications.length"
ng-class="notificationService.notificationClasses"
bs-tooltip=""
title="User Notifications"
data-title="User Notifications"
data-placement="left"
data-container="body">
{{ notificationService.notifications.length }}

View file

@ -13,9 +13,9 @@
</span>
<span class="right">
<i class="fa fa-bar-chart-o toggle-icon" ng-class="chartVisible ? 'active' : ''"
ng-click="toggleChart()" title="Toggle Chart" bs-tooltip="tooltip.title"></i>
ng-click="toggleChart()" data-title="Toggle Chart" bs-tooltip="tooltip.title"></i>
<a href="{{ logsPath }}" download="usage-log.json" target="_new">
<i class="fa fa-download toggle-icon" title="Download Logs" bs-tooltip="tooltip.title"></i>
<i class="fa fa-download toggle-icon" data-title="Download Logs" bs-tooltip="tooltip.title"></i>
</a>
</span>
</div>
@ -55,7 +55,7 @@
<td>
<span class="log-performer" ng-if="log.metadata.oauth_token_application">
<div>
<span class="application-reference" title="log.metadata.oauth_token_application"
<span class="application-reference" data-title="log.metadata.oauth_token_application"
client-id="log.metadata.oauth_token_application_id"></span>
</div>
<div style="text-align: center; font-size: 12px; color: #aaa; padding: 4px;">on behalf of</div>

View file

@ -18,7 +18,7 @@
</a>
<i class="fa fa-exclamation-triangle" ng-show="requireCreate && !namespaces[org.name].can_create_repo"
title="You do not have permission to create repositories for this organization"
data-title="You do not have permission to create repositories for this organization"
data-placement="right"
bs-tooltip="tooltip.title"></i>
</li>

View file

@ -38,7 +38,7 @@
<td>
{{ plan.title }}
<div class="deprecated-plan-label" ng-show="plan.deprecated">
<span class="context-tooltip" title="This plan has been discontinued. As a valued early adopter, you may continue to stay on this plan indefinitely." bs-tooltip="tooltip.title" data-placement="right">Discontinued Plan</span>
<span class="context-tooltip" data-title="This plan has been discontinued. As a valued early adopter, you may continue to stay on this plan indefinitely." bs-tooltip="tooltip.title" data-placement="right">Discontinued Plan</span>
</div>
</td>
<td>{{ plan.privateRepos }}</td>

View file

@ -3,7 +3,7 @@
<div class="container" ng-show="!loading">
<div class="alert alert-info">
Default permissions provide a means of specifying <span class="context-tooltip" title="By default, all repositories have the creating user added as an 'Admin'" bs-tooltip="tooltip.title">additional</span> permissions that should be granted automatically to a repository.
Default permissions provide a means of specifying <span class="context-tooltip" data-title="By default, all repositories have the creating user added as an 'Admin'" bs-tooltip="tooltip.title">additional</span> permissions that should be granted automatically to a repository.
</div>
<div class="side-controls">
@ -17,13 +17,13 @@
<thead>
<th>
<span class="context-tooltip"
title="The user or robot that is creating a repository. If '(Organization Default)', then any repository created in this organization will be granted the permission."
data-title="The user or robot that is creating a repository. If '(Organization Default)', then any repository created in this organization will be granted the permission."
bs-tooltip="tooltip.title" data-container="body">
Repository Creator
</span>
</th>
<th>
<span class="context-tooltip" title="The user, robot or team that is being granted the permission"
<span class="context-tooltip" data-title="The user, robot or team that is being granted the permission"
bs-tooltip="tooltip.title" data-container="body">
Applies To User/Robot/Team
</span>

View file

@ -1,2 +1,2 @@
<i class="fa fa-lock fa-lg" style="{{ repo.is_public ? 'visibility: hidden' : 'visibility: inherit' }}" title="Private Repository"></i>
<i class="fa fa-lock fa-lg" style="{{ repo.is_public ? 'visibility: hidden' : 'visibility: inherit' }}" data-title="Private Repository"></i>
<i class="fa fa-hdd-o"></i>

View file

@ -0,0 +1,100 @@
<div class="setup-trigger-directive-element">
<!-- Modal message dialog -->
<div class="modal fade" id="setupTriggerModal">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title">Setup new build trigger</h4>
</div>
<div class="modal-body">
<!-- Trigger-specific setup -->
<div class="trigger-description-element trigger-option-section" ng-switch on="trigger.service">
<div ng-switch-when="github">
<div class="trigger-setup-github" repository="repository" trigger="trigger"
analyze="checkAnalyze(isValid)"></div>
</div>
</div>
<!-- Pull information -->
<div class="trigger-option-section" ng-show="showPullRequirements">
<div ng-show="!pullRequirements">
<span class="quay-spinner"></span> Checking pull credential requirements...
</div>
<div ng-show="pullRequirements">
<div class="alert alert-danger" ng-if="pullRequirements.status == 'error'">
{{ pullRequirements.message }}
</div>
<div class="alert alert-warning" ng-if="pullRequirements.status == 'warning'">
{{ pullRequirements.message }}
</div>
<div class="alert alert-success" ng-if="pullRequirements.status == 'analyzed' && pullRequirements.is_public === false">
The
<a href="{{ pullRequirements.dockerfile_url }}" ng-if="pullRequirements.dockerfile_url" target="_blank">Dockerfile found</a>
<span ng-if="!pullRequirements.dockerfile_url">Dockerfile found</span>
depends on repository
<a href="/repository/{{ pullRequirements.namespace }}/{{ pullRequirements.name }}" target="_blank">
{{ pullRequirements.namespace }}/{{ pullRequirements.name }}
</a> which requires
a robot account for pull access, because it is marked <strong>private</strong>.
</div>
</div>
<table style="width: 100%;" ng-show="pullRequirements">
<tr>
<td style="width: 114px">
<div class="context-tooltip" data-title="The credentials used by the builder when pulling images" bs-tooltip>
Pull Credentials:
</div>
</td>
<td>
<div ng-if="!isNamespaceAdmin(repository.namespace)" style="color: #aaa;">
In order to set pull credentials for a build trigger, you must be an Administrator of the namespace <strong>{{ repository.namespace }}</strong>
</div>
<div class="btn-group btn-group-sm" ng-if="isNamespaceAdmin(repository.namespace)">
<button type="button" class="btn btn-default"
ng-class="publicPull ? 'active btn-info' : ''" ng-click="setPublicPull(true)">Public</button>
<button type="button" class="btn btn-default"
ng-class="publicPull ? '' : 'active btn-info'" ng-click="setPublicPull(false)">
<i class="fa fa-wrench"></i>
Robot account
</button>
</div>
</td>
</tr>
<tr ng-show="!publicPull">
<td>
</td>
<td>
<div class="entity-search" namespace="repository.namespace" include-teams="false"
input-title="'Select robot account for pulling...'"
is-organization="repository.is_organization"
is-persistent="true"
current-entity="pullEntity"
filter="['robot']"></div>
<div class="alert alert-info" ng-if="pullRequirements.robots.length" style="margin-top: 20px; margin-bottom: 0px;">
Note: We've automatically selected robot account <span class="entity-reference" entity="pullRequirements.robots[0]"></span>, since it has access to the repository.
</div>
<div class="alert alert-warning" ng-if="!pullRequirements.robots.length" style="margin-top: 20px; margin-bottom: 0px;">
Note: No robot account currently has access to the repository. Please create one and/or assign access in the
<a href="/repository/{{ pullRequirements.namespace }}/{{ pullRequirements.name }}/admin" target="_blank">repository's admin panel</a>.
</div>
</td>
</tr>
</table>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary"
ng-disabled="!trigger.$ready || (!publicPull && !pullEntity) || checkingPullRequirements"
ng-click="activate">Finished</button>
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
</div>

View file

@ -1,13 +1,15 @@
<div class="signup-form-element">
<form class="form-signup" name="signupForm" ng-submit="register()" data-trigger="manual"
data-content="{{ registerError }}" data-placement="left" ng-show="!awaitingConfirmation && !registering">
<input type="text" class="form-control" placeholder="Create a username" name="username" ng-model="newUser.username" autofocus required>
<form class="form-signup" name="signupForm" ng-submit="register()" ngshow="!awaitingConfirmation && !registering">
<input type="text" class="form-control" placeholder="Create a username" name="username" ng-model="newUser.username" autofocus required ng-pattern="/^[a-z0-9_]{4,30}$/">
<input type="email" class="form-control" placeholder="Email address" ng-model="newUser.email" required>
<input type="password" class="form-control" placeholder="Create a password" ng-model="newUser.password" required>
<input type="password" class="form-control" placeholder="Create a password" ng-model="newUser.password" required
ng-pattern="/^.{8,}$/">
<input type="password" class="form-control" placeholder="Verify your password" ng-model="newUser.repeatPassword"
match="newUser.password" required>
match="newUser.password" required
ng-pattern="/^.{8,}$/">
<div class="form-group signin-buttons">
<button class="btn btn-primary btn-block landing-signup-button" ng-disabled="signupForm.$invalid" type="submit"
<button id="signupButton"
class="btn btn-primary btn-block landing-signup-button" ng-disabled="signupForm.$invalid" type="submit"
analytics-on analytics-event="register">
<span quay-show="Features.BILLING">Sign Up for Free!</span>
<span quay-show="!Features.BILLING">Sign Up</span>

View file

@ -1,6 +1,6 @@
<span class="trigger-description-element" ng-switch on="trigger.service">
<span ng-switch-when="github">
<i class="fa fa-github fa-lg" style="margin-right: 6px" title="GitHub" bs-tooltip="tooltip.title"></i>
<i class="fa fa-github fa-lg" style="margin-right: 6px" data-title="GitHub" bs-tooltip="tooltip.title"></i>
Push to GitHub repository <a href="https://github.com/{{ trigger.config.build_source }}" target="_new">{{ trigger.config.build_source }}</a>
<div style="margin-top: 4px; margin-left: 26px; font-size: 12px; color: gray;" ng-if="trigger.config.subdir">
<span>Dockerfile:

View file

@ -335,6 +335,42 @@ quayApp = angular.module('quay', quayDependencies, function($provide, cfpLoading
}]);
$provide.factory('UIService', [function() {
var uiService = {};
uiService.hidePopover = function(elem) {
var popover = $('#signupButton').data('bs.popover');
if (popover) {
popover.hide();
}
};
uiService.showPopover = function(elem, content) {
var popover = $(elem).data('bs.popover');
if (!popover) {
$(elem).popover({'content': '-', 'placement': 'left'});
}
setTimeout(function() {
var popover = $(elem).data('bs.popover');
popover.options.content = content;
popover.show();
}, 500);
};
uiService.showFormError = function(elem, result) {
var message = result.data['message'] || result.data['error_description'] || '';
if (message) {
uiService.showPopover(elem, message);
} else {
uiService.hidePopover(elem);
}
};
return uiService;
}]);
$provide.factory('UtilService', ['$sanitize', function($sanitize) {
var utilService = {};
@ -1833,7 +1869,7 @@ quayApp.directive('signupForm', function () {
scope: {
},
controller: function($scope, $location, $timeout, ApiService, KeyService, UserService, Config) {
controller: function($scope, $location, $timeout, ApiService, KeyService, UserService, Config, UIService) {
$('.form-signup').popover();
if (Config.MIXPANEL_KEY) {
@ -1847,24 +1883,21 @@ quayApp.directive('signupForm', function () {
$scope.awaitingConfirmation = false;
$scope.registering = false;
$scope.register = function() {
$('.form-signup').popover('hide');
UIService.hidePopover('#signupButton');
$scope.registering = true;
ApiService.createNewUser($scope.newUser).then(function() {
$scope.awaitingConfirmation = true;
$scope.registering = false;
$scope.awaitingConfirmation = true;
if (Config.MIXPANEL_KEY) {
mixpanel.alias($scope.newUser.username);
}
}, function(result) {
$scope.registering = false;
$scope.registerError = result.data.message;
$timeout(function() {
$('.form-signup').popover('show');
});
UIService.showFormError('#signupButton', result);
});
};
}
@ -2904,6 +2937,7 @@ quayApp.directive('entitySearch', function () {
controller: function($scope, $element, Restangular, UserService, ApiService) {
$scope.lazyLoading = true;
$scope.isAdmin = false;
$scope.currentEntityInternal = $scope.currentEntity;
$scope.lazyLoad = function() {
if (!$scope.namespace || !$scope.lazyLoading) { return; }
@ -2986,7 +3020,9 @@ quayApp.directive('entitySearch', function () {
};
$scope.clearEntityInternal = function() {
$scope.currentEntityInternal = null;
$scope.currentEntity = null;
if ($scope.entitySelected) {
$scope.entitySelected(null);
}
@ -3000,6 +3036,7 @@ quayApp.directive('entitySearch', function () {
}
if ($scope.isPersistent) {
$scope.currentEntityInternal = entity;
$scope.currentEntity = entity;
}
@ -3124,6 +3161,16 @@ quayApp.directive('entitySearch', function () {
$scope.$watch('inputTitle', function(title) {
input.setAttribute('placeholder', title);
});
$scope.$watch('currentEntity', function(entity) {
if ($scope.currentEntityInternal != entity) {
if (entity) {
$scope.setEntityInternal(entity, false);
} else {
$scope.clearEntityInternal();
}
}
});
}
};
return directiveDefinitionObject;
@ -3661,6 +3708,145 @@ quayApp.directive('dropdownSelectMenu', function () {
});
quayApp.directive('setupTriggerDialog', function () {
var directiveDefinitionObject = {
templateUrl: '/static/directives/setup-trigger-dialog.html',
replace: false,
transclude: false,
restrict: 'C',
scope: {
'repository': '=repository',
'trigger': '=trigger',
'counter': '=counter',
'canceled': '&canceled',
'activated': '&activated'
},
controller: function($scope, $element, ApiService, UserService) {
$scope.show = function() {
$scope.pullEntity = null;
$scope.publicPull = true;
$scope.showPullRequirements = false;
$('#setupTriggerModal').modal({});
$('#setupTriggerModal').on('hidden.bs.modal', function () {
$scope.$apply(function() {
$scope.cancelSetupTrigger();
});
});
};
$scope.isNamespaceAdmin = function(namespace) {
return UserService.isNamespaceAdmin(namespace);
};
$scope.cancelSetupTrigger = function() {
$scope.canceled({'trigger': $scope.trigger});
};
$scope.hide = function() {
$('#setupTriggerModal').modal('hide');
};
$scope.setPublicPull = function(value) {
$scope.publicPull = value;
};
$scope.checkAnalyze = function(isValid) {
if (!isValid) {
$scope.publicPull = true;
$scope.pullEntity = null;
$scope.showPullRequirements = false;
$scope.checkingPullRequirements = false;
return;
}
$scope.checkingPullRequirements = true;
$scope.showPullRequirements = true;
$scope.pullRequirements = null;
var params = {
'repository': $scope.repository.namespace + '/' + $scope.repository.name,
'trigger_uuid': $scope.trigger.id
};
var data = {
'config': $scope.trigger.config
};
ApiService.analyzeBuildTrigger(data, params).then(function(resp) {
$scope.pullRequirements = resp;
if (resp['status'] == 'publicbase') {
$scope.publicPull = true;
$scope.pullEntity = null;
} else if (resp['namespace']) {
$scope.publicPull = false;
if (resp['robots'] && resp['robots'].length > 0) {
$scope.pullEntity = resp['robots'][0];
} else {
$scope.pullEntity = null;
}
}
$scope.checkingPullRequirements = false;
}, function(resp) {
$scope.pullRequirements = resp;
$scope.checkingPullRequirements = false;
});
};
$scope.activate = function() {
var params = {
'repository': $scope.repository.namespace + '/' + $scope.repository.name,
'trigger_uuid': $scope.trigger.id
};
var data = {
'config': $scope.trigger['config']
};
if ($scope.pullEntity) {
data['pull_robot'] = $scope.pullEntity['name'];
}
ApiService.activateBuildTrigger(data, params).then(function(resp) {
trigger['is_active'] = true;
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"
}
}
});
});
};
var check = function() {
if ($scope.counter && $scope.trigger && $scope.repository) {
$scope.show();
}
};
$scope.$watch('trigger', check);
$scope.$watch('counter', check);
$scope.$watch('repository', check);
}
};
return directiveDefinitionObject;
});
quayApp.directive('triggerSetupGithub', function () {
var directiveDefinitionObject = {
priority: 0,
@ -3670,15 +3856,18 @@ quayApp.directive('triggerSetupGithub', function () {
restrict: 'C',
scope: {
'repository': '=repository',
'trigger': '=trigger'
'trigger': '=trigger',
'analyze': '&analyze'
},
controller: function($scope, $element, ApiService) {
$scope.analyzeCounter = 0;
$scope.setupReady = false;
$scope.loading = true;
$scope.handleLocationInput = function(location) {
$scope.trigger['config']['subdir'] = location || '';
$scope.isInvalidLocation = $scope.locations.indexOf(location) < 0;
$scope.analyze({'isValid': !$scope.isInvalidLocation});
};
$scope.handleLocationSelected = function(datum) {
@ -3689,6 +3878,7 @@ quayApp.directive('triggerSetupGithub', function () {
$scope.currentLocation = location;
$scope.trigger['config']['subdir'] = location || '';
$scope.isInvalidLocation = false;
$scope.analyze({'isValid': true});
};
$scope.selectRepo = function(repo, org) {
@ -3727,6 +3917,7 @@ quayApp.directive('triggerSetupGithub', function () {
$scope.locations = null;
$scope.trigger.$ready = false;
$scope.isInvalidLocation = false;
$scope.analyze({'isValid': false});
return;
}
@ -3739,12 +3930,14 @@ quayApp.directive('triggerSetupGithub', function () {
} else {
$scope.currentLocation = null;
$scope.isInvalidLocation = resp['subdir'].indexOf('') < 0;
$scope.analyze({'isValid': !$scope.isInvalidLocation});
}
}, function(resp) {
$scope.locationError = resp['message'] || 'Could not load Dockerfile locations';
$scope.locations = null;
$scope.trigger.$ready = false;
$scope.isInvalidLocation = false;
$scope.analyze({'isValid': false});
});
}
};
@ -3789,7 +3982,14 @@ quayApp.directive('triggerSetupGithub', function () {
});
};
loadSources();
var check = function() {
if ($scope.repository && $scope.trigger) {
loadSources();
}
};
$scope.$watch('repository', check);
$scope.$watch('trigger', check);
$scope.$watch('currentRepo', function(repo) {
$scope.selectRepoInternal(repo);
@ -4409,6 +4609,17 @@ quayApp.run(['$location', '$rootScope', 'Restangular', 'UserService', 'PlanServi
});
};
$rootScope.$watch('description', function(description) {
if (!description) {
description = 'Hosted private docker repositories. Includes full user management and history. Free for public repositories.';
}
// Note: We set the content of the description tag manually here rather than using Angular binding
// because we need the <meta> tag to have a default description that is not of the form "{{ description }}",
// we read by tools that do not properly invoke the Angular code.
$('#descriptionTag').attr('content', description);
});
$rootScope.$on('$routeUpdate', function(){
if ($location.search()['tab']) {
changeTab($location.search()['tab']);
@ -4425,7 +4636,7 @@ quayApp.run(['$location', '$rootScope', 'Restangular', 'UserService', 'PlanServi
if (current.$$route.description) {
$rootScope.description = current.$$route.description;
} else {
$rootScope.description = 'Hosted private docker repositories. Includes full user management and history. Free for public repositories.';
$rootScope.description = '';
}
$rootScope.fixFooter = !!current.$$route.fixFooter;

View file

@ -1178,6 +1178,8 @@ function RepoAdminCtrl($scope, Restangular, ApiService, KeyService, $routeParams
$scope.githubRedirectUri = KeyService.githubRedirectUri;
$scope.githubClientId = KeyService.githubClientId;
$scope.showTriggerSetupCounter = 0;
$scope.getBadgeFormat = function(format, repo) {
if (!repo) { return; }
@ -1467,65 +1469,15 @@ function RepoAdminCtrl($scope, Restangular, ApiService, KeyService, $routeParams
};
$scope.setupTrigger = function(trigger) {
$scope.triggerSetupReady = false;
$scope.currentSetupTrigger = trigger;
trigger['_pullEntity'] = null;
trigger['_publicPull'] = true;
$('#setupTriggerModal').modal({});
$('#setupTriggerModal').on('hidden.bs.modal', function () {
$scope.$apply(function() {
$scope.cancelSetupTrigger();
});
});
$scope.showTriggerSetupCounter++;
};
$scope.isNamespaceAdmin = function(namespace) {
return UserService.isNamespaceAdmin(namespace);
};
$scope.cancelSetupTrigger = function(trigger) {
if ($scope.currentSetupTrigger != trigger) { return; }
$scope.finishSetupTrigger = function(trigger) {
$('#setupTriggerModal').modal('hide');
$scope.currentSetupTrigger = null;
var params = {
'repository': namespace + '/' + name,
'trigger_uuid': trigger.id
};
var data = {
'config': trigger['config']
};
if (trigger['_pullEntity']) {
data['pull_robot'] = trigger['_pullEntity']['name'];
}
ApiService.activateBuildTrigger(data, params).then(function(resp) {
trigger['is_active'] = true;
trigger['pull_robot'] = resp['pull_robot'];
}, function(resp) {
$scope.triggers.splice($scope.triggers.indexOf(trigger), 1);
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"
}
}
});
});
};
$scope.cancelSetupTrigger = function() {
if (!$scope.currentSetupTrigger) { return; }
$('#setupTriggerModal').modal('hide');
$scope.deleteTrigger($scope.currentSetupTrigger);
$scope.currentSetupTrigger = null;
$scope.deleteTrigger(trigger);
};
$scope.startTrigger = function(trigger) {
@ -1620,7 +1572,7 @@ function RepoAdminCtrl($scope, Restangular, ApiService, KeyService, $routeParams
}
function UserAdminCtrl($scope, $timeout, $location, ApiService, PlanService, UserService, CookieService, KeyService,
$routeParams, $http, Features) {
$routeParams, $http, UIService, Features) {
$scope.Features = Features;
if ($routeParams['migrate']) {
@ -1657,8 +1609,6 @@ function UserAdminCtrl($scope, $timeout, $location, ApiService, PlanService, Use
$scope.githubClientId = KeyService.githubClientId;
$scope.authorizedApps = null;
$('.form-change').popover();
$scope.logsShown = 0;
$scope.invoicesShown = 0;
@ -1748,7 +1698,8 @@ function UserAdminCtrl($scope, $timeout, $location, ApiService, PlanService, Use
};
$scope.changeEmail = function() {
$('#changeEmailForm').popover('hide');
UIService.hidePopover('#changeEmailForm');
$scope.updatingUser = true;
$scope.changeEmailSent = false;
@ -1763,16 +1714,13 @@ function UserAdminCtrl($scope, $timeout, $location, ApiService, PlanService, Use
$scope.changeEmailForm.$setPristine();
}, function(result) {
$scope.updatingUser = false;
$scope.changeEmailError = result.data.message;
$timeout(function() {
$('#changeEmailForm').popover('show');
});
UIService.showFormError('#changeEmailForm', result);
});
};
$scope.changePassword = function() {
$('#changePasswordForm').popover('hide');
UIService.hidePopover('#changePasswordForm');
$scope.updatingUser = true;
$scope.changePasswordSuccess = false;
@ -1790,11 +1738,7 @@ function UserAdminCtrl($scope, $timeout, $location, ApiService, PlanService, Use
UserService.load();
}, function(result) {
$scope.updatingUser = false;
$scope.changePasswordError = result.data.message;
$timeout(function() {
$('#changePasswordForm').popover('show');
});
UIService.showFormError('#changePasswordForm', result);
});
};
}
@ -2185,7 +2129,7 @@ function OrgViewCtrl($rootScope, $scope, ApiService, $routeParams) {
loadOrganization();
}
function OrgAdminCtrl($rootScope, $scope, $timeout, Restangular, $routeParams, UserService, PlanService, ApiService, Features) {
function OrgAdminCtrl($rootScope, $scope, $timeout, Restangular, $routeParams, UserService, PlanService, ApiService, Features, UIService) {
var orgname = $routeParams.orgname;
// Load the list of plans.
@ -2226,10 +2170,12 @@ function OrgAdminCtrl($rootScope, $scope, $timeout, Restangular, $routeParams, U
};
$scope.$watch('organizationEmail', function(e) {
$('#changeEmailForm').popover('hide');
UIService.hidePopover('#changeEmailForm');
});
$scope.changeEmail = function() {
UIService.hidePopover('#changeEmailForm');
$scope.changingOrganization = true;
var params = {
'orgname': orgname
@ -2245,10 +2191,7 @@ function OrgAdminCtrl($rootScope, $scope, $timeout, Restangular, $routeParams, U
$scope.organization = org;
}, function(result) {
$scope.changingOrganization = false;
$scope.changeEmailError = result.data.message;
$timeout(function() {
$('#changeEmailForm').popover('show');
});
UIService.showFormError('#changeEmailForm', result);
});
};

View file

@ -222,7 +222,17 @@ ImageHistoryTree.prototype.draw = function(container) {
if (d.image.command && d.image.command.length) {
html += '<span class="command info-line"><i class="fa fa-terminal"></i>' + formatCommand(d.image) + '</span>';
}
html += '<span class="created info-line"><i class="fa fa-calendar"></i>' + formatTime(d.image.created) + '</span>';
html += '<span class="created info-line"><i class="fa fa-calendar"></i>' + formatTime(d.image.created) + '</span>';
var tags = d.tags || [];
html += '<span class="tooltip-tags tags">';
for (var i = 0; i < tags.length; ++i) {
var tag = tags[i];
var kind = 'default';
html += '<span class="label label-' + kind + ' tag" data-tag="' + tag + '">' + tag + '</span>';
}
html += '</span>';
return html;
})
@ -330,6 +340,23 @@ ImageHistoryTree.prototype.changeImage_ = function(imageId) {
};
/**
* Expands the given collapsed node in the tree.
*/
ImageHistoryTree.prototype.expandCollapsed_ = function(imageNode) {
var index = imageNode.parent.children.indexOf(imageNode);
if (index < 0 || imageNode.encountered.length < 2) {
return;
}
// Note: we start at 1 since the 0th encountered node is the parent.
imageNode.parent.children.splice(index, 1, imageNode.encountered[1]);
this.maxHeight_ = this.determineMaximumHeight_(this.root_);
this.update_(this.root_);
this.updateDimensions_();
};
/**
* Builds the root node for the tree.
*/
@ -632,7 +659,10 @@ ImageHistoryTree.prototype.update_ = function(source) {
.attr("dy", ".35em")
.attr("text-anchor", function(d) { return d.children || d._children ? "end" : "start"; })
.text(function(d) { return d.name; })
.on("click", function(d) { if (d.image) { that.changeImage_(d.image.id); } })
.on("click", function(d) {
if (d.image) { that.changeImage_(d.image.id); }
if (d.collapsed) { that.expandCollapsed_(d); }
})
.on('mouseover', tip.show)
.on('mouseout', tip.hide);
@ -685,9 +715,9 @@ ImageHistoryTree.prototype.update_ = function(source) {
if (d.virtual) {
return 'virtual';
}
if (!currentImage) {
return '';
}
if (!currentImage) {
return '';
}
return d.image.id == currentImage.id ? 'current' : '';
});
@ -709,7 +739,7 @@ ImageHistoryTree.prototype.update_ = function(source) {
if (tag == currentTag) {
kind = 'success';
}
html += '<span class="label label-' + kind + ' tag" data-tag="' + tag + '"">' + tag + '</span>';
html += '<span class="label label-' + kind + ' tag" data-tag="' + tag + '" title="' + tag + '">' + tag + '</span>';
}
return html;
});
@ -1686,7 +1716,7 @@ LogUsageChart.prototype.draw = function(container, logData, startDate, endDate)
.duration(500)
.call(chart);
nv.utils.windowResize(chart.update);
nv.utils.windoweResize(chart.update);
chart.multibar.dispatch.on('elementClick', function(e) { that.handleElementClicked_(e); });
chart.dispatch.on('stateChange', function(e) { that.handleStateChange_(e); });

View file

@ -23,7 +23,7 @@
<dd am-time-ago="parseDate(image.value.created)"></dd>
<dt>Compressed Image Size</dt>
<dd><span class="context-tooltip"
title="The amount of data sent between Docker and Quay.io when pushing/pulling"
data-title="The amount of data sent between Docker and Quay.io when pushing/pulling"
bs-tooltip="tooltip.title" data-container="body">{{ image.value.size | bytes }}</span>
</dd>
@ -64,7 +64,7 @@
</div>
<div class="change" ng-repeat="change in combinedChanges | filter:search | limitTo:50">
<i ng-class="{'added': 'fa fa-plus-square', 'removed': 'fa fa-minus-square', 'changed': 'fa fa-pencil-square'}[change.kind]"></i>
<span title="{{change.file}}">
<span data-title="{{change.file}}">
<span style="color: #888;">
<span ng-repeat="folder in getFolders(change.file)"><a href="javascript:void(0)" ng-click="setFolderFilter(getFolder(change.file), $index)">{{folder}}</a>/</span></span><span>{{getFilename(change.file)}}</span>
</span>

View file

@ -1,3 +1,3 @@
<div quay-include="{'Features.BILLING': 'landing-normal.html', '!Features.BILLING': 'landing-login.html'}" onload="chromify()">
<span class="quay-spinner"></span>
</div>
</div>

View file

@ -106,7 +106,7 @@
</td>
</tr>
<tr>
<td>Client Secret: <i class="fa fa-lock fa-lg" title="Keep this secret!" bs-tooltip style="margin-left: 10px"></i></td>
<td>Client Secret: <i class="fa fa-lock fa-lg" data-title="Keep this secret!" bs-tooltip style="margin-left: 10px"></i></td>
<td>
{{ application.client_secret }}
</td>

View file

@ -51,7 +51,7 @@
<div class="section">
<div class="repo-option">
<input type="radio" id="publicrepo" name="publicorprivate" ng-model="repo.is_public" value="1">
<i class="fa fa-unlock fa-large" title="Public Repository"></i>
<i class="fa fa-unlock fa-large" data-title="Public Repository"></i>
<div class="option-description">
<label for="publicrepo"><strong>Public</strong></label>
@ -60,7 +60,7 @@
</div>
<div class="repo-option">
<input type="radio" id="privaterepo" name="publicorprivate" ng-model="repo.is_public" value="0">
<i class="fa fa-lock fa-large" title="Private Repository"></i>
<i class="fa fa-lock fa-large" data-title="Private Repository"></i>
<div class="option-description">
<label for="privaterepo"><strong>Private</strong></label>
@ -75,7 +75,7 @@
<span ng-if="isUserNamespace">under your personal namespace</span>
<span ng-if="!isUserNamespace">under the organization <b>{{ repo.namespace }}</b></span>, you will need to upgrade your plan to
<b style="border-bottom: 1px dotted black;" data-html="true"
title="{{ '<b>' + planRequired.title + '</b><br>' + planRequired.privateRepos + ' private repositories' }}" bs-tooltip>
data-title="{{ '<b>' + planRequired.title + '</b><br>' + planRequired.privateRepos + ' private repositories' }}" bs-tooltip>
{{ planRequired.title }}
</b>.
This will cost $<span>{{ planRequired.price / 100 }}</span>/month.

View file

@ -114,7 +114,7 @@
</span>
</td>
<td>
<a href="/organization/{{ organization.name }}/logs/{{ memberInfo.name }}" title="Member Usage Logs" bs-tooltip="tooltip.title">
<a href="/organization/{{ organization.name }}/logs/{{ memberInfo.name }}" data-title="Member Usage Logs" bs-tooltip="tooltip.title">
<i class="fa fa-book"></i>
</a>
</td>

View file

@ -15,7 +15,7 @@
<div class="row hidden-xs">
<div class="col-md-4 col-md-offset-8 col-sm-5 col-sm-offset-7 header-col" ng-show="organization.is_admin">
Team Permissions
<i class="info-icon fa fa-info-circle" data-placement="bottom" data-original-title="" title=""
<i class="info-icon fa fa-info-circle" data-placement="bottom" data-original-title="" data-title=""
data-content="Global permissions for the team and its members<br><br><dl><dt>Member</dt><dd>Permissions are assigned on a per repository basis</dd><dt>Creator</dt><dd>A team can create its own repositories</dd><dt>Admin</dt><dd>A team has full control of the organization</dd></dl>"></i>
</div>
</div>

View file

@ -4,13 +4,13 @@
</div>
<div class="button-bar-right">
<a href="/organizations/new/" title="Starts the process to create a new organization" bs-tooltip="tooltip.title">
<a href="/organizations/new/" data-title="Starts the process to create a new organization" bs-tooltip="tooltip.title">
<button class="btn btn-success">
<i class="fa fa-plus"></i>
Create New Organization
</button>
</a>
<a href="/user/?migrate" ng-show="!user.anonymous" title="Starts the process to convert this account into an organization" bs-tooltip="tooltip.title">
<a href="/user/?migrate" ng-show="!user.anonymous" data-title="Starts the process to convert this account into an organization" bs-tooltip="tooltip.title">
<button class="btn btn-primary">
<i class="fa fa-caret-square-o-right"></i>
Convert account
@ -43,7 +43,7 @@
<div class="tour-section row">
<div class="col-md-7"><img src="/static/img/org-repo-list.png" title="Repositories - Quay.io" data-screenshot-url="https://quay.io/repository/" class="img-responsive"></div>
<div class="col-md-7"><img src="/static/img/org-repo-list.png" data-title="Repositories - Quay.io" data-screenshot-url="https://quay.io/repository/" class="img-responsive"></div>
<div class="col-md-5">
<div class="tour-section-title">A central collection of repositories</div>
<div class="tour-section-description">
@ -57,7 +57,7 @@
</div>
<div class="tour-section row">
<div class="col-md-7 col-md-push-5"><img src="/static/img/org-admin.png" title="buynlarge Admin - Quay.io" data-screenshot-url="https://quay.io/organization/buynlarge/admin" class="img-responsive"></div>
<div class="col-md-7 col-md-push-5"><img src="/static/img/org-admin.png" data-title="buynlarge Admin - Quay.io" data-screenshot-url="https://quay.io/organization/buynlarge/admin" class="img-responsive"></div>
<div class="col-md-5 col-md-pull-7">
<div class="tour-section-title">Organization settings at a glance</div>
<div class="tour-section-description">
@ -73,7 +73,7 @@
</div>
<div class="tour-section row">
<div class="col-md-7"><img src="/static/img/org-logs.png" title="buynlarge Admin - Quay.io" data-screenshot-url="https://quay.io/organization/buynlarge" class="img-responsive"></div>
<div class="col-md-7"><img src="/static/img/org-logs.png" data-title="buynlarge Admin - Quay.io" data-screenshot-url="https://quay.io/organization/buynlarge" class="img-responsive"></div>
<div class="col-md-5">
<div class="tour-section-title">Logging for comprehensive analysis</div>
<div class="tour-section-description">
@ -94,7 +94,7 @@
</div>
<div class="tour-section row">
<div class="col-md-7 col-md-push-5"><img src="/static/img/org-teams.png" title="buynlarge - Quay.io" data-screenshot-url="https://quay.io/organization/buynlarge" class="img-responsive"></div>
<div class="col-md-7 col-md-push-5"><img src="/static/img/org-teams.png" data-title="buynlarge - Quay.io" data-screenshot-url="https://quay.io/organization/buynlarge" class="img-responsive"></div>
<div class="col-md-5 col-md-pull-7">
<div class="tour-section-title">Teams simplify access controls</div>
<div class="tour-section-description">
@ -115,7 +115,7 @@
</div>
<div class="tour-section row">
<div class="col-md-7"><img src="/static/img/org-repo-admin.png" title="buynlarge/orgrepo - Quay.io" data-screenshot-url="https://quay.io/repository/buynlarge/orgrepo" class="img-responsive"></div>
<div class="col-md-7"><img src="/static/img/org-repo-admin.png" data-title="buynlarge/orgrepo - Quay.io" data-screenshot-url="https://quay.io/repository/buynlarge/orgrepo" class="img-responsive"></div>
<div class="col-md-5">
<div class="tour-section-title">Fine-grained control of sharing</div>
<div class="tour-section-description">
@ -133,13 +133,13 @@
</div>
<div class="button-bar-right button-bar-bottom">
<a href="/organizations/new/" title="Starts the process to create a new organization" bs-tooltip="tooltip.title">
<a href="/organizations/new/" data-title="Starts the process to create a new organization" bs-tooltip="tooltip.title">
<button class="btn btn-success">
<i class="fa fa-plus"></i>
Create New Organization
</button>
</a>
<a href="/user/?migrate" ng-show="!user.anonymous" title="Starts the process to convert this account into an organization" bs-tooltip="tooltip.title">
<a href="/user/?migrate" ng-show="!user.anonymous" data-title="Starts the process to convert this account into an organization" bs-tooltip="tooltip.title">
<button class="btn btn-primary">
<i class="fa fa-caret-square-o-right"></i>
Convert account

View file

@ -7,7 +7,7 @@
<div class="feature">
<span class="context-tooltip" bs-tooltip="tooltip.title" data-container="body" data-placement="right"
title="All plans have unlimited public repositories">
data-title="All plans have unlimited public repositories">
<span class="hidden-sm-inline">Public Repositories</span>
<span class="visible-sm-inline">Public Repos</span>
</span>
@ -15,49 +15,49 @@
</div>
<div class="feature">
<span class="context-tooltip" bs-tooltip="tooltip.title" data-container="body" data-placement="right"
title="SSL encryption is enabled end-to-end for all operations">
data-title="SSL encryption is enabled end-to-end for all operations">
SSL Encryption
</span>
<i class="fa fa-lock visible-lg"></i>
</div>
<div class="feature">
<span class="context-tooltip" bs-tooltip="tooltip.title" data-container="body" data-placement="right"
title="Allows users or organizations to grant permissions in multiple repositories to the same non-login-capable account">
data-title="Allows users or organizations to grant permissions in multiple repositories to the same non-login-capable account">
Robot accounts
</span>
<i class="fa fa-wrench visible-lg"></i>
</div>
<div class="feature">
<span class="context-tooltip" bs-tooltip="tooltip.title" data-container="body" data-placement="right"
title="Repository images can be built directly from Dockerfiles">
data-title="Repository images can be built directly from Dockerfiles">
Dockerfile Build
</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"
title="Grant subsets of users in an organization their own permissions, either on a global basis or a per-repository basis">
data-title="Grant subsets of users in an organization their own permissions, either on a global basis or a per-repository basis">
Teams
</span>
<i class="fa fa-group visible-lg"></i>
</div>
<div class="feature">
<span class="context-tooltip" bs-tooltip="tooltip.title" data-container="body" data-placement="right"
title="Every action take within an organization is logged in detail, with the ability to visualize logs and download them">
data-title="Every action take within an organization is logged in detail, with the ability to visualize logs and download them">
Logging
</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"
title="Administrators can view and download the full invoice history for their organization">
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"
title="All plans have a 14-day free trial">
data-title="All plans have a 14-day free trial">
<span class="hidden-sm-inline">14-Day Free Trial</span>
<span class="visible-sm-inline">14-Day Trial</span>
</span>

View file

@ -50,7 +50,7 @@
<!-- Status Image -->
<a ng-href="/repository/{{ repo.namespace }}/{{ repo.name }}" ng-if="repo && repo.name">
<img ng-src="/repository/{{ repo.namespace }}/{{ repo.name }}/status?token={{ repo.status_token }}" title="Docker Repository on Quay.io">
<img ng-src="/repository/{{ repo.namespace }}/{{ repo.name }}/status?token={{ repo.status_token }}" data-title="Docker Repository on Quay.io">
</a>
<!-- Embed formats -->
@ -271,7 +271,7 @@
</div>
<div ng-show="trigger.is_active" class="trigger-description" trigger="trigger"></div>
<div class="trigger-pull-credentials" ng-if="trigger.is_active && trigger.pull_robot">
<span class="context-tooltip" title="The credentials used by the builder when pulling images" bs-tooltip>
<span class="context-tooltip" data-title="The credentials used by the builder when pulling images" bs-tooltip>
Pull Credentials:
</span>
<span class="entity-reference" entity="trigger.pull_robot"></span>
@ -279,7 +279,7 @@
</td>
<td style="white-space: nowrap;">
<div class="dropdown" style="display: inline-block" ng-visible="trigger.is_active">
<button class="btn btn-default dropdown-toggle" data-toggle="dropdown" title="Build History" bs-tooltip="tooltip.title" data-container="body"
<button class="btn btn-default dropdown-toggle" data-toggle="dropdown" data-title="Build History" bs-tooltip="tooltip.title" data-container="body"
ng-click="loadTriggerBuildHistory(trigger)">
<i class="fa fa-tasks"></i>
<b class="caret"></b>
@ -297,7 +297,7 @@
</div>
<div class="dropdown" style="display: inline-block">
<button class="btn btn-default dropdown-toggle" data-toggle="dropdown" title="Trigger Settings" bs-tooltip="tooltip.title" data-container="body">
<button class="btn btn-default dropdown-toggle" data-toggle="dropdown" data-title="Trigger Settings" bs-tooltip="tooltip.title" data-container="body">
<i class="fa fa-cog"></i>
<b class="caret"></b>
</button>
@ -377,76 +377,17 @@
</div>
</div>
<!-- Auth dialog -->
<div class="docker-auth-dialog" username="'$token'" token="shownToken.code"
shown="!!shownToken" counter="shownTokenCounter">
<i class="fa fa-key"></i> {{ shownToken.friendlyName }}
</div>
<!-- Modal message dialog -->
<div class="modal fade" id="setupTriggerModal">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title">Setup new build trigger</h4>
</div>
<div class="modal-body">
<div class="trigger-option-section">
<table style="width: 100%;">
<tr>
<td style="width: 114px">
<div class="context-tooltip" title="The credentials used by the builder when pulling images" bs-tooltip>
Pull Credentials:
</div>
</td>
<td>
<div ng-if="!isNamespaceAdmin(repo.namespace)" style="color: #aaa;">
In order to set pull credentials for a build trigger, you must be an Administrator of the namespace <strong>{{ repo.namespace }}</strong>
</div>
<div class="btn-group btn-group-sm" ng-if="isNamespaceAdmin(repo.namespace)">
<button type="button" class="btn btn-default"
ng-class="currentSetupTrigger._publicPull ? 'active btn-info' : ''" ng-click="currentSetupTrigger._publicPull = true">Public</button>
<button type="button" class="btn btn-default"
ng-class="currentSetupTrigger._publicPull ? '' : 'active btn-info'" ng-click="currentSetupTrigger._publicPull = false">
<i class="fa fa-wrench"></i>
Robot account
</button>
</div>
</td>
</tr>
<tr ng-show="!currentSetupTrigger._publicPull">
<td>
</td>
<td>
<div class="entity-search" namespace="repo.namespace" include-teams="false"
input-title="'Select robot account for pulling...'"
is-organization="repo.is_organization"
is-persistent="true"
current-entity="currentSetupTrigger._pullEntity"
filter="['robot']"></div>
</td>
</tr>
</table>
</div>
<div class="trigger-description-element trigger-option-section" ng-switch on="currentSetupTrigger.service">
<div ng-switch-when="github">
<div class="trigger-setup-github" repository="repo" trigger="currentSetupTrigger"></div>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary"
ng-disabled="!currentSetupTrigger.$ready || (!currentSetupTrigger._publicPull && !currentSetupTrigger._pullEntity)"
ng-click="finishSetupTrigger(currentSetupTrigger)">Finished</button>
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
<!-- Setup trigger dialog-->
<div class="setup-trigger-dialog" repository="repo"
trigger="currentSetupTrigger"
canceled="cancelSetupTrigger(trigger)"
counter="showTriggerSetupCounter"></div>
<!-- Modal message dialog -->
<div class="modal fade" id="cannotchangeModal">

View file

@ -55,7 +55,7 @@
<i class="fa fa-archive"></i>
<a href="/repository/{{ repo.namespace }}/{{ repo.name }}/build/{{ currentBuild.id }}/buildpack"
style="display: inline-block; margin-left: 6px" bs-tooltip="tooltip.title"
title="View the uploaded build package for this build">Build Package</a>
data-title="View the uploaded build package for this build">Build Package</a>
</span>
</div>
<span class="phase-icon" ng-class="build.phase"></span>

View file

@ -4,7 +4,7 @@
<div class="button-bar-right">
<a href="/new/">
<button class="btn btn-success">
<i class="fa fa-upload user-tool" title="Create new repository"></i>
<i class="fa fa-upload user-tool" data-title="Create new repository"></i>
Create Repository
</button>
</a>
@ -60,11 +60,11 @@
<div class="description markdown-view" content="repository.description" first-line-only="true"></div>
</div>
<div class="page-controls">
<button class="btn btn-default" title="Previous Page" bs-tooltip="title" ng-show="page > 1"
<button class="btn btn-default" data-title="Previous Page" bs-tooltip="title" ng-show="page > 1"
ng-click="movePublicPage(-1)">
<i class="fa fa-chevron-left"></i>
</button>
<button class="btn btn-default" title="Next Page" bs-tooltip="title" ng-show="page < publicPageCount"
<button class="btn btn-default" data-title="Next Page" bs-tooltip="title" ng-show="page < publicPageCount"
ng-click="movePublicPage(1)">
<i class="fa fa-chevron-right"></i>
</button>

View file

@ -69,10 +69,10 @@
<td>
<img src="//www.gravatar.com/avatar/{{ authInfo.gravatar }}?s=16&d=identicon">
<a href="{{ authInfo.application.url }}" ng-if="authInfo.application.url" target="_blank"
title="{{ authInfo.application.description || authInfo.application.name }}" bs-tooltip>
data-title="{{ authInfo.application.description || authInfo.application.name }}" bs-tooltip>
{{ authInfo.application.name }}
</a>
<span ng-if="!authInfo.application.url" title="{{ authInfo.application.description || authInfo.application.name }}" bs-tooltip>
<span ng-if="!authInfo.application.url" data-title="{{ authInfo.application.description || authInfo.application.name }}" bs-tooltip>
{{ authInfo.application.name }}
</span>
<span class="by">{{ authInfo.application.organization.name }}</span>
@ -80,7 +80,7 @@
<td>
<span class="label label-default scope"
ng-class="{'repo:admin': 'label-primary', 'repo:write': 'label-success', 'repo:create': 'label-success'}[scopeInfo.scope]"
ng-repeat="scopeInfo in authInfo.scopes" title="{{ scopeInfo.description }}" bs-tooltip>
ng-repeat="scopeInfo in authInfo.scopes" data-title="{{ scopeInfo.description }}" bs-tooltip>
{{ scopeInfo.scope }}
</span>
</td>
@ -124,8 +124,8 @@
<div class="panel-title">Change e-mail address</div>
<div class="panel-body">
<form class="form-change col-md-6" id="changeEmailForm" name="changeEmailForm" ng-submit="changeEmail()" data-trigger="manual"
data-content="{{ changeEmailError }}" data-placement="right" ng-show="!awaitingConfirmation && !registering">
<form class="form-change col-md-6" id="changeEmailForm" name="changeEmailForm" ng-submit="changeEmail()"
ng-show="!awaitingConfirmation && !registering">
<input type="email" class="form-control" placeholder="Your new e-mail address" ng-model="cuser.email" required>
<button class="btn btn-primary" ng-disabled="changeEmailForm.$invalid || cuser.email == user.email" type="submit">Change E-mail Address</button>
</form>
@ -146,11 +146,12 @@
<span class="help-block" ng-show="changePasswordSuccess">Password changed successfully</span>
<div ng-show="!updatingUser" class="panel-body">
<form class="form-change col-md-6" id="changePasswordForm" name="changePasswordForm" ng-submit="changePassword()" data-trigger="manual"
data-content="{{ changePasswordError }}" data-placement="right" ng-show="!awaitingConfirmation && !registering">
<input type="password" class="form-control" placeholder="Your new password" ng-model="cuser.password" required>
<form class="form-change col-md-6" id="changePasswordForm" name="changePasswordForm" ng-submit="changePassword()"
ng-show="!awaitingConfirmation && !registering">
<input type="password" class="form-control" placeholder="Your new password" ng-model="cuser.password" required
ng-pattern="/^.{8,}$/">
<input type="password" class="form-control" placeholder="Verify your new password" ng-model="cuser.repeatPassword"
match="cuser.password" required>
match="cuser.password" required ng-pattern="/^.{8,}$/">
<button class="btn btn-danger" ng-disabled="changePasswordForm.$invalid" type="submit"
analytics-on analytics-event="change_pass">Change Password</button>
</form>
@ -169,7 +170,7 @@
<div class="panel-title">GitHub Login:</div>
<div class="panel-body">
<div ng-show="githubLogin" class="lead col-md-8">
<i class="fa fa-github fa-lg" style="margin-right: 6px;" title="GitHub" bs-tooltip="tooltip.title"></i>
<i class="fa fa-github fa-lg" style="margin-right: 6px;" data-title="GitHub" bs-tooltip="tooltip.title"></i>
<b><a href="https://github.com/{{githubLogin}}" target="_blank">{{githubLogin}}</a></b>
</div>
<div ng-show="!githubLogin" class="col-md-8">

View file

@ -17,7 +17,7 @@
<!-- Builds -->
<div class="dropdown" data-placement="top" style="display: inline-block"
bs-tooltip=""
title="{{ runningBuilds.length ? 'Dockerfile Builds Running: ' + (runningBuilds.length) : 'Dockerfile Build' }}"
data-title="{{ runningBuilds.length ? 'Dockerfile Builds Running: ' + (runningBuilds.length) : 'Dockerfile Build' }}"
ng-show="repo.can_write || buildHistory.length">
<button class="btn btn-default dropdown-toggle" data-toggle="dropdown">
<i class="fa fa-tasks fa-lg"></i>
@ -51,15 +51,15 @@
<!-- Admin -->
<a id="admin-cog" href="{{ '/repository/' + repo.namespace + '/' + repo.name + '/admin' }}"
ng-show="repo.can_admin">
<button class="btn btn-default" title="Repository Settings" bs-tooltip="tooltip" data-placement="top">
<button class="btn btn-default" data-title="Repository Settings" bs-tooltip="tooltip" data-placement="top">
<i class="fa fa-cog fa-lg"></i></button></a>
<!-- Pull Command -->
<span class="pull-command visible-md-inline">
<div class="pull-container" title="Pull repository" bs-tooltip="tooltip.title">
<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" title="Copy to Clipboard" data-clipboard-target="pull-text">
<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>
@ -145,10 +145,10 @@
</ul>
</div>
<span class="right-tag-controls">
<i class="fa fa-tag" title="Tags" bs-tooltip="title">
<i class="fa fa-tag" data-title="Tags" bs-tooltip="title">
<span class="tag-count">{{getTagCount(repo)}}</span>
</i>
<i class="fa fa-archive" title="Images" bs-tooltip="title">
<i class="fa fa-archive" data-title="Images" bs-tooltip="title">
<span class="tag-count">{{imageHistory.value.length}}</span>
</i>
</span>
@ -162,7 +162,7 @@
<dd am-time-ago="parseDate(currentTag.image.created)"></dd>
<dt>Total Compressed Size</dt>
<dd><span class="context-tooltip"
title="The amount of data sent between Docker and Quay.io when pushing/pulling"
data-title="The amount of data sent between Docker and Quay.io when pushing/pulling"
bs-tooltip="tooltip.title" data-container="body">{{ getTotalSize(currentTag) | bytes }}</span>
</dd>
</dl>
@ -171,7 +171,7 @@
<div class="tag-image-size" ng-repeat="image in getImagesForTagBySize(currentTag) | limitTo: 10">
<span class="size-limiter">
<span class="size-bar" style="{{ 'width:' + (image.size / getTotalSize(currentTag)) * 100 + '%' }}"
bs-tooltip="" title="{{ image.size | bytes }}"></span>
bs-tooltip="" data-title="{{ image.size | bytes }}"></span>
</span>
<span class="size-title"><a href="javascript:void(0)" ng-click="setImage(image.id, true)">{{ image.id.substr(0, 12) }}</a></span>
</div>
@ -199,14 +199,14 @@
<dd><a href="{{'/repository/' + repo.namespace + '/' + repo.name + '/image/' + currentImage.id}}">{{ currentImage.id }}</a></dd>
<dt>Compressed Image Size</dt>
<dd><span class="context-tooltip"
title="The amount of data sent between Docker and Quay.io when pushing/pulling"
data-title="The amount of data sent between Docker and Quay.io when pushing/pulling"
bs-tooltip="tooltip.title" data-container="body">{{ currentImage.size | bytes }}</span>
</dd>
<dt ng-show="currentImage.command && currentImage.command.length">Command</dt>
<dd ng-show="currentImage.command && currentImage.command.length" class="codetooltipcontainer">
<pre class="formatted-command trimmed"
data-html="true"
bs-tooltip="" title="{{ getTooltipCommand(currentImage) }}"
bs-tooltip="" data-title="{{ getTooltipCommand(currentImage) }}"
data-placement="top">{{ getFormattedCommand(currentImage) }}</pre>
</dd>
</dl>
@ -216,17 +216,17 @@
<div class="changes-container small-changes-container"
ng-show="currentImageChanges.changed.length || currentImageChanges.added.length || currentImageChanges.removed.length">
<div class="changes-count-container accordion-toggle" data-toggle="collapse" data-parent="#accordion" data-target="#collapseChanges">
<span class="change-count added" ng-show="currentImageChanges.added.length > 0" title="Files Added"
<span class="change-count added" ng-show="currentImageChanges.added.length > 0" data-title="Files Added"
bs-tooltip="tooltip.title" data-placement="top">
<i class="fa fa-plus-square"></i>
<b>{{currentImageChanges.added.length}}</b>
</span>
<span class="change-count removed" ng-show="currentImageChanges.removed.length > 0" title="Files Removed"
<span class="change-count removed" ng-show="currentImageChanges.removed.length > 0" data-title="Files Removed"
bs-tooltip="tooltip.title" data-placement="top">
<i class="fa fa-minus-square"></i>
<b>{{currentImageChanges.removed.length}}</b>
</span>
<span class="change-count changed" ng-show="currentImageChanges.changed.length > 0" title="Files Changed"
<span class="change-count changed" ng-show="currentImageChanges.changed.length > 0" data-title="Files Changed"
bs-tooltip="tooltip.title" data-placement="top">
<i class="fa fa-pencil-square"></i>
<b>{{currentImageChanges.changed.length}}</b>
@ -237,15 +237,15 @@
<div class="well well-sm">
<div class="change added" ng-repeat="file in currentImageChanges.added | limitTo:5">
<i class="fa fa-plus-square"></i>
<span title="{{file}}">{{file}}</span>
<span data-title="{{file}}">{{file}}</span>
</div>
<div class="change removed" ng-repeat="file in currentImageChanges.removed | limitTo:5">
<i class="fa fa-minus-square"></i>
<span title="{{file}}">{{file}}</span>
<span data-title="{{file}}">{{file}}</span>
</div>
<div class="change changed" ng-repeat="file in currentImageChanges.changed | limitTo:5">
<i class="fa fa-pencil-square"></i>
<span title="{{file}}">{{file}}</span>
<span data-title="{{file}}">{{file}}</span>
</div>
</div>
<div class="more-changes" ng-show="getMoreCount(currentImageChanges) > 0">
@ -296,7 +296,7 @@
<!--<i class="fa fa-archive"></i>-->
<span class="image-listing-circle"></span>
<span class="image-listing-line"></span>
<span class="context-tooltip image-listing-id" bs-tooltip="" title="{{ getFirstTextLine(image.comment) }}"
<span class="context-tooltip image-listing-id" bs-tooltip="" data-title="{{ getFirstTextLine(image.comment) }}"
data-html="true">
{{ image.id.substr(0, 12) }}
</span>