From 2459b6b46728d148e8859cf46b2796bce155355f Mon Sep 17 00:00:00 2001 From: Joseph Schorr Date: Wed, 25 Mar 2015 15:31:05 -0400 Subject: [PATCH 01/18] Start on new org view --- endpoints/api/organization.py | 30 +-- static/css/pages/org-view.css | 9 + static/directives/repo-list-grid.html | 20 +- .../directives/repository-events-table.html | 2 +- static/directives/teams-manager.html | 30 +++ static/js/directives/ui/repo-list-grid.js | 4 +- static/js/directives/ui/teams-manager.js | 82 ++++++++ static/js/pages/org-admin.js | 2 +- static/js/pages/org-view.js | 88 +++++++- static/partials/old-org-view.html | 85 ++++++++ static/partials/org-view.html | 193 +++++++++++------- 11 files changed, 442 insertions(+), 103 deletions(-) create mode 100644 static/css/pages/org-view.css create mode 100644 static/directives/teams-manager.html create mode 100644 static/js/directives/ui/teams-manager.js create mode 100644 static/partials/old-org-view.html diff --git a/endpoints/api/organization.py b/endpoints/api/organization.py index 4302bd62f..115644789 100644 --- a/endpoints/api/organization.py +++ b/endpoints/api/organization.py @@ -24,16 +24,20 @@ logger = logging.getLogger(__name__) def org_view(o, teams): - admin_org = AdministerOrganizationPermission(o.username) - is_admin = admin_org.can() + is_admin = AdministerOrganizationPermission(o.username).can() + is_member = OrganizationMemberPermission(o.username).can() + view = { 'name': o.username, 'email': o.email if is_admin else '', 'avatar': avatar.compute_hash(o.email, name=o.username), - 'teams': {t.name : team_view(o.username, t) for t in teams}, - 'is_admin': is_admin + 'is_admin': is_admin, + 'is_member': is_member } + if teams is not None: + view['teams'] = {t.name : team_view(o.username, t) for t in teams} + if is_admin: view['invoice_email'] = o.invoice_email @@ -129,17 +133,17 @@ class Organization(ApiResource): @nickname('getOrganization') def get(self, orgname): """ Get the details for the specified organization """ - permission = OrganizationMemberPermission(orgname) - if permission.can(): - try: - org = model.get_organization(orgname) - except model.InvalidOrganizationException: - raise NotFound() + try: + org = model.get_organization(orgname) + except model.InvalidOrganizationException: + raise NotFound() + teams = None + if OrganizationMemberPermission(orgname).can(): teams = model.get_teams_within_org(org) - return org_view(org, teams) - raise Unauthorized() + return org_view(org, teams) + @require_scope(scopes.ORG_ADMIN) @nickname('changeOrganizationDetails') @@ -218,7 +222,7 @@ class OrgPrivateRepositories(ApiResource): @path_param('orgname', 'The name of the organization') class OrgnaizationMemberList(ApiResource): """ Resource for listing the members of an organization. """ - + @require_scope(scopes.ORG_ADMIN) @nickname('getOrganizationMembers') def get(self, orgname): diff --git a/static/css/pages/org-view.css b/static/css/pages/org-view.css new file mode 100644 index 000000000..f922343c1 --- /dev/null +++ b/static/css/pages/org-view.css @@ -0,0 +1,9 @@ +.org-view .organization-name { + vertical-align: middle; + margin-left: 6px; +} + +.org-view h3 { + margin-bottom: 20px; + margin-top: 0px; +} \ No newline at end of file diff --git a/static/directives/repo-list-grid.html b/static/directives/repo-list-grid.html index edad56285..88a58ad54 100644 --- a/static/directives/repo-list-grid.html +++ b/static/directives/repo-list-grid.html @@ -2,15 +2,17 @@
-
- - Starred -
-
- - {{ namespace.name }} - {{ namespace.name }} +
+
+ + Starred +
+
+ + {{ namespace.name }} + {{ namespace.name }} +
diff --git a/static/directives/repository-events-table.html b/static/directives/repository-events-table.html index 26eb6e951..5c3f7b87f 100644 --- a/static/directives/repository-events-table.html +++ b/static/directives/repository-events-table.html @@ -14,7 +14,7 @@ error-message="'Could not load repository events'">
-
No notification have been setup for this repository.
+
No notifications have been setup for this repository.
Click the "Create Notification" button above to add a new notification for a repository event.
diff --git a/static/directives/teams-manager.html b/static/directives/teams-manager.html new file mode 100644 index 000000000..75f3544f9 --- /dev/null +++ b/static/directives/teams-manager.html @@ -0,0 +1,30 @@ +
+ + Create Team + + +
+
+
+
+ + + {{ team.name }} + + + {{ team.name }} + +
+ +
+
+ +
+ + +
+
+
+
+
\ No newline at end of file diff --git a/static/js/directives/ui/repo-list-grid.js b/static/js/directives/ui/repo-list-grid.js index aa54bfda0..a01aec827 100644 --- a/static/js/directives/ui/repo-list-grid.js +++ b/static/js/directives/ui/repo-list-grid.js @@ -11,9 +11,9 @@ angular.module('quay').directive('repoListGrid', function () { scope: { repositoriesResource: '=repositoriesResource', starred: '=starred', - user: "=user", namespace: '=namespace', - starToggled: '&starToggled' + starToggled: '&starToggled', + hideTitle: '=hideTitle' }, controller: function($scope, $element, UserService) { $scope.isOrganization = function(namespace) { diff --git a/static/js/directives/ui/teams-manager.js b/static/js/directives/ui/teams-manager.js new file mode 100644 index 000000000..ceb9d00eb --- /dev/null +++ b/static/js/directives/ui/teams-manager.js @@ -0,0 +1,82 @@ +/** + * Element for managing the teams of an organization. + */ +angular.module('quay').directive('teamsManager', function () { + var directiveDefinitionObject = { + priority: 0, + templateUrl: '/static/directives/teams-manager.html', + replace: false, + transclude: false, + restrict: 'C', + scope: { + 'organization': '=organization' + }, + controller: function($scope, $element, ApiService, CreateService) { + $scope.TEAM_PATTERN = TEAM_PATTERN; + $scope.teamRoles = [ + { 'id': 'member', 'title': 'Member', 'kind': 'default' }, + { 'id': 'creator', 'title': 'Creator', 'kind': 'success' }, + { 'id': 'admin', 'title': 'Admin', 'kind': 'primary' } + ]; + + $scope.setRole = function(role, teamname) { + var previousRole = $scope.organization.teams[teamname].role; + $scope.organization.teams[teamname].role = role; + + var params = { + 'orgname': $scope.organization.name, + 'teamname': teamname + }; + + var data = $scope.organization.teams[teamname]; + + var errorHandler = ApiService.errorDisplay('Cannot update team', function(resp) { + $scope.organization.teams[teamname].role = previousRole; + }); + + ApiService.updateOrganizationTeam(data, params).then(function(resp) { + }, errorHandler); + }; + + $scope.createTeam = function(teamname) { + if (!teamname) { + return; + } + + if ($scope.organization.teams[teamname]) { + $('#team-' + teamname).removeClass('highlight'); + setTimeout(function() { + $('#team-' + teamname).addClass('highlight'); + }, 10); + return; + } + + var orgname = $scope.organization.name; + CreateService.createOrganizationTeam(ApiService, orgname, teamname, function(created) { + $scope.organization.teams[teamname] = created; + }); + }; + + $scope.askDeleteTeam = function(teamname) { + bootbox.confirm('Are you sure you want to delete team ' + teamname + '?', function(resp) { + if (resp) { + $scope.deleteTeam(teamname); + } + }); + }; + + $scope.deleteTeam = function(teamname) { + var params = { + 'orgname': $scope.organization.name, + 'teamname': teamname + }; + + ApiService.deleteOrganizationTeam(null, params).then(function() { + delete $scope.organization.teams[teamname]; + }, ApiService.errorDisplay('Cannot delete team')); + }; + } + }; + + return directiveDefinitionObject; +}); \ No newline at end of file diff --git a/static/js/pages/org-admin.js b/static/js/pages/org-admin.js index 194366f8a..b655e825b 100644 --- a/static/js/pages/org-admin.js +++ b/static/js/pages/org-admin.js @@ -1,6 +1,6 @@ (function() { /** - * Organization admin/settings page. + * DEPRECATED: Organization admin/settings page. */ angular.module('quayPages').config(['pages', function(pages) { pages.create('org-admin', 'org-admin.html', OrgAdminCtrl); diff --git a/static/js/pages/org-view.js b/static/js/pages/org-view.js index f1a3fa287..0f33e9d92 100644 --- a/static/js/pages/org-view.js +++ b/static/js/pages/org-view.js @@ -3,10 +3,94 @@ * Page that displays details about an organization, such as its teams. */ angular.module('quayPages').config(['pages', function(pages) { - pages.create('org-view', 'org-view.html', OrgViewCtrl); + pages.create('org-view', 'org-view.html', OrgViewCtrl, { + 'newLayout': true, + 'title': 'Organization {{ organization.name }}', + 'description': 'Organization {{ organization.name }}' + }, ['layout']) + + pages.create('org-view', 'old-org-view.html', OldOrgViewCtrl, { + }, ['old-layout']); }]); - function OrgViewCtrl($rootScope, $scope, ApiService, $routeParams, CreateService) { + function OrgViewCtrl($scope, $routeParams, $timeout, ApiService, UIService) { + var orgname = $routeParams.orgname; + + $scope.showLogsCounter = 0; + $scope.showApplicationsCounter = 0; + $scope.showInvoicesCounter = 0; + $scope.changingOrganization = false; + + $scope.$watch('organizationEmail', function(e) { + UIService.hidePopover('#changeEmailForm'); + }); + + var loadRepositories = function() { + var options = { + 'public': false, + 'private': true, + 'sort': true, + 'namespace': orgname, + }; + + $scope.repositoriesResource = ApiService.listReposAsResource().withOptions(options).get(function(resp) { + return resp.repositories; + }); + }; + + var loadOrganization = function() { + $scope.orgResource = ApiService.getOrganizationAsResource({'orgname': orgname}).get(function(org) { + $scope.organization = org; + $scope.organizationEmail = org.email; + $scope.isAdmin = org.is_admin; + $scope.isMember = org.is_member; + + // Load the repositories. + $timeout(function() { + loadRepositories(); + }, 10); + }); + }; + + // Load the organization. + loadOrganization(); + + $scope.showInvoices = function() { + $scope.showInvoicesCounter++; + }; + + $scope.showApplications = function() { + $scope.showApplicationsCounter++; + }; + + $scope.showLogs = function() { + $scope.showLogsCounter++; + }; + + $scope.changeEmail = function() { + UIService.hidePopover('#changeEmailForm'); + + $scope.changingOrganization = true; + var params = { + 'orgname': orgname + }; + + var data = { + 'email': $scope.organizationEmail + }; + + ApiService.changeOrganizationDetails(data, params).then(function(org) { + $scope.changingOrganization = false; + $scope.changeEmailForm.$setPristine(); + $scope.organization = org; + }, function(result) { + $scope.changingOrganization = false; + UIService.showFormError('#changeEmailForm', result); + }); + }; + } + + function OldOrgViewCtrl($rootScope, $scope, ApiService, $routeParams, CreateService) { var orgname = $routeParams.orgname; $scope.TEAM_PATTERN = TEAM_PATTERN; diff --git a/static/partials/old-org-view.html b/static/partials/old-org-view.html new file mode 100644 index 000000000..c5aae8994 --- /dev/null +++ b/static/partials/old-org-view.html @@ -0,0 +1,85 @@ +
+
+
+
+ + + Create Team + + + Settings +
+
+ + + +
+
+
+
+ + + {{ team.name }} + + + {{ team.name }} + +
+ +
+
+ +
+ + +
+
+
+
+
+ + + + + + + diff --git a/static/partials/org-view.html b/static/partials/org-view.html index c5aae8994..f28476c56 100644 --- a/static/partials/org-view.html +++ b/static/partials/org-view.html @@ -1,85 +1,128 @@ -
-
-
-
+
+
+
+ + + + {{ organization.name }} + +
- - Create Team +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
- Settings -
-
+
+ +
+

Repositories

+
+
+
- + +
+

Teams

+
+
-
-
-
-
- - - {{ team.name }} - - - {{ team.name }} - + +
+

Robot Accounts

+
+
+ + +
+

Default Permissions

+
+
+ + +
+
+
+ + +
+

Applications

+
+
+ + +
+

Plan Usage and Billing

+
+
+ + +
+

Billing Invoices

+
+
+ + +
+

Organization Settings

+ + +
+
Organization's e-mail address
+
+
+ + + +
+
-
- -
- - -
-
+
-
- - - - - - - +
\ No newline at end of file From 6d0855f4fc74563a140e1711dbb910f9c4d505e8 Mon Sep 17 00:00:00 2001 From: Joseph Schorr Date: Wed, 25 Mar 2015 16:30:13 -0400 Subject: [PATCH 02/18] Small UI improvements to the teams tab --- static/css/directives/ui/teams-manager.css | 3 +++ static/directives/popup-input-button.html | 2 +- static/directives/teams-manager.html | 3 ++- static/partials/org-view.html | 1 - 4 files changed, 6 insertions(+), 3 deletions(-) create mode 100644 static/css/directives/ui/teams-manager.css diff --git a/static/css/directives/ui/teams-manager.css b/static/css/directives/ui/teams-manager.css new file mode 100644 index 000000000..24de3e908 --- /dev/null +++ b/static/css/directives/ui/teams-manager.css @@ -0,0 +1,3 @@ +.teams-manager .popup-input-button { + float: right; +} \ No newline at end of file diff --git a/static/directives/popup-input-button.html b/static/directives/popup-input-button.html index d6acadeb2..f76de6a43 100644 --- a/static/directives/popup-input-button.html +++ b/static/directives/popup-input-button.html @@ -1,4 +1,4 @@ -
\ No newline at end of file +
diff --git a/static/directives/build-mini-status.html b/static/directives/build-mini-status.html index ec743ce94..590a857b5 100644 --- a/static/directives/build-mini-status.html +++ b/static/directives/build-mini-status.html @@ -19,4 +19,4 @@
- \ No newline at end of file + diff --git a/static/directives/cor-checkable-item.html b/static/directives/cor-checkable-item.html index f3e65e39b..4dde44d92 100644 --- a/static/directives/cor-checkable-item.html +++ b/static/directives/cor-checkable-item.html @@ -1,3 +1,3 @@ - \ No newline at end of file + diff --git a/static/directives/cor-checkable-menu-item.html b/static/directives/cor-checkable-menu-item.html index 452e37ea7..3fc5f7c25 100644 --- a/static/directives/cor-checkable-menu-item.html +++ b/static/directives/cor-checkable-menu-item.html @@ -1 +1 @@ -
  • \ No newline at end of file +
  • diff --git a/static/directives/cor-checkable-menu.html b/static/directives/cor-checkable-menu.html index 2c6fce8f4..74c626ff1 100644 --- a/static/directives/cor-checkable-menu.html +++ b/static/directives/cor-checkable-menu.html @@ -9,4 +9,4 @@ - \ No newline at end of file + diff --git a/static/directives/cor-confirm-dialog.html b/static/directives/cor-confirm-dialog.html index 330729390..c6aa9d2fd 100644 --- a/static/directives/cor-confirm-dialog.html +++ b/static/directives/cor-confirm-dialog.html @@ -22,4 +22,4 @@
    - \ No newline at end of file + diff --git a/static/directives/cor-floating-bottom-bar.html b/static/directives/cor-floating-bottom-bar.html index 2e5337fd2..11615e6a8 100644 --- a/static/directives/cor-floating-bottom-bar.html +++ b/static/directives/cor-floating-bottom-bar.html @@ -1,3 +1,3 @@
    -
    \ No newline at end of file + diff --git a/static/directives/cor-loader-inline.html b/static/directives/cor-loader-inline.html index 39ffb5b99..3a2c42c1d 100644 --- a/static/directives/cor-loader-inline.html +++ b/static/directives/cor-loader-inline.html @@ -2,4 +2,4 @@
    - \ No newline at end of file + diff --git a/static/directives/cor-loader.html b/static/directives/cor-loader.html index 112680a22..f0aab7afc 100644 --- a/static/directives/cor-loader.html +++ b/static/directives/cor-loader.html @@ -2,4 +2,4 @@
    - \ No newline at end of file + diff --git a/static/directives/cor-log-box.html b/static/directives/cor-log-box.html index c5442d0f7..6d3157db3 100644 --- a/static/directives/cor-log-box.html +++ b/static/directives/cor-log-box.html @@ -8,4 +8,4 @@
    New Logs
    - \ No newline at end of file + diff --git a/static/directives/cor-option.html b/static/directives/cor-option.html index 0eb57170b..727e3dda3 100644 --- a/static/directives/cor-option.html +++ b/static/directives/cor-option.html @@ -1,3 +1,3 @@
  • -
  • \ No newline at end of file + diff --git a/static/directives/cor-options-menu.html b/static/directives/cor-options-menu.html index 8b6cf1e26..7e5f43cc3 100644 --- a/static/directives/cor-options-menu.html +++ b/static/directives/cor-options-menu.html @@ -3,4 +3,4 @@ - \ No newline at end of file + diff --git a/static/directives/cor-step-bar.html b/static/directives/cor-step-bar.html index 274a2c924..117f8185d 100644 --- a/static/directives/cor-step-bar.html +++ b/static/directives/cor-step-bar.html @@ -1,3 +1,3 @@
    -
    \ No newline at end of file + diff --git a/static/directives/cor-step.html b/static/directives/cor-step.html index 5339db30e..acc9baee4 100644 --- a/static/directives/cor-step.html +++ b/static/directives/cor-step.html @@ -3,4 +3,4 @@ {{ text }} - \ No newline at end of file + diff --git a/static/directives/cor-tab-content.html b/static/directives/cor-tab-content.html index 997ae5af1..747ccb2c8 100644 --- a/static/directives/cor-tab-content.html +++ b/static/directives/cor-tab-content.html @@ -1 +1 @@ -
    \ No newline at end of file +
    diff --git a/static/directives/cor-tab-panel.html b/static/directives/cor-tab-panel.html index f92d683ab..d041c9466 100644 --- a/static/directives/cor-tab-panel.html +++ b/static/directives/cor-tab-panel.html @@ -1,3 +1,3 @@
    -
    \ No newline at end of file + diff --git a/static/directives/cor-tab.html b/static/directives/cor-tab.html index 61c8b327f..07d4e0e92 100644 --- a/static/directives/cor-tab.html +++ b/static/directives/cor-tab.html @@ -10,4 +10,4 @@ - \ No newline at end of file + diff --git a/static/directives/cor-tabs.html b/static/directives/cor-tabs.html index 1a965932e..5ab85ecb1 100644 --- a/static/directives/cor-tabs.html +++ b/static/directives/cor-tabs.html @@ -1 +1 @@ - \ No newline at end of file + diff --git a/static/directives/cor-title-action.html b/static/directives/cor-title-action.html index f06f9b78d..807fe1bab 100644 --- a/static/directives/cor-title-action.html +++ b/static/directives/cor-title-action.html @@ -1,3 +1,3 @@
    -
    \ No newline at end of file + diff --git a/static/directives/cor-title-content.html b/static/directives/cor-title-content.html index 6acbe47b3..5b2077d08 100644 --- a/static/directives/cor-title-content.html +++ b/static/directives/cor-title-content.html @@ -1,3 +1,3 @@

    -
    \ No newline at end of file + diff --git a/static/directives/cor-title-link.html b/static/directives/cor-title-link.html index f400b8741..428671f86 100644 --- a/static/directives/cor-title-link.html +++ b/static/directives/cor-title-link.html @@ -1 +1 @@ - \ No newline at end of file + diff --git a/static/directives/entity-reference.html b/static/directives/entity-reference.html index 0252515dc..2229bcb0c 100644 --- a/static/directives/entity-reference.html +++ b/static/directives/entity-reference.html @@ -1,38 +1,3 @@ - - - - {{entity.name}} - {{entity.name}} - - - - - - {{entity.name}} - {{entity.name}} - - - - - - - - - - - - {{ getPrefix(entity.name) }}+{{ getShortenedName(entity.name) }} - - - {{ getPrefix(entity.name) }}+{{ getShortenedName(entity.name) }} - - - - {{getShortenedName(entity.name)}} - - - - + diff --git a/static/directives/entity-search.html b/static/directives/entity-search.html index 80114df88..ae181735d 100644 --- a/static/directives/entity-search.html +++ b/static/directives/entity-search.html @@ -6,12 +6,26 @@ diff --git a/static/directives/fetch-tag-dialog.html b/static/directives/fetch-tag-dialog.html index befe6cedc..54480562c 100644 --- a/static/directives/fetch-tag-dialog.html +++ b/static/directives/fetch-tag-dialog.html @@ -67,4 +67,4 @@ - \ No newline at end of file + diff --git a/static/directives/filter-control.html b/static/directives/filter-control.html index 9f0a5e140..c8afc88fe 100644 --- a/static/directives/filter-control.html +++ b/static/directives/filter-control.html @@ -1,3 +1,3 @@ - \ No newline at end of file + diff --git a/static/directives/header-bar.html b/static/directives/header-bar.html index bac17c19c..3fb263d4b 100644 --- a/static/directives/header-bar.html +++ b/static/directives/header-bar.html @@ -23,7 +23,7 @@