diff --git a/endpoints/api.py b/endpoints/api.py index ed85e6e35..2a18edf4c 100644 --- a/endpoints/api.py +++ b/endpoints/api.py @@ -272,9 +272,10 @@ def get_matching_users(prefix): def get_matching_entities(prefix): teams = [] - namespace_name = request.args.get('namespace', None) + namespace_name = request.args.get('namespace', '') robot_namespace = None organization = None + try: organization = model.get_organization(namespace_name) @@ -308,7 +309,7 @@ def get_matching_entities(prefix): 'is_robot': user.is_robot, } - if user.is_org_member is not None: + if organization is not None: user_json['is_org_member'] = user.is_robot or user.is_org_member return user_json diff --git a/static/css/quay.css b/static/css/quay.css index e005172c6..f9dbb870f 100644 --- a/static/css/quay.css +++ b/static/css/quay.css @@ -963,12 +963,20 @@ form input.ng-valid.ng-dirty, .entity-mini-listing { margin: 2px; white-space: nowrap !important; + position: relative; } .entity-mini-listing i { margin-right: 8px; } +.entity-mini-listing i.fa-exclamation-triangle { + position: absolute; + right: -16px; + top: 4px; + color: #c09853; +} + .entity-mini-listing .warning { margin-top: 6px; font-size: 10px; diff --git a/static/directives/billing-options.html b/static/directives/billing-options.html index 598586bcb..3b43e6805 100644 --- a/static/directives/billing-options.html +++ b/static/directives/billing-options.html @@ -5,7 +5,7 @@ Credit Card
- +
@@ -24,7 +24,7 @@
Billing Options - +
diff --git a/static/directives/entity-search.html b/static/directives/entity-search.html index fb4a6cf24..68f15b1cd 100644 --- a/static/directives/entity-search.html +++ b/static/directives/entity-search.html @@ -5,7 +5,7 @@
- +
diff --git a/static/directives/plan-manager.html b/static/directives/plan-manager.html index cf7612d0d..8ee324d20 100644 --- a/static/directives/plan-manager.html +++ b/static/directives/plan-manager.html @@ -1,6 +1,6 @@
- +
@@ -45,13 +45,13 @@
diff --git a/static/directives/robots-manager.html b/static/directives/robots-manager.html index c478fbb02..68a2ed08a 100644 --- a/static/directives/robots-manager.html +++ b/static/directives/robots-manager.html @@ -1,5 +1,5 @@
- +
Robot accounts allow for delegating access in multiple repositories to role-based accounts that you manage
diff --git a/static/directives/signup-form.html b/static/directives/signup-form.html index b72c7636e..562223d96 100644 --- a/static/directives/signup-form.html +++ b/static/directives/signup-form.html @@ -19,7 +19,7 @@
- +
diff --git a/static/directives/spinner.html b/static/directives/spinner.html new file mode 100644 index 000000000..c0e0eb0e9 --- /dev/null +++ b/static/directives/spinner.html @@ -0,0 +1 @@ +
diff --git a/static/js/app.js b/static/js/app.js index 60ca1e082..11c8c3d0e 100644 --- a/static/js/app.js +++ b/static/js/app.js @@ -586,9 +586,9 @@ quayApp = angular.module('quay', ['ngRoute', 'chieffancypants.loadingBar', 'rest // WARNING WARNING WARNING $routeProvider. when('/repository/:namespace/:name', {templateUrl: '/static/partials/view-repo.html', controller: RepoCtrl, - fixFooter: true, reloadOnSearch: false}). + fixFooter: false, reloadOnSearch: false}). when('/repository/:namespace/:name/tag/:tag', {templateUrl: '/static/partials/view-repo.html', controller: RepoCtrl, - fixFooter: true}). + fixFooter: false}). when('/repository/:namespace/:name/image/:image', {templateUrl: '/static/partials/image-view.html', controller: ImageViewCtrl, reloadOnSearch: false}). when('/repository/:namespace/:name/admin', {templateUrl: '/static/partials/repo-admin.html', controller:RepoAdminCtrl, reloadOnSearch: false}). when('/repository/', {title: 'Repositories', description: 'Public and private docker repositories list', @@ -1316,6 +1316,21 @@ quayApp.directive('resourceView', function () { }); +quayApp.directive('quaySpinner', function () { + var directiveDefinitionObject = { + priority: 0, + templateUrl: '/static/directives/spinner.html', + replace: false, + transclude: true, + restrict: 'C', + scope: {}, + controller: function($scope, $element) { + } + }; + return directiveDefinitionObject; +}); + + quayApp.directive('organizationHeader', function () { var directiveDefinitionObject = { priority: 0, @@ -1579,18 +1594,18 @@ quayApp.directive('entitySearch', function () { number++; var input = $element[0].firstChild.firstChild; - var namespace = $scope.namespace || ''; $(input).typeahead({ name: 'entities' + number, remote: { url: '/api/entities/%QUERY', replace: function (url, uriEncodedQuery) { - url = url.replace('%QUERY', uriEncodedQuery); - url += '?namespace=' + encodeURIComponent(namespace); - if ($scope.includeTeams) { - url += '&includeTeams=true' - } - return url; + var namespace = $scope.namespace || ''; + url = url.replace('%QUERY', uriEncodedQuery); + url += '?namespace=' + encodeURIComponent(namespace); + if ($scope.includeTeams) { + url += '&includeTeams=true' + } + return url; }, filter: function(data) { var datums = []; @@ -1616,8 +1631,8 @@ quayApp.directive('entitySearch', function () { } template += '' + datum.value + ''; - if (datum.entity.is_org_member !== undefined && !datum.entity.is_org_member && datum.kind == 'user') { - template += '
This user is outside your organization
'; + if (datum.entity.is_org_member === false && datum.entity.kind == 'user') { + template += ''; } template += '
'; diff --git a/static/js/controllers.js b/static/js/controllers.js index bbe67a53e..adfc79962 100644 --- a/static/js/controllers.js +++ b/static/js/controllers.js @@ -539,7 +539,7 @@ function RepoAdminCtrl($scope, Restangular, ApiService, $routeParams, $rootScope newWebhook.customPOST($scope.newWebhook).then(function(resp) { $scope.webhooks.push(resp); $scope.newWebhook.url = ''; - $scope.newWebhookForm.$setPristine(); + $scope.createWebhookForm.$setPristine(); }); }; @@ -590,23 +590,19 @@ function RepoAdminCtrl($scope, Restangular, ApiService, $routeParams, $rootScope } function UserAdminCtrl($scope, $timeout, $location, Restangular, PlanService, UserService, KeyService, $routeParams) { - $scope.$watch(function () { return UserService.currentUser(); }, function (currentUser) { + if ($routeParams['migrate']) { + $('#migrateTab').tab('show') + } + + UserService.updateUserIn($scope, function(user) { $scope.askForPassword = currentUser.askForPassword; - if (!currentUser.anonymous) { - $scope.user = currentUser; - } - $scope.loading = false; - }, true); + }); $scope.readyForPlan = function() { // Show the subscribe dialog if a plan was requested. return $routeParams['plan']; }; - if ($routeParams['migrate']) { - $('#migrateTab').tab('show') - } - $scope.loading = true; $scope.updatingUser = false; $scope.changePasswordSuccess = false; @@ -685,13 +681,11 @@ function UserAdminCtrl($scope, $timeout, $location, Restangular, PlanService, Us }; } -function ImageViewCtrl($scope, $routeParams, $rootScope, Restangular) { +function ImageViewCtrl($scope, $routeParams, $rootScope, $timeout, ApiService, Restangular) { var namespace = $routeParams.namespace; var name = $routeParams.name; var imageid = $routeParams.image; - $('#copyClipboard').clipboardCopy(); - $scope.parseDate = function(dateString) { return Date.parse(dateString); }; @@ -737,61 +731,63 @@ function ImageViewCtrl($scope, $routeParams, $rootScope, Restangular) { if ($scope.tree) { return; } $scope.tree = new ImageFileChangeTree($scope.image, $scope.combinedChanges); - setTimeout(function() { + $timeout(function() { $scope.tree.draw('changes-tree-container'); }, 10); }; - // Fetch the image. - $scope.loading = true; - var imageFetch = Restangular.one('repository/' + namespace + '/' + name + '/image/' + imageid); - imageFetch.get().then(function(image) { - $scope.loading = false; - $scope.repo = { - 'name': name, - 'namespace': namespace - }; - $scope.image = image; - $rootScope.title = 'View Image - ' + image.id; - $rootScope.description = 'Viewing docker image ' + image.id + ' under repository ' + namespace + '/' + name + + var fetchImage = function() { + $scope.image = ApiService.at('repository', namespace, name, 'image', imageid).get(function(image) { + $scope.repo = { + 'name': name, + 'namespace': namespace + }; + + $rootScope.title = 'View Image - ' + image.id; + $rootScope.description = 'Viewing docker image ' + image.id + ' under repository ' + namespace + '/' + name + ': Image changes tree and list view'; - }, function() { - $rootScope.title = 'Unknown Image'; - $scope.loading = false; - }); - // Fetch the image changes. - var changesFetch = Restangular.one('repository/' + namespace + '/' + name + '/image/' + imageid + '/changes'); - changesFetch.get().then(function(changes) { - var combinedChanges = []; - var addCombinedChanges = function(c, kind) { - for (var i = 0; i < c.length; ++i) { - combinedChanges.push({ - 'kind': kind, - 'file': c[i] - }); - } - }; + // Fetch the image's changes. + fetchChanges(); - addCombinedChanges(changes.added, 'added'); - addCombinedChanges(changes.removed, 'removed'); - addCombinedChanges(changes.changed, 'changed'); + $('#copyClipboard').clipboardCopy(); - $scope.combinedChanges = combinedChanges; - $scope.imageChanges = changes; - }); + return image; + }); + }; + + var fetchChanges = function() { + var changesFetch = Restangular.one('repository/' + namespace + '/' + name + '/image/' + imageid + '/changes'); + changesFetch.get().then(function(changes) { + var combinedChanges = []; + var addCombinedChanges = function(c, kind) { + for (var i = 0; i < c.length; ++i) { + combinedChanges.push({ + 'kind': kind, + 'file': c[i] + }); + } + }; + + addCombinedChanges(changes.added, 'added'); + addCombinedChanges(changes.removed, 'removed'); + addCombinedChanges(changes.changed, 'changed'); + + $scope.combinedChanges = combinedChanges; + $scope.imageChanges = changes; + }); + }; + + // Fetch the image. + fetchImage(); } function V1Ctrl($scope, $location, UserService) { - $scope.$watch( function () { return UserService.currentUser(); }, function (currentUser) { - $scope.user = currentUser; - }, true); + UserService.updateUserIn($scope); } function NewRepoCtrl($scope, $location, $http, $timeout, UserService, Restangular, PlanService) { - $scope.$watch( function () { return UserService.currentUser(); }, function (currentUser) { - $scope.user = currentUser; - }, true); + UserService.updateUserIn($scope); $scope.repo = { 'is_public': 1, @@ -805,6 +801,94 @@ function NewRepoCtrl($scope, $location, $http, $timeout, UserService, Restangula }); }); + // Watch the namespace on the repo. If it changes, we update the plan and the public/private + // accordingly. + $scope.isUserNamespace = true; + $scope.$watch('repo.namespace', function(namespace) { + // Note: Can initially be undefined. + if (!namespace) { return; } + + var isUserNamespace = (namespace == $scope.user.username); + + $scope.planRequired = null; + $scope.isUserNamespace = isUserNamespace; + + if (isUserNamespace) { + // Load the user's subscription information in case they want to create a private + // repository. + PlanService.getSubscription(null, subscribedToPlan, function() { + PlanService.getMinimumPlan(1, false, function(minimum) { $scope.planRequired = minimum; }); + }); + } else { + $scope.planRequired = null; + + var checkPrivateAllowed = Restangular.one('organization/' + namespace + '/private'); + checkPrivateAllowed.get().then(function(resp) { + $scope.planRequired = resp.privateAllowed ? null : {}; + }, function() { + $scope.planRequired = {}; + }); + + // Auto-set to private repo. + $scope.repo.is_public = '0'; + } + }); + + $scope.createNewRepo = function() { + $('#repoName').popover('hide'); + + var uploader = $('#file-drop')[0]; + if ($scope.repo.initialize && uploader.files.length < 1) { + $('#missingfileModal').modal(); + return; + } + + $scope.creating = true; + var repo = $scope.repo; + var data = { + 'namespace': repo.namespace, + 'repository': repo.name, + 'visibility': repo.is_public == '1' ? 'public' : 'private', + 'description': repo.description + }; + + var createPost = Restangular.one('repository'); + createPost.customPOST(data).then(function(created) { + $scope.creating = false; + $scope.created = created; + + // Repository created. Start the upload process if applicable. + if ($scope.repo.initialize) { + startFileUpload(created); + return; + } + + // Otherwise, redirect to the repo page. + $location.path('/repository/' + created.namespace + '/' + created.name); + }, function(result) { + $scope.creating = false; + $scope.createError = result.data; + $timeout(function() { + $('#repoName').popover('show'); + }); + }); + }; + + $scope.upgradePlan = function() { + var callbacks = { + 'started': function() { $scope.planChanging = true; }, + 'opened': function() { $scope.planChanging = true; }, + 'closed': function() { $scope.planChanging = false; }, + 'success': subscribedToPlan, + 'failure': function(resp) { + $('#couldnotsubscribeModal').modal(); + $scope.planChanging = false; + } + }; + + PlanService.changePlan($scope, null, $scope.planRequired.stripeId, callbacks); + }; + var startBuild = function(repo, fileId) { $scope.building = true; @@ -890,120 +974,19 @@ function NewRepoCtrl($scope, $location, $http, $timeout, UserService, Restangula } }); }; - - $scope.createNewRepo = function() { - $('#repoName').popover('hide'); - - var uploader = $('#file-drop')[0]; - if ($scope.repo.initialize && uploader.files.length < 1) { - $('#missingfileModal').modal(); - return; - } - - $scope.creating = true; - var repo = $scope.repo; - var data = { - 'namespace': repo.namespace, - 'repository': repo.name, - 'visibility': repo.is_public == '1' ? 'public' : 'private', - 'description': repo.description - }; - - var createPost = Restangular.one('repository'); - createPost.customPOST(data).then(function(created) { - $scope.creating = false; - $scope.created = created; - - // Repository created. Start the upload process if applicable. - if ($scope.repo.initialize) { - startFileUpload(created); - return; - } - - // Otherwise, redirect to the repo page. - $location.path('/repository/' + created.namespace + '/' + created.name); - }, function(result) { - $scope.creating = false; - $scope.createError = result.data; - $timeout(function() { - $('#repoName').popover('show'); - }); - }); - }; - - $scope.upgradePlan = function() { - var callbacks = { - 'started': function() { $scope.planChanging = true; }, - 'opened': function() { $scope.planChanging = true; }, - 'closed': function() { $scope.planChanging = false; }, - 'success': subscribedToPlan, - 'failure': function(resp) { - $('#couldnotsubscribeModal').modal(); - $scope.planChanging = false; - } - }; - - PlanService.changePlan($scope, null, $scope.planRequired.stripeId, callbacks); - }; - - // Watch the namespace on the repo. If it changes, we update the plan and the public/private - // accordingly. - $scope.isUserNamespace = true; - $scope.$watch('repo.namespace', function(namespace) { - // Note: Can initially be undefined. - if (!namespace) { return; } - - var isUserNamespace = (namespace == $scope.user.username); - - $scope.planRequired = null; - $scope.isUserNamespace = isUserNamespace; - - if (isUserNamespace) { - // Load the user's subscription information in case they want to create a private - // repository. - PlanService.getSubscription(null, subscribedToPlan, function() { - PlanService.getMinimumPlan(1, false, function(minimum) { $scope.planRequired = minimum; }); - }); - } else { - $scope.planRequired = null; - - var checkPrivateAllowed = Restangular.one('organization/' + namespace + '/private'); - checkPrivateAllowed.get().then(function(resp) { - $scope.planRequired = resp.privateAllowed ? null : {}; - }, function() { - $scope.planRequired = {}; - }); - - // Auto-set to private repo. - $scope.repo.is_public = '0'; - } - }); } -function OrgViewCtrl($rootScope, $scope, Restangular, $routeParams) { +function OrgViewCtrl($rootScope, $scope, Restangular, ApiService, $routeParams) { + var orgname = $routeParams.orgname; + $('.info-icon').popover({ - 'trigger': 'hover', - 'html': true + 'trigger': 'hover', + 'html': true }); $scope.TEAM_PATTERN = TEAM_PATTERN; $rootScope.title = 'Loading...'; - var orgname = $routeParams.orgname; - - var loadOrganization = function() { - var getOrganization = Restangular.one(getRestUrl('organization', orgname)); - getOrganization.get().then(function(resp) { - $scope.organization = resp; - $scope.loading = false; - - $rootScope.title = orgname; - $rootScope.description = 'Viewing organization ' + orgname; - }, function() { - $scope.loading = false; - }); - }; - $scope.teamRoles = [ { 'id': 'member', 'title': 'Member', 'kind': 'default' }, { 'id': 'creator', 'title': 'Creator', 'kind': 'success' }, @@ -1063,10 +1046,21 @@ function OrgViewCtrl($rootScope, $scope, Restangular, $routeParams) { }); }; + var loadOrganization = function() { + $scope.orgResource = ApiService.at('organization', orgname).get(function(org) { + $scope.organization = org; + $rootScope.title = orgname; + $rootScope.description = 'Viewing organization ' + orgname; + }); + }; + + // Load the organization. loadOrganization(); } -function OrgAdminCtrl($rootScope, $scope, Restangular, $routeParams, UserService, PlanService) { +function OrgAdminCtrl($rootScope, $scope, Restangular, $routeParams, UserService, PlanService, ApiService) { + var orgname = $routeParams.orgname; + // Load the list of plans. PlanService.getPlans(function(plans) { $scope.plans = plans.business; @@ -1082,8 +1076,6 @@ function OrgAdminCtrl($rootScope, $scope, Restangular, $routeParams, UserService addPlans(plans.business); }); - var orgname = $routeParams.orgname; - $scope.orgname = orgname; $scope.membersLoading = true; $scope.membersFound = null; @@ -1133,35 +1125,32 @@ function OrgAdminCtrl($rootScope, $scope, Restangular, $routeParams, UserService }; var loadOrganization = function() { - var getOrganization = Restangular.one(getRestUrl('organization', orgname)); - getOrganization.get().then(function(resp) { - if (resp && resp.is_admin) { - $scope.organization = resp; + $scope.orgResource = ApiService.at('organization', orgname).get(function(org) { + if (org && org.is_admin) { + $scope.organization = org; $rootScope.title = orgname + ' (Admin)'; $rootScope.description = 'Administration page for organization ' + orgname; } - - $scope.loading = false; - }, function() { - $scope.loading = false; }); }; + // Load the organization. loadOrganization(); } -function TeamViewCtrl($rootScope, $scope, Restangular, $routeParams) { +function TeamViewCtrl($rootScope, $scope, Restangular, ApiService, $routeParams) { $('.info-icon').popover({ - 'trigger': 'hover', - 'html': true + 'trigger': 'hover', + 'html': true }); - $scope.orgname = $routeParams.orgname; var teamname = $routeParams.teamname; + var orgname = $routeParams.orgname; + + $scope.orgname = orgname; + $scope.teamname = teamname; $rootScope.title = 'Loading...'; - $scope.loading = true; - $scope.teamname = teamname; $scope.addNewMember = function(member) { if ($scope.members[member.name]) { return; } @@ -1195,57 +1184,38 @@ function TeamViewCtrl($rootScope, $scope, Restangular, $routeParams) { }; var loadOrganization = function() { - var getOrganization = Restangular.one(getRestUrl('organization', $scope.orgname)) - getOrganization.get().then(function(resp) { - $scope.organization = resp; + $scope.orgResource = ApiService.at('organization', orgname).get(function(org) { + $scope.organization = org; $scope.team = $scope.organization.teams[teamname]; - $scope.loading = !$scope.organization || !$scope.members; - }, function() { - $scope.organization = null; - $scope.members = null; - $scope.loading = false; + $rootScope.title = teamname + ' (' + $scope.orgname + ')'; + $rootScope.description = 'Team management page for team ' + teamname + ' under organization ' + $scope.orgname; + loadMembers(); + return org; }); }; var loadMembers = function() { - var getMembers = Restangular.one(getRestUrl('organization', $scope.orgname, 'team', teamname, 'members')); - getMembers.get().then(function(resp) { + $scope.membersResource = ApiService.at('organization', $scope.orgname, 'team', teamname, 'members').get(function(resp) { $scope.members = resp.members; $scope.canEditMembers = resp.can_edit; - $scope.loading = !$scope.organization || !$scope.members; - $rootScope.title = teamname + ' (' + $scope.orgname + ')'; - $rootScope.description = 'Team management page for team ' + teamname + ' under organization ' + $scope.orgname; - }, function() { - $scope.organization = null; - $scope.members = null; - $scope.loading = false; - }); + return resp.members; + }); }; + // Load the organization. loadOrganization(); - loadMembers(); } function OrgsCtrl($scope, UserService) { - $scope.loading = true; - - $scope.$watch(function () { return UserService.currentUser(); }, function (currentUser) { - $scope.user = currentUser; - $scope.loading = false; - }, true); + UserService.updateUserIn($scope); browserchrome.update(); } function NewOrgCtrl($scope, $routeParams, $timeout, $location, UserService, PlanService, Restangular) { - $scope.loading = true; - - $scope.$watch(function () { return UserService.currentUser(); }, function (currentUser) { - $scope.user = currentUser; - $scope.loading = false; - }, true); + UserService.updateUserIn($scope); - requested = $routeParams['plan']; + var requested = $routeParams['plan']; // Load the list of plans. PlanService.getPlans(function(plans) { @@ -1284,13 +1254,13 @@ function NewOrgCtrl($scope, $routeParams, $timeout, $location, UserService, Plan var createPost = Restangular.one('organization/'); createPost.customPOST(data).then(function(created) { - $scope.creating = false; $scope.created = created; // Reset the organizations list. UserService.load(); var showOrg = function() { + $scope.creating = false; $location.path('/organization/' + org.name + '/'); }; @@ -1301,6 +1271,7 @@ function NewOrgCtrl($scope, $routeParams, $timeout, $location, UserService, Plan } // Otherwise, show the subscribe for the plan. + $scope.creating = true; var callbacks = { 'opened': function() { $scope.creating = true; }, 'closed': showOrg, @@ -1308,7 +1279,7 @@ function NewOrgCtrl($scope, $routeParams, $timeout, $location, UserService, Plan 'failure': showOrg }; - PlanService.changePlan($scope, org.name, $scope.currentPlan.stripeId, false, callbacks); + PlanService.changePlan($scope, org.name, $scope.currentPlan.stripeId, callbacks); }, function(result) { $scope.creating = false; $scope.createError = result.data.message || result.data; @@ -1320,49 +1291,38 @@ function NewOrgCtrl($scope, $routeParams, $timeout, $location, UserService, Plan } -function OrgMemberLogsCtrl($scope, $routeParams, $rootScope, $timeout, Restangular) { +function OrgMemberLogsCtrl($scope, $routeParams, $rootScope, $timeout, Restangular, ApiService) { var orgname = $routeParams.orgname; var membername = $routeParams.membername; $scope.orgname = orgname; - $scope.loading = true; $scope.memberInfo = null; $scope.ready = false; - var checkReady = function() { - $scope.loading = !$scope.organization || !$scope.memberInfo; - if (!$scope.loading) { - $rootScope.title = 'Logs for ' + $scope.memberInfo.username + ' (' + $scope.orgname + ')'; - $rootScope.description = 'Shows all the actions of ' + $scope.memberInfo.username + - ' under organization ' + $scope.orgname; - $timeout(function() { - $scope.ready = true; - }); - } - }; - var loadOrganization = function() { - var getOrganization = Restangular.one(getRestUrl('organization', orgname)) - getOrganization.get().then(function(resp) { - $scope.organization = resp; - checkReady(); - }, function() { - $scope.organization = null; - $scope.loading = false; + $scope.orgResource = ApiService.at('organization', orgname).get(function(org) { + $scope.organization = org; + return org; }); }; var loadMemberInfo = function() { - var getMemberInfo = Restangular.one(getRestUrl('organization', orgname, 'members', membername)) - getMemberInfo.get().then(function(resp) { + $scope.memberResource = ApiService.at('organization', $scope.orgname, 'members', membername).get(function(resp) { $scope.memberInfo = resp.member; - checkReady(); - }, function() { - $scope.memberInfo = null; - $scope.loading = false; - }); + + $rootScope.title = 'Logs for ' + $scope.memberInfo.username + ' (' + $scope.orgname + ')'; + $rootScope.description = 'Shows all the actions of ' + $scope.memberInfo.username + + ' under organization ' + $scope.orgname; + + $timeout(function() { + $scope.ready = true; + }); + + return resp.member; + }); }; + // Load the org info and the member info. loadOrganization(); loadMemberInfo(); } \ No newline at end of file diff --git a/static/js/graphing.js b/static/js/graphing.js index d92a6bfa3..fbf5f2162 100644 --- a/static/js/graphing.js +++ b/static/js/graphing.js @@ -54,7 +54,7 @@ ImageHistoryTree.prototype.calculateDimensions_ = function(container) { var cw = Math.max(document.getElementById(container).clientWidth, this.maxWidth_ * DEPTH_WIDTH); var ch = this.maxHeight_ * (DEPTH_HEIGHT + 10); - var margin = { top: 40, right: 20, bottom: 20, left: 40 }; + var margin = { top: 40, right: 20, bottom: 20, left: 80 }; var m = [margin.top, margin.right, margin.bottom, margin.left]; var w = cw - m[1] - m[3]; var h = ch - m[0] - m[2]; @@ -87,6 +87,7 @@ ImageHistoryTree.prototype.updateDimensions_ = function() { var viewportHeight = $(window).height(); var boundingBox = document.getElementById(container).getBoundingClientRect(); document.getElementById(container).style.maxHeight = (viewportHeight - boundingBox.top - 110) + 'px'; + $('#' + container).overscroll(); // Update the tree. @@ -94,9 +95,13 @@ ImageHistoryTree.prototype.updateDimensions_ = function() { var tree = this.tree_; var vis = this.vis_; + + var ow = w + m[1] + m[3]; + var oh = h + m[0] + m[2]; rootSvg - .attr("width", w + m[1] + m[3]) - .attr("height", h + m[0] + m[2]); + .attr("width", ow) + .attr("height", oh) + .attr("style", "width: " + ow + "px; height: " + oh + "px"); tree.size([w, h]); vis.attr("transform", "translate(" + m[3] + "," + m[0] + ")"); diff --git a/static/lib/jquery.overscroll.js b/static/lib/jquery.overscroll.js new file mode 100644 index 000000000..7126a8b1e --- /dev/null +++ b/static/lib/jquery.overscroll.js @@ -0,0 +1,793 @@ +/** + * Overscroll v1.7.3 + * A jQuery Plugin that emulates the iPhone scrolling experience in a browser. + * http://azoffdesign.com/overscroll + * + * Intended for use with the latest jQuery + * http://code.jquery.com/jquery-latest.js + * + * Copyright 2013, Jonathan Azoff + * Licensed under the MIT license. + * https://github.com/azoff/overscroll/blob/master/mit.license + * + * For API documentation, see the README file + * http://azof.fr/pYCzuM + * + * Date: Tuesday, March 18th 2013 + */ +(function(global, dom, browser, math, wait, cancel, namespace, $, none){ + + // We want to run this plug-in in strict-mode + // so that we may benefit from its optimizations + 'use strict'; + + // The key used to bind-instance specific data to an object + var datakey = 'overscroll'; + + // create node if there's not one present (e.g., for test runners) + if (dom.body === null) { + dom.documentElement.appendChild( + dom.createElement('body') + ); + } + + // quick fix for IE 8 and below since getComputedStyle() is not supported + // TODO: find a better solution + if (!global.getComputedStyle) { + global.getComputedStyle = function (el, pseudo) { + this.el = el; + this.getPropertyValue = function (prop) { + var re = /(\-([a-z]){1})/g; + if (prop == 'float') prop = 'styleFloat'; + if (re.test(prop)) { + prop = prop.replace(re, function () { + return arguments[2].toUpperCase(); + }); + } + return el.currentStyle[prop] ? el.currentStyle[prop] : null; + }; + return this; + }; + } + + // runs feature detection for overscroll + var compat = { + animate: (function(){ + var fn = global.requestAnimationFrame || + global.webkitRequestAnimationFrame || + global.mozRequestAnimationFrame || + global.oRequestAnimationFrame || + global.msRequestAnimationFrame || + function(callback) { wait(callback, 1000/60); }; + return function(callback) { + fn.call(global, callback); + }; + })(), + overflowScrolling: (function(){ + var style = ''; + var div = dom.createElement('div'); + var prefixes = ['webkit', 'moz', 'o', 'ms']; + dom.body.appendChild(div); + $.each(prefixes, function(i, prefix){ + div.style[prefix + 'OverflowScrolling'] = 'touch'; + }); + div.style.overflowScrolling = 'touch'; + var computedStyle = global.getComputedStyle(div); + if (!!computedStyle.overflowScrolling) { + style = 'overflow-scrolling'; + } else { + $.each(prefixes, function(i, prefix){ + if (!!computedStyle[prefix + 'OverflowScrolling']) { + style = '-' + prefix + '-overflow-scrolling'; + } + return !style; + }); + } + div.parentNode.removeChild(div); + return style; + })(), + cursor: (function() { + var div = dom.createElement('div'); + var prefixes = ['webkit', 'moz']; + var gmail = 'https://mail.google.com/mail/images/2/'; + var style = { + grab: 'url('+gmail+'openhand.cur), move', + grabbing: 'url('+gmail+'closedhand.cur), move' + }; + dom.body.appendChild(div); + $.each(prefixes, function(i, prefix){ + var found, cursor = '-' + prefix + '-grab'; + div.style.cursor = cursor; + var computedStyle = global.getComputedStyle(div); + found = computedStyle.cursor === cursor; + if (found) { + style = { + grab: '-' + prefix + '-grab', + grabbing: '-' + prefix + '-grabbing' + }; + } + return !found; + }); + div.parentNode.removeChild(div); + return style; + })() + }; + + // These are all the events that could possibly + // be used by the plug-in + var events = { + drag: 'mousemove touchmove', + end: 'mouseup mouseleave click touchend touchcancel', + hover: 'mouseenter mouseleave', + ignored: 'select dragstart drag', + scroll: 'scroll', + start: 'mousedown touchstart', + wheel: 'mousewheel DOMMouseScroll' + }; + + // These settings are used to tweak drift settings + // for the plug-in + var settings = { + captureThreshold: 3, + driftDecay: 1.1, + driftSequences: 22, + driftTimeout: 100, + scrollDelta: 15, + thumbOpacity: 0.7, + thumbThickness: 6, + thumbTimeout: 400, + wheelDelta: 20, + wheelTicks: 120 + }; + + // These defaults are used to complement any options + // passed into the plug-in entry point + var defaults = { + cancelOn: 'select,input,textarea', + direction: 'multi', + dragHold: false, + hoverThumbs: false, + scrollDelta: settings.scrollDelta, + showThumbs: true, + persistThumbs: false, + captureWheel: true, + wheelDelta: settings.wheelDelta, + wheelDirection: 'multi', + zIndex: 999, + ignoreSizing: false + }; + + // Triggers a DOM event on the overscrolled element. + // All events are namespaced under the overscroll name + function triggerEvent(event, target) { + target.trigger('overscroll:' + event); + } + + // Utility function to return a timestamp + function time() { + return (new Date()).getTime(); + } + + // Captures the position from an event, modifies the properties + // of the second argument to persist the position, and then + // returns the modified object + function capturePosition(event, position, index) { + position.x = event.pageX; + position.y = event.pageY; + position.time = time(); + position.index = index; + return position; + } + + // Used to move the thumbs around an overscrolled element + function moveThumbs(thumbs, sizing, left, top) { + + var ml, mt; + + if (thumbs && thumbs.added) { + if (thumbs.horizontal) { + ml = left * (1 + sizing.container.width / sizing.container.scrollWidth); + mt = top + sizing.thumbs.horizontal.top; + thumbs.horizontal.css('margin', mt + 'px 0 0 ' + ml + 'px'); + } + if (thumbs.vertical) { + ml = left + sizing.thumbs.vertical.left; + mt = top * (1 + sizing.container.height / sizing.container.scrollHeight); + thumbs.vertical.css('margin', mt + 'px 0 0 ' + ml + 'px'); + } + } + + } + + // Used to toggle the thumbs on and off + // of an overscrolled element + function toggleThumbs(thumbs, options, dragging) { + if (thumbs && thumbs.added && !options.persistThumbs) { + if (dragging) { + if (thumbs.vertical) { + thumbs.vertical.stop(true, true).fadeTo('fast', settings.thumbOpacity); + } + if (thumbs.horizontal) { + thumbs.horizontal.stop(true, true).fadeTo('fast', settings.thumbOpacity); + } + } else { + if (thumbs.vertical) { + thumbs.vertical.fadeTo('fast', 0); + } + if (thumbs.horizontal) { + thumbs.horizontal.fadeTo('fast', 0); + } + } + } + } + + // Defers click event listeners to after a mouseup event. + // Used to avoid unintentional clicks + function deferClick(target) { + var clicks, key = 'events'; + var events = $._data ? $._data(target[0], key) : target.data(key); + if (events && events.click) { + clicks = events.click.slice(); + target.off('click').one('click', function(){ + $.each(clicks, function(i, click){ + target.click(click); + }); return false; + }); + } + } + + // Toggles thumbs on hover. This event is only triggered + // if the hoverThumbs option is set + function hover(event) { + var data = event.data, + thumbs = data.thumbs, + options = data.options, + dragging = event.type === 'mouseenter'; + toggleThumbs(thumbs, options, dragging); + } + + // This function is only ever used when the overscrolled element + // scrolled outside of the scope of this plugin. + function scroll(event) { + var data = event.data; + if (!data.flags.dragged) { + /*jshint validthis:true */ + moveThumbs(data.thumbs, data.sizing, this.scrollLeft, this.scrollTop); + } + } + + // handles mouse wheel scroll events + function wheel(event) { + + // prevent any default wheel behavior + event.preventDefault(); + + var data = event.data, + options = data.options, + sizing = data.sizing, + thumbs = data.thumbs, + dwheel = data.wheel, + flags = data.flags, + original = event.originalEvent, + delta = 0, deltaX = 0, deltaY = 0; + + // stop any drifts + flags.drifting = false; + + // normalize the wheel ticks + if (original.detail) { + delta = -original.detail; + if (original.detailX) { + deltaX = -original.detailX; + } + if (original.detailY) { + deltaY = -original.detailY; + } + } else if (original.wheelDelta) { + delta = original.wheelDelta / settings.wheelTicks; + if (original.wheelDeltaX) { + deltaX = original.wheelDeltaX / settings.wheelTicks; + } + if (original.wheelDeltaY) { + deltaY = original.wheelDeltaY / settings.wheelTicks; + } + } + + // apply a pixel delta to each tick + delta *= options.wheelDelta; + deltaX *= options.wheelDelta; + deltaY *= options.wheelDelta; + + // initialize flags if this is the first tick + if (!dwheel) { + data.target.data(datakey).dragging = flags.dragging = true; + data.wheel = dwheel = { timeout: null }; + toggleThumbs(thumbs, options, true); + } + + // actually modify scroll offsets + if (options.wheelDirection === 'vertical'){ + /*jshint validthis:true */ + this.scrollTop -= delta; + } else if ( options.wheelDirection === 'horizontal') { + this.scrollLeft -= delta; + } else { + this.scrollLeft -= deltaX; + this.scrollTop -= deltaY || delta; + } + + if (dwheel.timeout) { cancel(dwheel.timeout); } + + moveThumbs(thumbs, sizing, this.scrollLeft, this.scrollTop); + + dwheel.timeout = wait(function() { + data.target.data(datakey).dragging = flags.dragging = false; + toggleThumbs(thumbs, options, data.wheel = null); + }, settings.thumbTimeout); + + } + + // updates the current scroll offset during a mouse move + function drag(event) { + + event.preventDefault(); + + var data = event.data, + touches = event.originalEvent.touches, + options = data.options, + sizing = data.sizing, + thumbs = data.thumbs, + position = data.position, + flags = data.flags, + target = data.target.get(0); + + + // correct page coordinates for touch devices + if (touches && touches.length) { + event = touches[0]; + } + + if (!flags.dragged) { + toggleThumbs(thumbs, options, true); + } + + flags.dragged = true; + + if (options.direction !== 'vertical') { + target.scrollLeft -= (event.pageX - position.x); + } + + if (data.options.direction !== 'horizontal') { + target.scrollTop -= (event.pageY - position.y); + } + + capturePosition(event, data.position); + + if (--data.capture.index <= 0) { + data.target.data(datakey).dragging = flags.dragging = true; + capturePosition(event, data.capture, settings.captureThreshold); + } + + moveThumbs(thumbs, sizing, target.scrollLeft, target.scrollTop); + + } + + // sends the overscrolled element into a drift + function drift(target, event, callback) { + + var data = event.data, dx, dy, xMod, yMod, + capture = data.capture, + options = data.options, + sizing = data.sizing, + thumbs = data.thumbs, + elapsed = time() - capture.time, + scrollLeft = target.scrollLeft, + scrollTop = target.scrollTop, + decay = settings.driftDecay; + + // only drift if enough time has passed since + // the last capture event + if (elapsed > settings.driftTimeout) { + callback(data); return; + } + + // determine offset between last capture and current time + dx = options.scrollDelta * (event.pageX - capture.x); + dy = options.scrollDelta * (event.pageY - capture.y); + + // update target scroll offsets + if (options.direction !== 'vertical') { + scrollLeft -= dx; + } if (options.direction !== 'horizontal') { + scrollTop -= dy; + } + + // split the distance to travel into a set of sequences + xMod = dx / settings.driftSequences; + yMod = dy / settings.driftSequences; + + triggerEvent('driftstart', data.target); + + data.drifting = true; + + // animate the drift sequence + compat.animate(function render() { + if (data.drifting) { + var min = 1, max = -1; + data.drifting = false; + if (yMod > min && target.scrollTop > scrollTop || yMod < max && target.scrollTop < scrollTop) { + data.drifting = true; + target.scrollTop -= yMod; + yMod /= decay; + } + if (xMod > min && target.scrollLeft > scrollLeft || xMod < max && target.scrollLeft < scrollLeft) { + data.drifting = true; + target.scrollLeft -= xMod; + xMod /= decay; + } + moveThumbs(thumbs, sizing, target.scrollLeft, target.scrollTop); + compat.animate(render); + } else { + triggerEvent('driftend', data.target); + callback(data); + } + }); + + } + + // starts the drag operation and binds the mouse move handler + function start(event) { + + var data = event.data, + touches = event.originalEvent.touches, + target = data.target, + dstart = data.start = $(event.target), + flags = data.flags; + + // stop any drifts + flags.drifting = false; + + // only start drag if the user has not explictly banned it. + if (dstart.size() && !dstart.is(data.options.cancelOn)) { + + // without this the simple "click" event won't be recognized on touch clients + if (!touches) { event.preventDefault(); } + + if (!compat.overflowScrolling) { + target.css('cursor', compat.cursor.grabbing); + target.data(datakey).dragging = flags.dragging = flags.dragged = false; + + // apply the drag listeners to the doc or target + if(data.options.dragHold) { + $(document).on(events.drag, data, drag); + } else { + target.on(events.drag, data, drag); + } + } + + data.position = capturePosition(event, {}); + data.capture = capturePosition(event, {}, settings.captureThreshold); + triggerEvent('dragstart', target); + + } + + } + + // ends the drag operation and unbinds the mouse move handler + function stop(event) { + + var data = event.data, + target = data.target, + options = data.options, + flags = data.flags, + thumbs = data.thumbs, + + // hides the thumbs after the animation is done + done = function () { + if (thumbs && !options.hoverThumbs) { + toggleThumbs(thumbs, options, false); + } + }; + + // remove drag listeners from doc or target + if(options.dragHold) { + $(document).unbind(events.drag, drag); + } else { + target.unbind(events.drag, drag); + } + + // only fire events and drift if we started with a + // valid position + if (data.position) { + + triggerEvent('dragend', target); + + // only drift if a drag passed our threshold + if (flags.dragging && !compat.overflowScrolling) { + drift(target.get(0), event, done); + } else { + done(); + } + + } + + // only if we moved, and the mouse down is the same as + // the mouse up target do we defer the event + if (flags.dragging && !compat.overflowScrolling && data.start && data.start.is(event.target)) { + deferClick(data.start); + } + + // clear all internal flags and settings + target.data(datakey).dragging = + data.start = + data.capture = + data.position = + flags.dragged = + flags.dragging = false; + + // set the cursor back to normal + target.css('cursor', compat.cursor.grab); + + } + + // Ensures that a full set of options are provided + // for the plug-in. Also does some validation + function getOptions(options) { + + // fill in missing values with defaults + options = $.extend({}, defaults, options); + + // check for inconsistent directional restrictions + if (options.direction !== 'multi' && options.direction !== options.wheelDirection) { + options.wheelDirection = options.direction; + } + + // ensure positive values for deltas + options.scrollDelta = math.abs(parseFloat(options.scrollDelta)); + options.wheelDelta = math.abs(parseFloat(options.wheelDelta)); + + // fix values for scroll offset + options.scrollLeft = options.scrollLeft === none ? null : math.abs(parseFloat(options.scrollLeft)); + options.scrollTop = options.scrollTop === none ? null : math.abs(parseFloat(options.scrollTop)); + + return options; + + } + + // Returns the sizing information (bounding box) for the + // target DOM element + function getSizing(target) { + + var $target = $(target), + width = $target.width(), + height = $target.height(), + scrollWidth = width >= target.scrollWidth ? width : target.scrollWidth, + scrollHeight = height >= target.scrollHeight ? height : target.scrollHeight, + hasScroll = scrollWidth > width || scrollHeight > height; + + return { + valid: hasScroll, + container: { + width: width, + height: height, + scrollWidth: scrollWidth, + scrollHeight: scrollHeight + }, + thumbs: { + horizontal: { + width: width * width / scrollWidth, + height: settings.thumbThickness, + corner: settings.thumbThickness / 2, + left: 0, + top: height - settings.thumbThickness + }, + vertical: { + width: settings.thumbThickness, + height: height * height / scrollHeight, + corner: settings.thumbThickness / 2, + left: width - settings.thumbThickness, + top: 0 + } + } + }; + + } + + // Attempts to get (or implicitly creates) the + // remover function for the target passed + // in as an argument + function getRemover(target, orCreate) { + + var $target = $(target), thumbs, + data = $target.data(datakey) || {}, + style = $target.attr('style'), + fallback = orCreate ? function () { + + data = $target.data(datakey); + thumbs = data.thumbs; + + // restore original styles (if any) + if (style) { + $target.attr('style', style); + } else { + $target.removeAttr('style'); + } + + // remove any created thumbs + if (thumbs) { + if (thumbs.horizontal) { thumbs.horizontal.remove(); } + if (thumbs.vertical) { thumbs.vertical.remove(); } + } + + // remove any bound overscroll events and data + $target + .removeData(datakey) + .off(events.wheel, wheel) + .off(events.start, start) + .off(events.end, stop) + .off(events.ignored, ignore); + + } : $.noop; + + return $.isFunction(data.remover) ? data.remover : fallback; + + } + + // Genterates CSS specific to a particular thumb. + // It requires sizing data and options + function getThumbCss(size, options) { + return { + position: 'absolute', + opacity: options.persistThumbs ? settings.thumbOpacity : 0, + 'background-color': 'black', + width: size.width + 'px', + height: size.height + 'px', + 'border-radius': size.corner + 'px', + 'margin': size.top + 'px 0 0 ' + size.left + 'px', + 'z-index': options.zIndex + }; + } + + // Creates the DOM elements used as "thumbs" within + // the target container. + function createThumbs(target, sizing, options) { + + var div = '
', + thumbs = {}, + css = false; + + if (sizing.container.scrollWidth > 0 && options.direction !== 'vertical') { + css = getThumbCss(sizing.thumbs.horizontal, options); + thumbs.horizontal = $(div).css(css).prependTo(target); + } + + if (sizing.container.scrollHeight > 0 && options.direction !== 'horizontal') { + css = getThumbCss(sizing.thumbs.vertical, options); + thumbs.vertical = $(div).css(css).prependTo(target); + } + + thumbs.added = !!css; + + return thumbs; + + } + + // ignores events on the overscroll element + function ignore(event) { + event.preventDefault(); + } + + // This function takes a jQuery element, some + // (optional) options, and sets up event metadata + // for each instance the plug-in affects + function setup(target, options) { + + // create initial data properties for this instance + options = getOptions(options); + var sizing = getSizing(target), + thumbs, data = { + options: options, sizing: sizing, + flags: { dragging: false }, + remover: getRemover(target, true) + }; + + // only apply handlers if the overscrolled element + // actually has an area to scroll + if (sizing.valid || options.ignoreSizing) { + // provide a circular-reference, enable events, and + // apply any required CSS + data.target = target = $(target).css({ + position: 'relative', + cursor: compat.cursor.grab + }).on(events.start, data, start) + .on(events.end, data, stop) + .on(events.ignored, data, ignore); + + // apply the stop listeners for drag end + if(options.dragHold) { + $(document).on(events.end, data, stop); + } else { + data.target.on(events.end, data, stop); + } + + // apply any user-provided scroll offsets + if (options.scrollLeft !== null) { + target.scrollLeft(options.scrollLeft); + } if (options.scrollTop !== null) { + target.scrollTop(options.scrollTop); + } + + // use native oversroll, if it exists + if (compat.overflowScrolling) { + target.css(compat.overflowScrolling, 'touch'); + } else { + target.on(events.scroll, data, scroll); + } + + // check to see if the user would like mousewheel support + if (options.captureWheel) { + target.on(events.wheel, data, wheel); + } + + // add thumbs and listeners (if we're showing them) + if (options.showThumbs) { + if (compat.overflowScrolling) { + target.css('overflow', 'scroll'); + } else { + target.css('overflow', 'hidden'); + data.thumbs = thumbs = createThumbs(target, sizing, options); + if (thumbs.added) { + moveThumbs(thumbs, sizing, target.scrollLeft(), target.scrollTop()); + if (options.hoverThumbs) { + target.on(events.hover, data, hover); + } + } + } + } else { + target.css('overflow', 'hidden'); + } + + target.data(datakey, data); + } + + } + + // Removes any event listeners and other instance-specific + // data from the target. It attempts to leave the target + // at the state it found it. + function teardown(target) { + getRemover(target)(); + } + + // This is the entry-point for enabling the plug-in; + // You can find it's exposure point at the end + // of this closure + function overscroll(options) { + /*jshint validthis:true */ + return this.removeOverscroll().each(function() { + setup(this, options); + }); + } + + // This is the entry-point for disabling the plug-in; + // You can find it's exposure point at the end + // of this closure + function removeOverscroll() { + /*jshint validthis:true */ + return this.each(function () { + teardown(this); + }); + } + + // Extend overscroll to expose settings to the user + overscroll.settings = settings; + + // Extend jQuery's prototype to expose the plug-in. + // If the supports native overflowScrolling, overscroll will not + // attempt to override the browser's built in support + $.extend(namespace, { + overscroll: overscroll, + removeOverscroll: removeOverscroll + }); + +})(window, document, navigator, Math, setTimeout, clearTimeout, jQuery.fn, jQuery); diff --git a/static/partials/image-view.html b/static/partials/image-view.html index 04dd9aabb..b2ea2d040 100644 --- a/static/partials/image-view.html +++ b/static/partials/image-view.html @@ -1,93 +1,89 @@ -
- No image found -
+
+
+
+ +

