From 6caa6657a7e1cd8a30f3703defe2791f03cc765c Mon Sep 17 00:00:00 2001 From: Jimmy Zelinskie Date: Fri, 24 Apr 2015 13:20:03 -0400 Subject: [PATCH 01/38] fix manual upload build description --- static/directives/triggered-build-description.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/directives/triggered-build-description.html b/static/directives/triggered-build-description.html index fe9f31a80..2e41e9670 100644 --- a/static/directives/triggered-build-description.html +++ b/static/directives/triggered-build-description.html @@ -64,7 +64,7 @@ - + Triggered by commit
From fd65ca5916e562c8f0d44fce795fb9e949d7d891 Mon Sep 17 00:00:00 2001 From: Jimmy Zelinskie Date: Fri, 24 Apr 2015 16:11:24 -0400 Subject: [PATCH 02/38] migration: add custom-git service to database --- ...add_custom_git_trigger_type_to_database.py | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 data/migrations/versions/37c47a7af956_add_custom_git_trigger_type_to_database.py diff --git a/data/migrations/versions/37c47a7af956_add_custom_git_trigger_type_to_database.py b/data/migrations/versions/37c47a7af956_add_custom_git_trigger_type_to_database.py new file mode 100644 index 000000000..ef2f9efa3 --- /dev/null +++ b/data/migrations/versions/37c47a7af956_add_custom_git_trigger_type_to_database.py @@ -0,0 +1,25 @@ +"""add custom-git trigger type to database + +Revision ID: 37c47a7af956 +Revises: 3fee6f979c2a +Create Date: 2015-04-24 14:50:26.275516 + +""" + +# revision identifiers, used by Alembic. +revision = '37c47a7af956' +down_revision = '3fee6f979c2a' + +from alembic import op +import sqlalchemy as sa + + +def upgrade(tables): + op.bulk_insert(tables.buildtriggerservice, [{'id': 2, 'name': 'custom-git'}]) + + +def downgrade(tables): + op.execute( + tables.buildtriggerservice.delete() + .where(tables.buildtriggerservice.c.name == op.inline_literal('custom-git')) + ) From e70343d849786dc3c20079e65bd4b37cd3fe2114 Mon Sep 17 00:00:00 2001 From: Joseph Schorr Date: Fri, 24 Apr 2015 16:22:19 -0400 Subject: [PATCH 03/38] Faster cache lookup by removing a join with the ImagePlacementTable, removing the extra loop to add the locations and filtering the images looked up by the base image --- buildman/jobutil/buildjob.py | 4 +++- data/model/legacy.py | 15 +++++++++++++++ test/test_imagetree.py | 21 +++++++++++++++++++++ 3 files changed, 39 insertions(+), 1 deletion(-) diff --git a/buildman/jobutil/buildjob.py b/buildman/jobutil/buildjob.py index 3c00a3bc3..91c98f6b0 100644 --- a/buildman/jobutil/buildjob.py +++ b/buildman/jobutil/buildjob.py @@ -104,7 +104,9 @@ class BuildJob(object): return None # Build an in-memory tree of the full heirarchy of images in the repository. - all_images = model.get_repository_images(repo_namespace, repo_name) + all_images = model.get_repository_images_directly(repo_build.repository, + with_ancestor=base_image) + all_tags = model.list_repository_tags(repo_namespace, repo_name) tree = ImageTree(all_images, all_tags, base_filter=base_image.id) diff --git a/data/model/legacy.py b/data/model/legacy.py index 813fe0e67..38dea3ecc 100644 --- a/data/model/legacy.py +++ b/data/model/legacy.py @@ -1751,6 +1751,21 @@ def get_matching_repository_images(namespace_name, repository_name, docker_image return _get_repository_images_base(namespace_name, repository_name, modify_query) + +def get_repository_images_directly(repository, with_ancestor=None): + query = (Image + .select(Image, ImageStorage) + .join(ImageStorage) + .where(Image.repository == repository)) + + if with_ancestor: + ancestors_string = '%s%s/' % (with_ancestor.ancestors, with_ancestor.id) + query = query.where((Image.ancestors ** (ancestors_string + '%')) | + (Image.id == with_ancestor.id)) + + return query + + def get_repository_images(namespace_name, repository_name): return _get_repository_images_base(namespace_name, repository_name, lambda q: q) diff --git a/test/test_imagetree.py b/test/test_imagetree.py index e257792c4..f1622e868 100644 --- a/test/test_imagetree.py +++ b/test/test_imagetree.py @@ -91,6 +91,27 @@ class TestImageTree(unittest.TestCase): self.assertEquals('staging', tree.tag_containing_image(result[0])) + def test_longest_path_simple_repo_direct_lookup(self): + repository = model.get_repository(NAMESPACE, SIMPLE_REPO) + all_images = list(model.get_repository_images(NAMESPACE, SIMPLE_REPO)) + all_tags = list(model.list_repository_tags(NAMESPACE, SIMPLE_REPO)) + + base_image = self._get_base_image(all_images) + tag_image = all_tags[0].image + + def checker(index, image): + return True + + filtered_images = model.get_repository_images_directly(repository, with_ancestor=base_image) + self.assertEquals(set([f.id for f in filtered_images]), set([a.id for a in all_images])) + + tree = ImageTree(filtered_images, all_tags) + + ancestors = tag_image.ancestors.split('/')[2:-1] # Skip the first image. + result = tree.find_longest_path(base_image.id, checker) + self.assertEquals(3, len(result)) + self.assertEquals('latest', tree.tag_containing_image(result[-1])) + if __name__ == '__main__': unittest.main() From 31260d50f5ad343eff417bdfe660951c80b9b015 Mon Sep 17 00:00:00 2001 From: Joseph Schorr Date: Fri, 24 Apr 2015 16:37:37 -0400 Subject: [PATCH 04/38] Rename the new images method to a slightly better name --- buildman/jobutil/buildjob.py | 4 ++-- data/model/legacy.py | 2 +- test/test_imagetree.py | 3 ++- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/buildman/jobutil/buildjob.py b/buildman/jobutil/buildjob.py index 91c98f6b0..a8a5e2b80 100644 --- a/buildman/jobutil/buildjob.py +++ b/buildman/jobutil/buildjob.py @@ -104,8 +104,8 @@ class BuildJob(object): return None # Build an in-memory tree of the full heirarchy of images in the repository. - all_images = model.get_repository_images_directly(repo_build.repository, - with_ancestor=base_image) + all_images = model.get_repository_images_without_placements(repo_build.repository, + with_ancestor=base_image) all_tags = model.list_repository_tags(repo_namespace, repo_name) tree = ImageTree(all_images, all_tags, base_filter=base_image.id) diff --git a/data/model/legacy.py b/data/model/legacy.py index 38dea3ecc..014df99df 100644 --- a/data/model/legacy.py +++ b/data/model/legacy.py @@ -1752,7 +1752,7 @@ def get_matching_repository_images(namespace_name, repository_name, docker_image return _get_repository_images_base(namespace_name, repository_name, modify_query) -def get_repository_images_directly(repository, with_ancestor=None): +def get_repository_images_without_placements(repository, with_ancestor=None): query = (Image .select(Image, ImageStorage) .join(ImageStorage) diff --git a/test/test_imagetree.py b/test/test_imagetree.py index f1622e868..9d86da7ba 100644 --- a/test/test_imagetree.py +++ b/test/test_imagetree.py @@ -102,7 +102,8 @@ class TestImageTree(unittest.TestCase): def checker(index, image): return True - filtered_images = model.get_repository_images_directly(repository, with_ancestor=base_image) + filtered_images = model.get_repository_images_without_placements(repository, + with_ancestor=base_image) self.assertEquals(set([f.id for f in filtered_images]), set([a.id for a in all_images])) tree = ImageTree(filtered_images, all_tags) From 01698e8d16a56a16bd8a2a78e90900f854591227 Mon Sep 17 00:00:00 2001 From: Joseph Schorr Date: Fri, 24 Apr 2015 16:42:31 -0400 Subject: [PATCH 05/38] Fix OAuth 500 error --- endpoints/web.py | 7 ++++--- templates/oauthorize.html | 4 ++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/endpoints/web.py b/endpoints/web.py index 46a0b1502..13f8cbc55 100644 --- a/endpoints/web.py +++ b/endpoints/web.py @@ -27,6 +27,7 @@ from util.systemlogs import build_logs_archive from auth import scopes import features +import json logger = logging.getLogger(__name__) @@ -431,16 +432,16 @@ def request_authorization_code(): # Load the application information. oauth_app = provider.get_application_for_client_id(client_id) - app_email = oauth_app.email or organization.email + app_email = oauth_app.avatar_email or oauth_app.organization.email oauth_app_view = { 'name': oauth_app.name, 'description': oauth_app.description, 'url': oauth_app.application_uri, - 'avatar': avatar.get_data(oauth_app.name, app_email, 'app'), + 'avatar': json.dumps(avatar.get_data(oauth_app.name, app_email, 'app')), 'organization': { 'name': oauth_app.organization.username, - 'avatar': avatar.get_data_for_org(oauth_app.organization) + 'avatar': json.dumps(avatar.get_data_for_org(oauth_app.organization)) } } diff --git a/templates/oauthorize.html b/templates/oauthorize.html index 0997f1e1b..42aa44e08 100644 --- a/templates/oauthorize.html +++ b/templates/oauthorize.html @@ -13,10 +13,10 @@
- +

