diff --git a/static/css/directives/repo-view/repo-panel-info.css b/static/css/directives/repo-view/repo-panel-info.css index 2291fd852..a0d35b312 100644 --- a/static/css/directives/repo-view/repo-panel-info.css +++ b/static/css/directives/repo-view/repo-panel-info.css @@ -1,3 +1,14 @@ +.repo-panel-info-element .right-controls { + margin-bottom: 20px; + float: right; +} + +.repo-panel-info-element .right-controls .copy-box { + width: 400px; + display: inline-block; + margin-left: 10px; +} + .repo-panel-info-element .stat-col { border-right: 2px solid #eee; } diff --git a/static/css/directives/repo-view/repo-panel-tags.css b/static/css/directives/repo-view/repo-panel-tags.css index aebf689f8..9df9c2679 100644 --- a/static/css/directives/repo-view/repo-panel-tags.css +++ b/static/css/directives/repo-view/repo-panel-tags.css @@ -63,4 +63,9 @@ .repo-panel-tags-element .options-col { padding-left: 20px; +} + +.repo-panel-tags-element .options-col .fa-download { + color: #999; + cursor: pointer; } \ No newline at end of file diff --git a/static/css/directives/ui/fetch-tag-dialog.css b/static/css/directives/ui/fetch-tag-dialog.css new file mode 100644 index 000000000..8155650a5 --- /dev/null +++ b/static/css/directives/ui/fetch-tag-dialog.css @@ -0,0 +1,19 @@ +.fetch-tag-dialog .modal-table { + width: 100%; +} + +.fetch-tag-dialog .modal-table .first-col { + width: 140px; +} + +.fetch-tag-dialog .co-dialog .modal-body { + padding: 20px; +} + +.fetch-tag-dialog .entity-search { + margin: 10px; +} + +.fetch-tag-dialog pre.command { + margin-top: 10px; +} \ No newline at end of file diff --git a/static/css/quay.css b/static/css/quay.css index bcc0f6584..333fbd411 100644 --- a/static/css/quay.css +++ b/static/css/quay.css @@ -4787,6 +4787,20 @@ i.slack-icon { height: 16px; } +i.docker-icon { + background-image: url(/static/img/docker.png); + background-size: 16px; + width: 16px; + height: 16px; +} + +i.rocket-icon { + background-image: url(/static/img/rocket.png); + background-size: 16px; + width: 16px; + height: 16px; +} + .external-notification-view-element { margin: 10px; padding: 6px; diff --git a/static/directives/fetch-tag-dialog.html b/static/directives/fetch-tag-dialog.html new file mode 100644 index 000000000..befe6cedc --- /dev/null +++ b/static/directives/fetch-tag-dialog.html @@ -0,0 +1,70 @@ +
+ + +
\ No newline at end of file diff --git a/static/directives/repo-view/repo-panel-info.html b/static/directives/repo-view/repo-panel-info.html index 24f779460..245e3ba22 100644 --- a/static/directives/repo-view/repo-panel-info.html +++ b/static/directives/repo-view/repo-panel-info.html @@ -62,7 +62,14 @@
+ + + +

Description

+
Image + + + + + @@ -98,4 +104,7 @@
\ No newline at end of file + action-handler="tagActionHandler">
+ +
+
\ No newline at end of file diff --git a/static/img/docker.png b/static/img/docker.png new file mode 100644 index 000000000..ee01a5ee8 Binary files /dev/null and b/static/img/docker.png differ diff --git a/static/img/rocket.png b/static/img/rocket.png new file mode 100644 index 000000000..e42c08141 Binary files /dev/null and b/static/img/rocket.png differ diff --git a/static/js/directives/repo-view/repo-panel-info.js b/static/js/directives/repo-view/repo-panel-info.js index 5946bac73..2902699ec 100644 --- a/static/js/directives/repo-view/repo-panel-info.js +++ b/static/js/directives/repo-view/repo-panel-info.js @@ -12,7 +12,16 @@ angular.module('quay').directive('repoPanelInfo', function () { 'repository': '=repository', 'builds': '=builds' }, - controller: function($scope, $element, ApiService) { + controller: function($scope, $element, ApiService, Config) { + $scope.$watch('repository', function(repository) { + if (!$scope.repository) { return; } + + var namespace = $scope.repository.namespace; + var name = $scope.repository.name; + + $scope.pullCommand = 'docker pull ' + Config.getDomain() + '/' + namespace + '/' + name; + }); + $scope.updateDescription = function(content) { $scope.repository.description = content; $scope.repository.put(); diff --git a/static/js/directives/ui/fetch-tag-dialog.js b/static/js/directives/ui/fetch-tag-dialog.js new file mode 100644 index 000000000..820339e6c --- /dev/null +++ b/static/js/directives/ui/fetch-tag-dialog.js @@ -0,0 +1,104 @@ +/** + * An element which adds a of dialog for fetching a tag. + */ +angular.module('quay').directive('fetchTagDialog', function () { + var directiveDefinitionObject = { + priority: 0, + templateUrl: '/static/directives/fetch-tag-dialog.html', + replace: false, + transclude: false, + restrict: 'C', + scope: { + 'repository': '=repository', + 'actionHandler': '=actionHandler' + }, + controller: function($scope, $element, $timeout, ApiService, UserService, Config) { + $scope.clearCounter = 0; + $scope.currentFormat = null; + $scope.currentEntity = null; + $scope.currentRobot = null; + + $scope.formats = [ + { + 'title': 'Squashed Docker Image', + 'icon': 'fa-file-archive-o', + 'command': 'curl -L -f {http}://{pull_user}:{pull_password}@{hostname}/c1/squash/{namespace}/{name}/{tag} | docker load', + 'require_creds': true + }, + + { + 'title': 'Basic Docker Pull', + 'icon': 'docker-icon', + 'command': 'docker pull {hostname}/{namespace}/{name}:{tag}' + }]; + + $scope.$watch('currentEntity', function(entity) { + if (!entity) { + $scope.currentRobot = null; + return; + } + + if ($scope.currentRobot && $scope.currentRobot.name == entity.name) { + return; + } + + $scope.currentRobot = null; + + var parts = entity.name.split('+'); + var namespace = parts[0]; + var shortname = parts[1]; + + var params = { + 'robot_shortname': shortname + }; + + var orgname = UserService.isOrganization(namespace) ? namespace : ''; + ApiService.getRobot(orgname, null, params).then(function(resp) { + $scope.currentRobot = resp; + }, ApiService.errorDisplay('Cannot download robot token')); + }); + + $scope.getCommand = function(format, robot) { + if (!format || !format.command) { return ''; } + if (format.require_creds && !robot) { return ''; } + + var params = { + 'pull_user': robot ? robot.name : '', + 'pull_password': robot ? robot.token : '', + 'hostname': Config.getDomain(), + 'http': Config.getHttp(), + 'namespace': $scope.repository.namespace, + 'name': $scope.repository.name, + 'tag': $scope.currentTag.name + }; + + var value = format.command; + for (var param in params) { + if (!params.hasOwnProperty(param)) { continue; } + value = value.replace('{' + param + '}', params[param]); + } + + return value; + }; + + $scope.setFormat = function(format) { + $scope.currentFormat = format; + }; + + $scope.actionHandler = { + 'askFetchTag': function(tag) { + $scope.currentTag = tag; + $scope.currentFormat = null; + $scope.currentEntity = null; + $scope.currentRobot = null; + + $scope.clearCounter++; + + $element.find('#copyClipboard').clipboardCopy(); + $element.find('#fetchTagDialog').modal({}); + } + }; + } + }; + return directiveDefinitionObject; +}); diff --git a/static/js/services/features-config.js b/static/js/services/features-config.js index e65f2fb9f..2632aa0d3 100644 --- a/static/js/services/features-config.js +++ b/static/js/services/features-config.js @@ -54,6 +54,10 @@ angular.module('quay').factory('Config', [function() { return config['PREFERRED_URL_SCHEME'] + '://' + auth + config['SERVER_HOSTNAME']; }; + config.getHttp = function() { + return config['PREFERRED_URL_SCHEME']; + }; + config.getUrl = function(opt_path) { var path = opt_path || ''; return config['PREFERRED_URL_SCHEME'] + '://' + config['SERVER_HOSTNAME'] + path; diff --git a/static/js/services/user-service.js b/static/js/services/user-service.js index 7f5ee4463..7d9cd7aca 100644 --- a/static/js/services/user-service.js +++ b/static/js/services/user-service.js @@ -83,6 +83,10 @@ function(ApiService, CookieService, $rootScope, Config) { }); }; + userService.isOrganization = function(name) { + return !!userService.getOrganization(name); + }; + userService.getOrganization = function(name) { if (!userResponse || !userResponse.organizations) { return null; } for (var i = 0; i < userResponse.organizations.length; ++i) {