From 2459b6b46728d148e8859cf46b2796bce155355f Mon Sep 17 00:00:00 2001 From: Joseph Schorr Date: Wed, 25 Mar 2015 15:31:05 -0400 Subject: [PATCH] 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