{{ application.name }}

- {{ application.organization.name }}

From f60e56c056e57598b69c37179f8a81787020d413 Mon Sep 17 00:00:00 2001 From: Joseph Schorr Date: Mon, 27 Apr 2015 14:02:55 -0400 Subject: [PATCH 06/38] Fix text only links and some spacing in FF --- static/css/directives/repo-view/repo-panel-info.css | 4 ++++ static/directives/build-mini-status.html | 5 +++-- static/directives/robots-manager.html | 4 ++-- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/static/css/directives/repo-view/repo-panel-info.css b/static/css/directives/repo-view/repo-panel-info.css index 496d32a2f..1b39b3a6b 100644 --- a/static/css/directives/repo-view/repo-panel-info.css +++ b/static/css/directives/repo-view/repo-panel-info.css @@ -67,4 +67,8 @@ .repo-panel-info-element .builds-list { min-height: 200px; +} + +.repo-panel-info-element .copy-box { + vertical-align: middle; } \ No newline at end of file diff --git a/static/directives/build-mini-status.html b/static/directives/build-mini-status.html index bba073329..97b14d5c5 100644 --- a/static/directives/build-mini-status.html +++ b/static/directives/build-mini-status.html @@ -1,6 +1,7 @@ - +
diff --git a/static/directives/robots-manager.html b/static/directives/robots-manager.html index 2e71ac142..4f0e76670 100644 --- a/static/directives/robots-manager.html +++ b/static/directives/robots-manager.html @@ -65,7 +65,7 @@ - + @@ -78,7 +78,7 @@ Direct Permissions on - From 1d9466a9af220aef5ad56394468c01acdbf7c521 Mon Sep 17 00:00:00 2001 From: Joseph Schorr Date: Mon, 27 Apr 2015 14:11:05 -0400 Subject: [PATCH 11/38] Make the title box more responsive --- static/directives/cor-title-content.html | 2 +- static/directives/cor-title-link.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/static/directives/cor-title-content.html b/static/directives/cor-title-content.html index 5b2077d08..0d3e13ddd 100644 --- a/static/directives/cor-title-content.html +++ b/static/directives/cor-title-content.html @@ -1,3 +1,3 @@ -
+