+ + {{repo.namespace}} + / + {{repo.name}} + / + {{image.value.id.substr(0, 12)}} +

+
-
-
- -

- - {{repo.namespace}} - / - {{repo.name}} - / - {{image.id.substr(0, 12)}} -

-
+ +
+ +
- -
- -
- - -
-
Full Image ID
-
-
-
-
- - - - + +
+
Full Image ID
+
+
+
+
+ + + + +
+
+ +
- - -
-
-
Created
-
-
+ +
Created
+
+ - -
- File Changes: -
-
- -
+ +
+ File Changes: +
+
+ +
- -
- -
-
-
-
- Showing {{(combinedChanges | filter:search | limitTo:50).length}} of {{(combinedChanges | filter:search).length}} results -
-
- -
-
-
-
-
- No matching changes -
-
- - - - {{folder}}/{{getFilename(change.file)}} - + +
+ +
+
+
+
+ Showing {{(combinedChanges | filter:search | limitTo:50).length}} of {{(combinedChanges | filter:search).length}} results +
+
+ +
+
+
+
+
+ No matching changes +
+
+ + + + {{folder}}/{{getFilename(change.file)}} + +
-
- -
-
-
+ +
+
+
+
- -
diff --git a/static/partials/new-organization.html b/static/partials/new-organization.html index de9927639..3033a20b7 100644 --- a/static/partials/new-organization.html +++ b/static/partials/new-organization.html @@ -1,8 +1,8 @@ -
- +
+
-
+
diff --git a/static/partials/new-repo.html b/static/partials/new-repo.html index 393d7003c..5f3ea1535 100644 --- a/static/partials/new-repo.html +++ b/static/partials/new-repo.html @@ -1,13 +1,15 @@
-

Please sign in

+
+
+
- +
- +
@@ -75,7 +77,7 @@ In order to make this repository private, you’ll need to upgrade your plan from {{ subscribedPlan.title }} to {{ planRequired.title }}. This will cost ${{ planRequired.price / 100 }}/month.
Upgrade now - +
diff --git a/static/partials/org-admin.html b/static/partials/org-admin.html index 17bdf56df..a4843005e 100644 --- a/static/partials/org-admin.html +++ b/static/partials/org-admin.html @@ -1,12 +1,5 @@ -
- -
- -
- No matching organization found -
- -
+
+
@@ -48,7 +41,7 @@
- +
@@ -105,8 +98,7 @@
- - +
diff --git a/static/partials/org-member-logs.html b/static/partials/org-member-logs.html index 7750de305..7f5d43077 100644 --- a/static/partials/org-member-logs.html +++ b/static/partials/org-member-logs.html @@ -1,16 +1,6 @@ -
- -
- -
- Organization not found -
- -
- Member not found -
- -
-
-
+
+
+
+
+
diff --git a/static/partials/org-view.html b/static/partials/org-view.html index 2a6deeb52..2a2dabdf2 100644 --- a/static/partials/org-view.html +++ b/static/partials/org-view.html @@ -1,51 +1,45 @@ -
- -
+
+
+
+
-
- No matching organization found -
+ + Create Team + -
-
-
- - - Create Team - - - Settings + Settings +
-
-