diff --git a/static/directives/cor-title-link.html b/static/directives/cor-title-link.html index 428671f86..afd345c75 100644 --- a/static/directives/cor-title-link.html +++ b/static/directives/cor-title-link.html @@ -1 +1 @@ - + From 0d1edef0d6b69267b7f02ea6ae5b2d9566110cde Mon Sep 17 00:00:00 2001 From: Joseph Schorr Date: Mon, 27 Apr 2015 14:13:26 -0400 Subject: [PATCH 12/38] Make the repo view title area more responsive --- static/css/pages/repo-view.css | 6 ++---- static/partials/repo-view.html | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/static/css/pages/repo-view.css b/static/css/pages/repo-view.css index 73e38891c..08971ed23 100644 --- a/static/css/pages/repo-view.css +++ b/static/css/pages/repo-view.css @@ -50,9 +50,7 @@ } @media (max-width: 767px) { - .repository-view .repo-star { - position: absolute; - top: 16px; - left: -16px; + .repository-view .cor-title-content { + padding-top: 8px; } } diff --git a/static/partials/repo-view.html b/static/partials/repo-view.html index 827df6146..b2dcee57c 100644 --- a/static/partials/repo-view.html +++ b/static/partials/repo-view.html @@ -11,7 +11,7 @@ {{ namespace }} / {{ name }} - +
From 6581a024bcb8a63f6c0b132cdbbd8ad227bf4fc2 Mon Sep 17 00:00:00 2001 From: Joseph Schorr Date: Mon, 27 Apr 2015 14:17:55 -0400 Subject: [PATCH 13/38] Make add tag be at the top of the tags operations menu --- static/directives/repo-view/repo-panel-tags.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/static/directives/repo-view/repo-panel-tags.html b/static/directives/repo-view/repo-panel-tags.html index cde9744ca..a95318ba2 100644 --- a/static/directives/repo-view/repo-panel-tags.html +++ b/static/directives/repo-view/repo-panel-tags.html @@ -133,12 +133,12 @@ - - Delete Tag - Add New Tag + + Delete Tag + From 4834dbd7c93a8d414a4999eefedcfc63b1b85abf Mon Sep 17 00:00:00 2001 From: Joseph Schorr Date: Mon, 27 Apr 2015 14:19:50 -0400 Subject: [PATCH 14/38] Add confirmation for deleting a robot --- static/directives/robots-manager.html | 2 +- static/js/directives/ui/robots-manager.js | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/static/directives/robots-manager.html b/static/directives/robots-manager.html index 4f0e76670..345d28fc3 100644 --- a/static/directives/robots-manager.html +++ b/static/directives/robots-manager.html @@ -96,7 +96,7 @@ View Credentials - + Delete Robot {{ robotInfo.name }} diff --git a/static/js/directives/ui/robots-manager.js b/static/js/directives/ui/robots-manager.js index 8cacfa687..ca672c4be 100644 --- a/static/js/directives/ui/robots-manager.js +++ b/static/js/directives/ui/robots-manager.js @@ -128,6 +128,15 @@ angular.module('quay').directive('robotsManager', function () { }, ApiService.errorDisplay('Cannot delete robot account')); }; + + $scope.askDeleteRobot = function(info) { + bootbox.confirm('Are you sure you want to delete robot ' + info.name + '?', function(resp) { + if (resp) { + $scope.deleteRobot(info); + } + }); + }; + var update = function() { if (!$scope.user && !$scope.organization) { return; } if ($scope.loading || !$scope.isEnabled) { return; } From 9d64291d11e0013065ac327012daa66457757dd4 Mon Sep 17 00:00:00 2001 From: Joseph Schorr Date: Mon, 27 Apr 2015 14:22:04 -0400 Subject: [PATCH 15/38] Fix logs filter box in FF --- static/css/directives/ui/logs-view.css | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/static/css/directives/ui/logs-view.css b/static/css/directives/ui/logs-view.css index 198bad42f..db77c9b23 100644 --- a/static/css/directives/ui/logs-view.css +++ b/static/css/directives/ui/logs-view.css @@ -94,7 +94,12 @@ white-space: nowrap; } +.logs-view-element .side-controls .filter-input { + vertical-align: middle; +} + .logs-view-element .side-controls { + float: none !important; text-align: right; margin-bottom: 20px; } \ No newline at end of file From 7ad06d2e5d8432c0ba1bc11ae6101302f6bc0ea3 Mon Sep 17 00:00:00 2001 From: Joseph Schorr Date: Mon, 27 Apr 2015 14:23:52 -0400 Subject: [PATCH 16/38] Remove dotted focus borders in FF --- static/css/core-ui.css | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/static/css/core-ui.css b/static/css/core-ui.css index d6281be96..4c401c8a2 100644 --- a/static/css/core-ui.css +++ b/static/css/core-ui.css @@ -1,3 +1,10 @@ +a:active { + outline: none !important; +} + +a:focus { + outline: none !important; +} .co-options-menu .fa-gear { color: #999; From 62f8d23a91792435119b1f946d5a0c38c25d67bb Mon Sep 17 00:00:00 2001 From: Joseph Schorr Date: Mon, 27 Apr 2015 14:25:44 -0400 Subject: [PATCH 17/38] Fix padding on warning in the application page --- static/partials/manage-application.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/partials/manage-application.html b/static/partials/manage-application.html index 0fb160f7c..45fb03ad5 100644 --- a/static/partials/manage-application.html +++ b/static/partials/manage-application.html @@ -14,7 +14,7 @@
-
+
Warning: There is no OAuth Redirect setup for this application. Please enter it in the Settings tab.
From 526f9c07b9e77d459b18698e1e0a49a21d1dc3ab Mon Sep 17 00:00:00 2001 From: Joseph Schorr Date: Mon, 27 Apr 2015 14:31:49 -0400 Subject: [PATCH 18/38] Hide images in search result descriptions and make the descriptions align better --- static/css/directives/ui/header-bar.css | 7 ++++++- static/directives/new-header-bar.html | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/static/css/directives/ui/header-bar.css b/static/css/directives/ui/header-bar.css index 4f3239d2d..7fa08bf22 100644 --- a/static/css/directives/ui/header-bar.css +++ b/static/css/directives/ui/header-bar.css @@ -153,7 +153,7 @@ nav.navbar-default .navbar-nav>li>a.active { margin-right: 4px; } -.header-bar-element .search-results li .description { +.header-bar-element .search-results li .result-description { overflow: hidden; text-overflow: ellipsis; max-height: 24px; @@ -161,6 +161,11 @@ nav.navbar-default .navbar-nav>li>a.active { display: inline-block; color: #aaa; vertical-align: middle; + margin-top: 2px; +} + +.header-bar-element .search-results li .description img { + display: none; } .header-bar-element .search-results li .score:before { diff --git a/static/directives/new-header-bar.html b/static/directives/new-header-bar.html index 5a6deb223..36be8e58e 100644 --- a/static/directives/new-header-bar.html +++ b/static/directives/new-header-bar.html @@ -185,7 +185,7 @@ {{ result.namespace.name }}/{{ result.name }} -
+
From 1380e526ffa8979620e7df4edc71d99b5a15d090 Mon Sep 17 00:00:00 2001 From: Joseph Schorr Date: Mon, 27 Apr 2015 14:34:11 -0400 Subject: [PATCH 19/38] Add padding and properly gray out avatars in the namespace selector --- .../css/directives/ui/namespace-selector.css | 35 +++++++++++++++++++ static/css/quay.css | 32 ----------------- 2 files changed, 35 insertions(+), 32 deletions(-) create mode 100644 static/css/directives/ui/namespace-selector.css diff --git a/static/css/directives/ui/namespace-selector.css b/static/css/directives/ui/namespace-selector.css new file mode 100644 index 000000000..21053e9c9 --- /dev/null +++ b/static/css/directives/ui/namespace-selector.css @@ -0,0 +1,35 @@ +.namespace-selector-dropdown .namespace { + padding: 6px; + padding-left: 10px; + cursor: pointer; + font-size: 14px; + color: black; +} + +.namespace-selector-dropdown .namespace-item { + position: relative; +} + +.namespace-selector-dropdown .namespace-item .fa { + position: absolute; + right: 12px; + top: 12px; + color: #aaa; +} + +.namespace-selector-dropdown .avatar { + margin-right: 4px; +} + +.namespace-selector-dropdown a.namespace { + color: black !important; +} + +.namespace-selector-dropdown .namespace-item.disabled .avatar { + -webkit-filter: grayscale(1); + opacity: 0.5; +} + +.namespace-selector-dropdown .namespace-item .tooltip-inner { + min-width: 200px; +} diff --git a/static/css/quay.css b/static/css/quay.css index 35bb92671..78f91b6b0 100644 --- a/static/css/quay.css +++ b/static/css/quay.css @@ -548,38 +548,6 @@ i.toggle-icon:hover { float: right; } -.namespace-selector-dropdown .namespace { - padding: 6px; - padding-left: 10px; - cursor: pointer; - font-size: 14px; - color: black; -} - -.namespace-selector-dropdown .namespace-item { - position: relative; -} - -.namespace-selector-dropdown .namespace-item .fa { - position: absolute; - right: 12px; - top: 12px; - color: #aaa; -} - -.namespace-selector-dropdown a.namespace { - color: black !important; -} - -.namespace-selector-dropdown .namespace-item.disabled img { - -webkit-filter: grayscale(1); - opacity: 0.5; -} - -.namespace-selector-dropdown .namespace-item .tooltip-inner { - min-width: 200px; -} - .notification-primary { background: #428bca; color: white; From 5a05a760733d9445d194390ac32821720ab664a5 Mon Sep 17 00:00:00 2001 From: Joseph Schorr Date: Mon, 27 Apr 2015 14:36:43 -0400 Subject: [PATCH 20/38] Make the warning about billing even more clear in the new repo page by bolding the namespace --- static/partials/new-repo.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/static/partials/new-repo.html b/static/partials/new-repo.html index c2a01ce34..6dd150bbb 100644 --- a/static/partials/new-repo.html +++ b/static/partials/new-repo.html @@ -90,9 +90,9 @@
- In order to make this repository private - under your personal namespace - under the organization {{ repo.namespace }}, you will need to upgrade your plan to + In order to make this repository private under + your personal namespace + organization {{ repo.namespace }}, you will need to upgrade your plan to {{ planRequired.title }} From a507d7d5c6a81f265d3d0a998ef56216c7d90d5a Mon Sep 17 00:00:00 2001 From: Joseph Schorr Date: Mon, 27 Apr 2015 14:38:12 -0400 Subject: [PATCH 21/38] Fix over usage notification to point to the new billing tabs --- static/js/services/notification-service.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/static/js/services/notification-service.js b/static/js/services/notification-service.js index 235083baa..ade256a63 100644 --- a/static/js/services/notification-service.js +++ b/static/js/services/notification-service.js @@ -56,6 +56,16 @@ function($rootScope, $interval, UserService, ApiService, StringBuilderService, P '

Please upgrade your plan to avoid disruptions in service.', 'page': function(metadata) { var organization = UserService.getOrganization(metadata['namespace']); + + // TODO(jschorr): Remove once the new layout is in prod. + if (Config.isNewLayout()) { + if (organization) { + return '/organization/' + metadata['namespace'] + '?tab=billing'; + } else { + return '/user/' + metadata['namespace'] + '?tab=billing'; + } + } + if (organization) { return '/organization/' + metadata['namespace'] + '/admin'; } else { From 35fce6b897cd8e68917344a55c24c736fa8fd264 Mon Sep 17 00:00:00 2001 From: Joseph Schorr Date: Mon, 27 Apr 2015 14:39:16 -0400 Subject: [PATCH 22/38] Switch to the cor loader in the new repo page --- static/partials/new-repo.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/static/partials/new-repo.html b/static/partials/new-repo.html index 6dd150bbb..5f3a7d57a 100644 --- a/static/partials/new-repo.html +++ b/static/partials/new-repo.html @@ -102,10 +102,10 @@ Upgrade now or did you mean to create this repository under {{ user.organizations[0].name }}? -
+
-
+
From 1979f8738493681331a6bacae8c370e7cbc93f9d Mon Sep 17 00:00:00 2001 From: Joseph Schorr Date: Mon, 27 Apr 2015 14:40:41 -0400 Subject: [PATCH 23/38] Switch spinners to loaders in the billing manager --- static/directives/plan-manager.html | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/static/directives/plan-manager.html b/static/directives/plan-manager.html index 18d87b55a..76038668f 100644 --- a/static/directives/plan-manager.html +++ b/static/directives/plan-manager.html @@ -1,6 +1,6 @@
-
+
@@ -58,7 +58,7 @@
@@ -66,14 +66,14 @@
From be9906167af6c8adff942db0f5dcee5a2f6de68a Mon Sep 17 00:00:00 2001 From: Joseph Schorr Date: Mon, 27 Apr 2015 14:43:55 -0400 Subject: [PATCH 24/38] Make repo pull and push counts on the info page be abbreviated --- .../directives/repo-view/repo-panel-info.html | 8 +++--- static/js/directives/filters/abbreviated.js | 26 +++++++++++++++++++ 2 files changed, 30 insertions(+), 4 deletions(-) create mode 100644 static/js/directives/filters/abbreviated.js diff --git a/static/directives/repo-view/repo-panel-info.html b/static/directives/repo-view/repo-panel-info.html index 3cef46919..5fdab5a4c 100644 --- a/static/directives/repo-view/repo-panel-info.html +++ b/static/directives/repo-view/repo-panel-info.html @@ -6,12 +6,12 @@
Repo Pulls
-
{{ repository.stats.pulls.today }}
+
{{ repository.stats.pulls.today | abbreviated }}
Last 24 hours
-
{{ repository.stats.pulls.thirty_day }}
+
{{ repository.stats.pulls.thirty_day | abbreviated }}
Last 30 days
@@ -21,12 +21,12 @@
Repo Pushes
-
{{ repository.stats.pushes.today }}
+
{{ repository.stats.pushes.today | abbreviated }}
Last 24 hours
-
{{ repository.stats.pushes.thirty_day }}
+
{{ repository.stats.pushes.thirty_day | abbreviated }}
Last 30 days
diff --git a/static/js/directives/filters/abbreviated.js b/static/js/directives/filters/abbreviated.js new file mode 100644 index 000000000..bf95c2491 --- /dev/null +++ b/static/js/directives/filters/abbreviated.js @@ -0,0 +1,26 @@ +/** + * Filter which displays numbers with suffixes. + * + * Based on: https://gist.github.com/pedrorocha-net/9aa21d5f34d9cc15d18f + */ +angular.module('quay').filter('abbreviated', function() { + return function(number) { + if (number >= 10000000) { + return (number / 1000000).toFixed(0) + 'M' + } + + if (number >= 1000000) { + return (number / 1000000).toFixed(1) + 'M' + } + + if (number >= 10000) { + return (number / 1000).toFixed(0) + 'K' + } + + if (number >= 1000) { + return (number / 1000).toFixed(1) + 'K' + } + + return number + } +}); From 5741656411202182912cc63d3b91b448744d07ac Mon Sep 17 00:00:00 2001 From: Joseph Schorr Date: Mon, 27 Apr 2015 17:36:31 -0400 Subject: [PATCH 25/38] Get a nice dropdown of tags working on the visualize tags tab --- .../repo-view/repo-panel-changes.css | 6 + .../directives/ui/multiselect-dropdown.css | 42 +++ static/directives/multiselect-dropdown.html | 31 ++ .../repo-view/repo-panel-changes.html | 19 +- static/js/directives/ng-transcope.js | 25 ++ .../repo-view/repo-panel-changes.js | 24 +- .../js/directives/ui/multiselect-dropdown.js | 35 +++ static/lib/dropdowns-enhancement.js | 267 ++++++++++++++++++ 8 files changed, 431 insertions(+), 18 deletions(-) create mode 100644 static/css/directives/ui/multiselect-dropdown.css create mode 100644 static/directives/multiselect-dropdown.html create mode 100644 static/js/directives/ng-transcope.js create mode 100644 static/js/directives/ui/multiselect-dropdown.js create mode 100644 static/lib/dropdowns-enhancement.js diff --git a/static/css/directives/repo-view/repo-panel-changes.css b/static/css/directives/repo-view/repo-panel-changes.css index 14838ee1d..8c4310a47 100644 --- a/static/css/directives/repo-view/repo-panel-changes.css +++ b/static/css/directives/repo-view/repo-panel-changes.css @@ -26,3 +26,9 @@ margin-right: 8px; vertical-align: middle; } + +.repo-panel-changes .multiselect-dropdown { + display: inline-block; + margin-left: 10px; + min-width: 200px; +} \ No newline at end of file diff --git a/static/css/directives/ui/multiselect-dropdown.css b/static/css/directives/ui/multiselect-dropdown.css new file mode 100644 index 000000000..76f29f3dd --- /dev/null +++ b/static/css/directives/ui/multiselect-dropdown.css @@ -0,0 +1,42 @@ +.multiselect-dropdown .dropdown, +.multiselect-dropdown .dropdown .btn-dropdown, +.multiselect-dropdown .dropdown .dropdown-menu { + width: 100%; +} + +.multiselect-dropdown .dropdown .btn-dropdown { + text-align: left; + position: relative; + padding-right: 16px; +} + +.multiselect-dropdown .dropdown .btn-dropdown .caret { + position: absolute; + top: 14px; + right: 10px; +} + +.multiselect-dropdown .none { + color: #ccc; + margin-right: 10px; +} + +.multiselect-dropdown .dropdown-menu { + padding: 10px; +} + +.multiselect-dropdown .dropdown-menu .menu-item { + padding: 4px; +} + +.multiselect-dropdown .dropdown-menu .menu-item .co-checkable-item { + margin-right: 6px; +} + +.multiselect-dropdown .dropdown-menu .menu-item .menu-item-template { + vertical-align: middle; +} + +.multiselect-dropdown .selected-item-template { + margin-right: 10px; +} diff --git a/static/directives/multiselect-dropdown.html b/static/directives/multiselect-dropdown.html new file mode 100644 index 000000000..11acda579 --- /dev/null +++ b/static/directives/multiselect-dropdown.html @@ -0,0 +1,31 @@ +
+ +
\ No newline at end of file diff --git a/static/directives/repo-view/repo-panel-changes.html b/static/directives/repo-view/repo-panel-changes.html index 281fff6bb..094df41b0 100644 --- a/static/directives/repo-view/repo-panel-changes.html +++ b/static/directives/repo-view/repo-panel-changes.html @@ -1,23 +1,24 @@
+

+ Visualize Tags: + + {{ item }} + +

+
No tags selected to view
- Please select one or more tags in the Tags tab to visualize. + Please select one or more tags above.
-
-

- Visualize Tags: - - {{ tag }} - -

- +
diff --git a/static/js/directives/ng-transcope.js b/static/js/directives/ng-transcope.js new file mode 100644 index 000000000..795256e8b --- /dev/null +++ b/static/js/directives/ng-transcope.js @@ -0,0 +1,25 @@ +/** + * Directive to transclude a template under an ng-repeat. From: http://stackoverflow.com/a/24512435 + */ +angular.module('quay').directive('ngTranscope', function() { + return { + link: function( $scope, $element, $attrs, controller, $transclude ) { + if ( !$transclude ) { + throw minErr( 'ngTranscope' )( 'orphan', + 'Illegal use of ngTransclude directive in the template! ' + + 'No parent directive that requires a transclusion found. ' + + 'Element: {0}', + startingTag( $element )); + } + var innerScope = $scope.$new(); + + $transclude( innerScope, function( clone ) { + $element.empty(); + $element.append( clone ); + $element.on( '$destroy', function() { + innerScope.$destroy(); + }); + }); + } + }; +}); diff --git a/static/js/directives/repo-view/repo-panel-changes.js b/static/js/directives/repo-view/repo-panel-changes.js index 7b5debe37..585d03c51 100644 --- a/static/js/directives/repo-view/repo-panel-changes.js +++ b/static/js/directives/repo-view/repo-panel-changes.js @@ -96,20 +96,24 @@ angular.module('quay').directive('repoPanelChanges', function () { 'isEnabled': '=isEnabled' }, controller: function($scope, $element, $timeout, ApiService, UtilService, ImageMetadataService) { + $scope.tagNames = []; var update = function() { - if (!$scope.repository || !$scope.selectedTags) { return; } + if (!$scope.repository || !$scope.isEnabled) { return; } + $scope.tagNames = Object.keys($scope.repository.tags); $scope.currentImage = null; $scope.currentTag = null; - if (!$scope.tracker) { + if ($scope.tracker) { + refreshTree(); + } else { updateImages(); } }; var updateImages = function() { - if (!$scope.repository || !$scope.images) { return; } + if (!$scope.repository || !$scope.images || !$scope.isEnabled) { return; } $scope.tracker = new RepositoryImageTracker($scope.repository, $scope.images); @@ -120,16 +124,17 @@ angular.module('quay').directive('repoPanelChanges', function () { $scope.$watch('selectedTags', update) $scope.$watch('repository', update); + $scope.$watch('isEnabled', update); + $scope.$watch('images', updateImages); - $scope.$watch('isEnabled', function(isEnabled) { - if (isEnabled) { - refreshTree(); - } - }); + $scope.updateState = function() { + update(); + }; var refreshTree = function() { - if (!$scope.repository || !$scope.images) { return; } + if (!$scope.repository || !$scope.images || !$scope.isEnabled) { return; } + if ($scope.selectedTags.length < 1) { return; } $('#image-history-container').empty(); @@ -149,6 +154,7 @@ angular.module('quay').directive('repoPanelChanges', function () { // Give enough time for the UI to be drawn before we resize the tree. $timeout(function() { $scope.tree.notifyResized(); + $scope.setTag($scope.selectedTags[0]); }, 100); // Listen for changes to the selected tag and image in the tree. diff --git a/static/js/directives/ui/multiselect-dropdown.js b/static/js/directives/ui/multiselect-dropdown.js new file mode 100644 index 000000000..d87629f8f --- /dev/null +++ b/static/js/directives/ui/multiselect-dropdown.js @@ -0,0 +1,35 @@ +/** + * An element which displays a dropdown for selecting multiple elements. + */ +angular.module('quay').directive('multiselectDropdown', function ($compile) { + var directiveDefinitionObject = { + priority: 0, + templateUrl: '/static/directives/multiselect-dropdown.html', + transclude: true, + replace: false, + restrict: 'C', + scope: { + 'items': '=items', + 'selectedItems': '=selectedItems', + 'itemName': '@itemName', + 'itemChecked': '&itemChecked' + }, + controller: function($scope, $element) { + $scope.isChecked = function(checked, item) { + return checked.indexOf(item) >= 0; + }; + + $scope.toggleItem = function(item) { + var isChecked = $scope.isChecked($scope.selectedItems, item); + if (!isChecked) { + $scope.selectedItems.push(item); + } else { + var index = $scope.selectedItems.indexOf(item); + $scope.selectedItems.splice(index, 1); + } + $scope.itemChecked({'item': item, 'checked': !isChecked}); + }; + } + }; + return directiveDefinitionObject; +}); \ No newline at end of file diff --git a/static/lib/dropdowns-enhancement.js b/static/lib/dropdowns-enhancement.js new file mode 100644 index 000000000..3b885dc7d --- /dev/null +++ b/static/lib/dropdowns-enhancement.js @@ -0,0 +1,267 @@ +/* ======================================================================== + * Bootstrap Dropdowns Enhancement: dropdowns-enhancement.js v3.1.1 (Beta 1) + * http://behigh.github.io/bootstrap_dropdowns_enhancement/ + * ======================================================================== + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + +(function($) { + "use strict"; + + var toggle = '[data-toggle="dropdown"]', + disabled = '.disabled, :disabled', + backdrop = '.dropdown-backdrop', + menuClass = 'dropdown-menu', + subMenuClass = 'dropdown-submenu', + namespace = '.bs.dropdown.data-api', + eventNamespace = '.bs.dropdown', + openClass = 'open', + touchSupport = 'ontouchstart' in document.documentElement, + opened; + + + function Dropdown(element) { + $(element).on('click' + eventNamespace, this.toggle) + } + + var proto = Dropdown.prototype; + + proto.toggle = function(event) { + var $element = $(this); + + if ($element.is(disabled)) return; + + var $parent = getParent($element); + var isActive = $parent.hasClass(openClass); + var isSubMenu = $parent.hasClass(subMenuClass); + var menuTree = isSubMenu ? getSubMenuParents($parent) : null; + + closeOpened(event, menuTree); + + if (!isActive) { + if (!menuTree) + menuTree = [$parent]; + + if (touchSupport && !$parent.closest('.navbar-nav').length && !menuTree[0].find(backdrop).length) { + // if mobile we use a backdrop because click events don't delegate + $('
').appendTo(menuTree[0]).on('click', closeOpened) + } + + for (var i = 0, s = menuTree.length; i < s; i++) { + if (!menuTree[i].hasClass(openClass)) { + menuTree[i].addClass(openClass); + positioning(menuTree[i].children('.' + menuClass), menuTree[i]); + } + } + opened = menuTree[0]; + } + + return false; + }; + + proto.keydown = function (e) { + if (!/(38|40|27)/.test(e.keyCode)) return; + + var $this = $(this); + + e.preventDefault(); + e.stopPropagation(); + + if ($this.is('.disabled, :disabled')) return; + + var $parent = getParent($this); + var isActive = $parent.hasClass('open'); + + if (!isActive || (isActive && e.keyCode == 27)) { + if (e.which == 27) $parent.find(toggle).trigger('focus'); + return $this.trigger('click') + } + + var desc = ' li:not(.divider):visible a'; + var desc1 = 'li:not(.divider):visible > input:not(disabled) ~ label'; + var $items = $parent.find(desc1 + ', ' + '[role="menu"]' + desc + ', [role="listbox"]' + desc); + + if (!$items.length) return; + + var index = $items.index($items.filter(':focus')); + + if (e.keyCode == 38 && index > 0) index--; // up + if (e.keyCode == 40 && index < $items.length - 1) index++; // down + if (!~index) index = 0; + + $items.eq(index).trigger('focus') + }; + + proto.change = function (e) { + + var + $parent, + $menu, + $toggle, + selector, + text = '', + $items; + + $menu = $(this).closest('.' + menuClass); + + $toggle = $menu.parent().find('[data-label-placement]'); + + if (!$toggle || !$toggle.length) { + $toggle = $menu.parent().find(toggle); + } + + if (!$toggle || !$toggle.length || $toggle.data('placeholder') === false) + return; // do nothing, no control + + ($toggle.data('placeholder') == undefined && $toggle.data('placeholder', $.trim($toggle.text()))); + text = $.data($toggle[0], 'placeholder'); + + $items = $menu.find('li > input:checked'); + + if ($items.length) { + text = []; + $items.each(function () { + var str = $(this).parent().find('label').eq(0), + label = str.find('.data-label'); + + if (label.length) { + var p = $('

'); + p.append(label.clone()); + str = p.html(); + } + else { + str = str.html(); + } + + + str && text.push($.trim(str)); + }); + + text = text.length < 4 ? text.join(', ') : text.length + ' selected'; + } + + var caret = $toggle.find('.caret'); + + $toggle.html(text || ' '); + if (caret.length) + $toggle.append(' ') && caret.appendTo($toggle); + + }; + + function positioning($menu, $control) { + if ($menu.hasClass('pull-center')) { + $menu.css('margin-right', $menu.outerWidth() / -2); + } + + if ($menu.hasClass('pull-middle')) { + $menu.css('margin-top', ($menu.outerHeight() / -2) - ($control.outerHeight() / 2)); + } + } + + function closeOpened(event, menuTree) { + if (opened) { + + if (!menuTree) { + menuTree = [opened]; + } + + var parent; + + if (opened[0] !== menuTree[0][0]) { + parent = opened; + } else { + parent = menuTree[menuTree.length - 1]; + if (parent.parent().hasClass(menuClass)) { + parent = parent.parent(); + } + } + + parent.find('.' + openClass).removeClass(openClass); + + if (parent.hasClass(openClass)) + parent.removeClass(openClass); + + if (parent === opened) { + opened = null; + $(backdrop).remove(); + } + } + } + + function getSubMenuParents($submenu) { + var result = [$submenu]; + var $parent; + while (!$parent || $parent.hasClass(subMenuClass)) { + $parent = ($parent || $submenu).parent(); + if ($parent.hasClass(menuClass)) { + $parent = $parent.parent(); + } + if ($parent.children(toggle)) { + result.unshift($parent); + } + } + return result; + } + + function getParent($this) { + var selector = $this.attr('data-target'); + + if (!selector) { + selector = $this.attr('href'); + selector = selector && /#[A-Za-z]/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, ''); //strip for ie7 + } + + var $parent = selector && $(selector); + + return $parent && $parent.length ? $parent : $this.parent() + } + + // DROPDOWN PLUGIN DEFINITION + // ========================== + + var old = $.fn.dropdown; + + $.fn.dropdown = function (option) { + return this.each(function () { + var $this = $(this); + var data = $this.data('bs.dropdown'); + + if (!data) $this.data('bs.dropdown', (data = new Dropdown(this))); + if (typeof option == 'string') data[option].call($this); + }) + }; + + $.fn.dropdown.Constructor = Dropdown; + + $.fn.dropdown.clearMenus = function(e) { + $(backdrop).remove(); + $('.' + openClass + ' ' + toggle).each(function () { + var $parent = getParent($(this)); + var relatedTarget = { relatedTarget: this }; + if (!$parent.hasClass('open')) return; + $parent.trigger(e = $.Event('hide' + eventNamespace, relatedTarget)); + if (e.isDefaultPrevented()) return; + $parent.removeClass('open').trigger('hidden' + eventNamespace, relatedTarget); + }); + return this; + }; + + + // DROPDOWN NO CONFLICT + // ==================== + + $.fn.dropdown.noConflict = function () { + $.fn.dropdown = old; + return this + }; + + + $(document).off(namespace) + .on('click' + namespace, closeOpened) + .on('click' + namespace, toggle, proto.toggle) + .on('click' + namespace, '.dropdown-menu > li > input[type="checkbox"] ~ label, .dropdown-menu > li > input[type="checkbox"], .dropdown-menu.noclose > li', function (e) { + e.stopPropagation() + }) + .on('change' + namespace, '.dropdown-menu > li > input[type="checkbox"], .dropdown-menu > li > input[type="radio"]', proto.change) + .on('keydown' + namespace, toggle + ', [role="menu"], [role="listbox"]', proto.keydown) +}(jQuery)); \ No newline at end of file From a03a327748408ee6c7003e5196ff0614cc818b00 Mon Sep 17 00:00:00 2001 From: Joseph Schorr Date: Tue, 28 Apr 2015 13:26:21 -0400 Subject: [PATCH 26/38] Fix timestamps on the build logs --- static/directives/build-logs-view.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/directives/build-logs-view.html b/static/directives/build-logs-view.html index 074ccd5b0..f99a7eb00 100644 --- a/static/directives/build-logs-view.html +++ b/static/directives/build-logs-view.html @@ -30,7 +30,7 @@
- +
From edd0ba4cdbab3c63edbfa400bb38402e64472b4e Mon Sep 17 00:00:00 2001 From: Jimmy Zelinskie Date: Wed, 29 Apr 2015 15:33:20 -0400 Subject: [PATCH 27/38] endpoints.verbs: 202 for unfinished aci sigs --- endpoints/verbs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/endpoints/verbs.py b/endpoints/verbs.py index 619da283f..88d9dc7b1 100644 --- a/endpoints/verbs.py +++ b/endpoints/verbs.py @@ -140,7 +140,7 @@ def _repo_verb_signature(namespace, repository, tag, verb, checker=None, **kwarg # Lookup the derived image storage for the verb. derived = model.find_derived_storage(repo_image.storage, verb) if derived is None or derived.uploading: - abort(404) + return make_response('', 202) # Check if we have a valid signer configured. if not signer.name: From 60036927c95f81c944b0303b4606250c35e007a4 Mon Sep 17 00:00:00 2001 From: Joseph Schorr Date: Wed, 29 Apr 2015 20:30:37 -0400 Subject: [PATCH 28/38] Really disallow usage of the same account for an org as the one being converted. Before, you could do so via email. --- endpoints/api/user.py | 12 ++++++------ test/test_api_usage.py | 10 ++++++++++ 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/endpoints/api/user.py b/endpoints/api/user.py index 93e77f47c..b03c5f87b 100644 --- a/endpoints/api/user.py +++ b/endpoints/api/user.py @@ -444,19 +444,19 @@ class ConvertToOrganization(ApiResource): user = get_authenticated_user() convert_data = request.get_json() - # Ensure that the new admin user is the not user being converted. - admin_username = convert_data['adminUser'] - if admin_username == user.username: - raise request_error(reason='invaliduser', - message='The admin user is not valid') - # Ensure that the sign in credentials work. + admin_username = convert_data['adminUser'] admin_password = convert_data['adminPassword'] (admin_user, error_message) = authentication.verify_user(admin_username, admin_password) if not admin_user: raise request_error(reason='invaliduser', message='The admin user credentials are not valid') + # Ensure that the new admin user is the not user being converted. + if admin_user.id == user.id: + raise request_error(reason='invaliduser', + message='The admin user is not valid') + # Subscribe the organization to the new plan. if features.BILLING: plan = convert_data.get('plan', 'free') diff --git a/test/test_api_usage.py b/test/test_api_usage.py index d82d344e4..6123aa1cc 100644 --- a/test/test_api_usage.py +++ b/test/test_api_usage.py @@ -307,6 +307,16 @@ class TestConvertToOrganization(ApiTestCase): self.assertEqual('The admin user is not valid', json['message']) + def test_sameadminuser_by_email(self): + self.login(READ_ACCESS_USER) + json = self.postJsonResponse(ConvertToOrganization, + data={'adminUser': 'no1@thanks.com', + 'adminPassword': 'password', + 'plan': 'free'}, + expected_code=400) + + self.assertEqual('The admin user is not valid', json['message']) + def test_invalidadminuser(self): self.login(READ_ACCESS_USER) json = self.postJsonResponse(ConvertToOrganization, From c059856597c2886e355a5d8254bf40a997069a12 Mon Sep 17 00:00:00 2001 From: Joseph Schorr Date: Thu, 30 Apr 2015 12:50:11 -0400 Subject: [PATCH 29/38] Add create repo link to the user and org view pages --- static/partials/org-view.html | 6 ++++++ static/partials/user-view.html | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/static/partials/org-view.html b/static/partials/org-view.html index cbaf77e57..5bca6aabb 100644 --- a/static/partials/org-view.html +++ b/static/partials/org-view.html @@ -8,6 +8,12 @@ {{ organization.name }} + + + + Create New Repository + +
diff --git a/static/partials/user-view.html b/static/partials/user-view.html index a4c8a56e5..29b4e9d9b 100644 --- a/static/partials/user-view.html +++ b/static/partials/user-view.html @@ -8,6 +8,12 @@ {{ viewuser.username }} + + + + Create New Repository + +
From 6673d90818202686e966d9feb323814bdc4f5f83 Mon Sep 17 00:00:00 2001 From: Joseph Schorr Date: Thu, 30 Apr 2015 13:01:40 -0400 Subject: [PATCH 30/38] Fix builds tab to reload if a new build comes in --- .../directives/repo-view/repo-panel-builds.js | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/static/js/directives/repo-view/repo-panel-builds.js b/static/js/directives/repo-view/repo-panel-builds.js index 5568676d4..f012acd27 100644 --- a/static/js/directives/repo-view/repo-panel-builds.js +++ b/static/js/directives/repo-view/repo-panel-builds.js @@ -58,14 +58,16 @@ angular.module('quay').directive('repoPanelBuilds', function () { $scope.fullBuilds = orderBy(unordered, $scope.options.predicate, $scope.options.reverse); }; - var loadBuilds = function() { + var loadBuilds = function(opt_forcerefresh) { if (!$scope.builds || !$scope.repository || !$scope.options.filter) { return; } // Note: We only refresh if the filter has changed. var filter = $scope.options.filter; - if ($scope.buildsResource && filter == $scope.currentFilter) { return; } + if ($scope.buildsResource && filter == $scope.currentFilter && !opt_forcerefresh) { + return; + } var since = null; var limit = 10; @@ -105,17 +107,30 @@ angular.module('quay').directive('repoPanelBuilds', function () { } // Replace any build records with updated records from the server. + var requireReload = false; $scope.builds.map(function(build) { + var found = false; for (var i = 0; i < $scope.allBuilds.length; ++i) { var current = $scope.allBuilds[i]; if (current.id == build.id && current.phase != build.phase) { $scope.allBuilds[i] = build; - break + found = true; + break; } } + + // If the build was not found, then a new build has started. Reload + // the builds list. + if (!found) { + requireReload = true; + } }); - updateBuilds(); + if (requireReload) { + loadBuilds(/* force refresh */true); + } else { + updateBuilds(); + } }; var loadBuildTriggers = function() { From ded28f6b305d3c5b0e70a1b49a21952855b71a43 Mon Sep 17 00:00:00 2001 From: Jimmy Zelinskie Date: Thu, 30 Apr 2015 13:03:50 -0400 Subject: [PATCH 31/38] redirect ac-discovery=1 to the index This is a temp fix because rkt doesn't follow redirects. --- endpoints/web.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/endpoints/web.py b/endpoints/web.py index 13f8cbc55..8711cbe9c 100644 --- a/endpoints/web.py +++ b/endpoints/web.py @@ -527,6 +527,9 @@ def redirect_to_repository(namespace, reponame, tag): permission = ReadRepositoryPermission(namespace, reponame) is_public = model.repository_is_public(namespace, reponame) + if request.args.get('ac-discovery', 0) == 1: + return index('') + if permission.can() or is_public: repository_name = '/'.join([namespace, reponame]) return redirect(url_for('web.repository', path=repository_name, tag=tag)) From b7317f894b5262df775c0b74bbe797d2031900d4 Mon Sep 17 00:00:00 2001 From: Joseph Schorr Date: Thu, 30 Apr 2015 15:33:19 -0400 Subject: [PATCH 32/38] UI fixes for all the new trigger stuff --- endpoints/api/build.py | 43 ++++++----- endpoints/api/trigger.py | 4 +- external_libraries.py | 2 +- .../directives/ui/setup-trigger-dialog.css | 28 ++++++++ .../css/directives/ui/source-commit-link.css | 38 +++++++++- static/css/directives/ui/step-view-step.css | 9 +++ .../directives/ui/trigger-setup-githost.css | 2 +- .../ui/triggered-build-description.css | 3 +- static/css/quay.css | 21 ------ static/directives/anchor.html | 2 +- .../repo-view/repo-panel-builds.html | 2 +- static/directives/setup-trigger-dialog.html | 11 ++- static/directives/source-commit-link.html | 11 +-- static/directives/source-ref-link.html | 4 +- static/directives/step-view-step.html | 16 ++--- static/directives/trigger-setup-githost.html | 24 +++---- .../triggered-build-description.html | 32 ++++++--- .../directives/repo-view/repo-panel-builds.js | 7 +- static/js/directives/ui/anchor.js | 1 + static/js/directives/ui/source-commit-link.js | 5 +- static/js/directives/ui/step-view.js | 72 ++++++++++--------- .../ui/triggered-build-description.js | 13 ++-- static/js/pages/build-package.js | 16 ++--- static/js/pages/repo-build.js | 7 +- static/js/services/trigger-service.js | 36 ++++++++++ 25 files changed, 260 insertions(+), 149 deletions(-) create mode 100644 static/css/directives/ui/setup-trigger-dialog.css create mode 100644 static/css/directives/ui/step-view-step.css diff --git a/endpoints/api/build.py b/endpoints/api/build.py index 0a5319a5c..1456ce8b0 100644 --- a/endpoints/api/build.py +++ b/endpoints/api/build.py @@ -14,7 +14,9 @@ from endpoints.building import start_build, PreparedBuild from endpoints.trigger import BuildTriggerHandler from data import model, database from auth.auth_context import get_authenticated_user -from auth.permissions import ModifyRepositoryPermission, AdministerOrganizationPermission +from auth.permissions import (ReadRepositoryPermission, ModifyRepositoryPermission, + AdministerRepositoryPermission, AdministerOrganizationPermission) + from data.buildlogs import BuildStatusRetrievalError from util.names import parse_robot_username @@ -33,7 +35,7 @@ def get_job_config(build_obj): try: return json.loads(build_obj.job_config) except: - return None + return {} def user_view(user): @@ -43,11 +45,12 @@ def user_view(user): 'is_robot': user.robot, } -def trigger_view(trigger, can_admin=False): +def trigger_view(trigger, can_read=False, can_admin=False): if trigger and trigger.uuid: build_trigger = BuildTriggerHandler.get_handler(trigger) return { 'service': trigger.service.name, + 'build_source': build_trigger.config.get('build_source') if can_read else None, 'config': build_trigger.config if can_admin else {}, 'id': trigger.uuid, 'connected_user': trigger.connected_user.username, @@ -58,7 +61,7 @@ def trigger_view(trigger, can_admin=False): return None -def build_status_view(build_obj, can_write=False, can_admin=False): +def build_status_view(build_obj): phase = build_obj.phase try: status = build_logs.get_status(build_obj.uuid) @@ -81,21 +84,32 @@ def build_status_view(build_obj, can_write=False, can_admin=False): if not retry: phase = database.BUILD_PHASE.ERROR - logger.debug('Can write: %s job_config: %s', can_write, build_obj.job_config) + repo_namespace = build_obj.repository.namespace_user.username + repo_name = build_obj.repository.name + + can_read = ReadRepositoryPermission(repo_namespace, repo_name).can() + can_write = ModifyRepositoryPermission(repo_namespace, repo_name).can() + can_admin = AdministerRepositoryPermission(repo_namespace, repo_name).can() + + job_config = get_job_config(build_obj) + resp = { 'id': build_obj.uuid, 'phase': phase, 'started': format_date(build_obj.started), 'display_name': build_obj.display_name, 'status': status or {}, - 'job_config': get_job_config(build_obj) if can_write else None, + 'subdirectory': job_config.get('build_subdir', ''), + 'tags': job_config.get('docker_tags', []), + 'manual_user': job_config.get('manual_user', None), 'is_writer': can_write, - 'trigger': trigger_view(build_obj.trigger, can_admin), + 'trigger': trigger_view(build_obj.trigger, can_read, can_admin), + 'trigger_metadata': job_config.get('trigger_metadata', None) if can_read else None, 'resource_key': build_obj.resource_key, 'pull_robot': user_view(build_obj.pull_robot) if build_obj.pull_robot else None, 'repository': { - 'namespace': build_obj.repository.namespace_user.username, - 'name': build_obj.repository.name + 'namespace': repo_namespace, + 'name': repo_name } } @@ -157,9 +171,8 @@ class RepositoryBuildList(RepositoryParamResource): since = datetime.datetime.utcfromtimestamp(since) builds = model.list_repository_builds(namespace, repository, limit, since=since) - can_write = ModifyRepositoryPermission(namespace, repository).can() return { - 'builds': [build_status_view(build, can_write) for build in builds] + 'builds': [build_status_view(build) for build in builds] } @require_repo_write @@ -211,7 +224,7 @@ class RepositoryBuildList(RepositoryParamResource): prepared.metadata = {} build_request = start_build(repo, prepared, pull_robot_name=pull_robot_name) - resp = build_status_view(build_request, can_write=True) + resp = build_status_view(build_request) repo_string = '%s/%s' % (namespace, repository) headers = { 'Location': api.url_for(RepositoryBuildStatus, repository=repo_string, @@ -236,8 +249,7 @@ class RepositoryBuildResource(RepositoryParamResource): except model.InvalidRepositoryBuildException: raise NotFound() - can_write = ModifyRepositoryPermission(namespace, repository).can() - return build_status_view(build, can_write) + return build_status_view(build) @require_repo_admin @nickname('cancelRepoBuild') @@ -271,8 +283,7 @@ class RepositoryBuildStatus(RepositoryParamResource): build.repository.namespace_user.username != namespace): raise NotFound() - can_write = ModifyRepositoryPermission(namespace, repository).can() - return build_status_view(build, can_write) + return build_status_view(build) @resource('/v1/repository//build//logs') diff --git a/endpoints/api/trigger.py b/endpoints/api/trigger.py index 805ea08bc..35dc6b9e9 100644 --- a/endpoints/api/trigger.py +++ b/endpoints/api/trigger.py @@ -432,7 +432,7 @@ class ActivateBuildTrigger(RepositoryParamResource): except TriggerStartException as tse: raise InvalidRequest(tse.message) - resp = build_status_view(build_request, can_write=True) + resp = build_status_view(build_request) repo_string = '%s/%s' % (namespace, repository) headers = { 'Location': api.url_for(RepositoryBuildStatus, repository=repo_string, @@ -456,7 +456,7 @@ class TriggerBuildList(RepositoryParamResource): builds = list(model.list_trigger_builds(namespace, repository, trigger_uuid, limit)) return { - 'builds': [build_status_view(build, can_write=True) for build in builds] + 'builds': [build_status_view(build) for build in builds] } diff --git a/external_libraries.py b/external_libraries.py index 7004a2cca..72da2f90e 100644 --- a/external_libraries.py +++ b/external_libraries.py @@ -18,7 +18,7 @@ EXTERNAL_JS = [ ] EXTERNAL_CSS = [ - 'netdna.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.css', + 'netdna.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.css', 'netdna.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css', 'fonts.googleapis.com/css?family=Source+Sans+Pro:400,700', 's3.amazonaws.com/cdn.core-os.net/icons/core-icons.css' diff --git a/static/css/directives/ui/setup-trigger-dialog.css b/static/css/directives/ui/setup-trigger-dialog.css new file mode 100644 index 000000000..482a687bf --- /dev/null +++ b/static/css/directives/ui/setup-trigger-dialog.css @@ -0,0 +1,28 @@ +.setup-trigger-directive-element .dockerfile-found-content { + margin-left: 32px; +} + +.setup-trigger-directive-element .dockerfile-found-content:before { + content: "\f071"; + font-family: FontAwesome; + color: rgb(255, 194, 0); + position: absolute; + top: 0px; + left: 0px; + font-size: 20px; +} + +.setup-trigger-directive-element .loading { + text-align: center; +} + +.setup-trigger-directive-element .loading .cor-loader-inline { + margin-right: 4px; +} + +.setup-trigger-directive-element .dockerfile-found { + position: relative; + margin-bottom: 16px; + padding-bottom: 16px; + border-bottom: 1px solid #eee; +} \ No newline at end of file diff --git a/static/css/directives/ui/source-commit-link.css b/static/css/directives/ui/source-commit-link.css index 489b79c9a..b16c15248 100644 --- a/static/css/directives/ui/source-commit-link.css +++ b/static/css/directives/ui/source-commit-link.css @@ -1,8 +1,42 @@ -.source-commit-link-element .fa { - margin-right: 4px; +.source-commit-link-element .commit-circle { + margin-right: 6px; display: inline-block; + padding-top: 2px; } +.source-commit-link-element .commit-circle { + display: inline-block; + border: 2px solid #999; + width: 10px; + height: 10px; + border-radius: 50%; + position: relative; +} + +.source-commit-link-element .commit-circle:before, +.source-commit-link-element .commit-circle:after { + content: ""; + display: inline-block; + border-top: 2px solid #999; + height: 10px; + width: 4px; + position: absolute; + top: 2px; +} + +.source-commit-link-element .commit-circle:before { + left: -5px; +} + +.source-commit-link-element .commit-circle:after { + left: 7px; +} + + .source-commit-link-element { white-space: nowrap; +} + +.source-commit-link-element .anchor { + vertical-align: middle; } \ No newline at end of file diff --git a/static/css/directives/ui/step-view-step.css b/static/css/directives/ui/step-view-step.css new file mode 100644 index 000000000..16f31abda --- /dev/null +++ b/static/css/directives/ui/step-view-step.css @@ -0,0 +1,9 @@ +.step-view-step-content .loading-message { + position: relative; + text-align: center; + display: block; +} + +.step-view-step-content .loading-message .cor-loader-inline { + margin-right: 6px; +} \ No newline at end of file diff --git a/static/css/directives/ui/trigger-setup-githost.css b/static/css/directives/ui/trigger-setup-githost.css index edaf63c86..b4ac0f94f 100644 --- a/static/css/directives/ui/trigger-setup-githost.css +++ b/static/css/directives/ui/trigger-setup-githost.css @@ -42,7 +42,7 @@ vertical-align: middle; } -.trigger-setup-githost-element li.repo-listing i { +.trigger-setup-githost-element li.host-repo-listing i { margin-right: 10px; margin-left: 6px; } diff --git a/static/css/directives/ui/triggered-build-description.css b/static/css/directives/ui/triggered-build-description.css index a3b0bda1a..803a58472 100644 --- a/static/css/directives/ui/triggered-build-description.css +++ b/static/css/directives/ui/triggered-build-description.css @@ -10,9 +10,8 @@ .triggered-build-description-element .commit-who img { width: 16px; height: 16px; - margin-left: 2px; + margin-left: 4px; margin-right: 2px; - border-radius: 50%; } .triggered-build-description-element .fa-github { diff --git a/static/css/quay.css b/static/css/quay.css index 02409c42a..b570ffa22 100644 --- a/static/css/quay.css +++ b/static/css/quay.css @@ -3768,27 +3768,6 @@ pre.command:before { border-bottom-left-radius: 0px; } -.setup-trigger-directive-element .dockerfile-found-content { - margin-left: 32px; -} - -.setup-trigger-directive-element .dockerfile-found-content:before { - content: "\f071"; - font-family: FontAwesome; - color: rgb(255, 194, 0); - position: absolute; - top: 0px; - left: 0px; - font-size: 20px; -} - -.setup-trigger-directive-element .dockerfile-found { - position: relative; - margin-bottom: 16px; - padding-bottom: 16px; - border-bottom: 1px solid #eee; -} - .slideinout { -webkit-transition:0.5s all; transition:0.5s linear all; diff --git a/static/directives/anchor.html b/static/directives/anchor.html index 563fbe581..258f441e8 100644 --- a/static/directives/anchor.html +++ b/static/directives/anchor.html @@ -1,4 +1,4 @@ - + diff --git a/static/directives/repo-view/repo-panel-builds.html b/static/directives/repo-view/repo-panel-builds.html index 3079d06f0..d4f2b1db7 100644 --- a/static/directives/repo-view/repo-panel-builds.html +++ b/static/directives/repo-view/repo-panel-builds.html @@ -54,7 +54,6 @@ style="min-width: 66px;"> Tags - @@ -139,6 +138,7 @@ {{ trigger.config.branchtag_regex || 'All' }} + (None) diff --git a/static/directives/setup-trigger-dialog.html b/static/directives/setup-trigger-dialog.html index 66a1c52b7..d20398ec5 100644 --- a/static/directives/setup-trigger-dialog.html +++ b/static/directives/setup-trigger-dialog.html @@ -1,5 +1,4 @@
-