- Fix namespace drop down to save the namespace last selected (and validate)

- Add a "can_create_repo" entry to the organization and have orgs grayed out in the new repo view if the user cannot create a repo
- Fix the multiple-orgs bug in the model
- Have the "create new repository" button disappear on landing if the org is selected and the user does not have create permissions for that org
This commit is contained in:
Joseph Schorr 2013-11-07 00:49:13 -05:00
parent 4b460be4dd
commit 0c4dec6de4
10 changed files with 89 additions and 14 deletions

View file

@ -322,7 +322,7 @@ def verify_user(username, password):
def get_user_organizations(username):
UserAlias = User.alias()
all_teams = User.select().join(Team).join(TeamMember)
all_teams = User.select().distinct().join(Team).join(TeamMember)
with_user = all_teams.join(UserAlias, on=(UserAlias.id == TeamMember.user))
return with_user.where(User.organization == True,
UserAlias.username == username)

View file

@ -84,7 +84,8 @@ def get_logged_in_user():
return {
'name': o.username,
'gravatar': compute_hash(o.email),
'is_org_admin': admin_org.can()
'is_org_admin': admin_org.can(),
'can_create_repo': admin_org.can() or CreateRepositoryPermission(o.username).can()
}
if current_user.is_anonymous():
@ -100,7 +101,8 @@ def get_logged_in_user():
'email': user.email,
'gravatar': compute_hash(user.email),
'askForPassword': user.password_hash is None,
'organizations': [org_view(o) for o in organizations]
'organizations': [org_view(o) for o in organizations],
'can_create_repo': True
})

View file

@ -46,6 +46,26 @@
font-size: 14px;
}
.namespace-selector-dropdown .namespace-item {
position: relative;
}
.namespace-selector-dropdown .namespace-item .fa {
position: absolute;
right: 12px;
top: 12px;
color: #aaa;
}
.namespace-selector-dropdown .namespace-item.disabled img {
-webkit-filter: grayscale(1);
opacity: 0.5;
}
.namespace-selector-dropdown .namespace-item .tooltip-inner {
min-width: 200px;
}
.user-notification {
background: red;
}

View file

@ -10,11 +10,17 @@
{{namespace}} <span class="caret"></span>
</button>
<ul class="dropdown-menu" role="menu">
<li ng-repeat="org in user.organizations">
<li class="namespace-item" ng-repeat="org in user.organizations"
ng-class="(requireCreate && !namespaces[org.name].can_create_repo) ? 'disabled' : ''">
<a class="namespace" href="javascript:void(0)" ng-click="setNamespace(org)">
<img src="//www.gravatar.com/avatar/{{ org.gravatar }}?s=24&d=identicon" />
<span class="namespace-name">{{ org.name }}</span>
</a>
<i class="fa fa-exclamation-triangle" ng-show="requireCreate && !namespaces[org.name].can_create_repo"
title="You do not have permission to create repositories for this organization"
data-placement="right"
bs-tooltip="tooltip.title"></i>
</li>
<li class="divider"></li>
<li>

View file

@ -47,7 +47,7 @@ function getMarkedDown(string) {
}
// Start the application code itself.
quayApp = angular.module('quay', ['restangular', 'angularMoment', 'angulartics', 'angulartics.mixpanel', '$strap.directives'], function($provide) {
quayApp = angular.module('quay', ['restangular', 'angularMoment', 'angulartics', 'angulartics.mixpanel', '$strap.directives', 'ngCookies'], function($provide) {
$provide.factory('UserService', ['Restangular', 'PlanService', function(Restangular, PlanService) {
var userResponse = {
verified: false,
@ -639,20 +639,42 @@ quayApp.directive('namespaceSelector', function () {
restrict: 'C',
scope: {
'user': '=user',
'namespace': '=namespace'
'namespace': '=namespace',
'requireCreate': '=requireCreate'
},
controller: function($scope, $element) {
controller: function($scope, $element, $cookieStore) {
$scope.namespaces = {};
$scope.initialize = function(user) {
var namespaces = {};
namespaces[user.username] = user;
for (var i = 0; i < user.organizations.length; ++i) {
namespaces[user.organizations[i].name] = user.organizations[i];
}
var initialNamespace = $cookieStore.get('quay.currentnamespace') || $scope.user.username;
$scope.namespaces = namespaces;
$scope.setNamespace($scope.namespaces[initialNamespace]);
};
$scope.setNamespace = function(namespaceObj) {
if (!namespaceObj) {
namespaceObj = {'name': '', 'gravatar': ''};
namespaceObj = $scope.namespaces[$scope.user.username];
}
if ($scope.requireCreate && !namespaceObj.can_create_repo) {
namespaceObj = $scope.namespaces[$scope.user.username];
}
var newNamespace = namespaceObj.name || namespaceObj.username;
$scope.namespaceObj = namespaceObj;
$scope.namespace = namespaceObj.name || namespaceObj.username;
$scope.namespace = newNamespace;
$cookieStore.put('quay.currentnamespace', newNamespace);
};
$scope.setNamespace($scope.user);
$scope.$watch('user', function(user) {
$scope.setNamespace(user);
$scope.user = user;
$scope.initialize(user);
});
}
};

View file

@ -230,6 +230,23 @@ function LandingCtrl($scope, $timeout, $location, Restangular, UserService, KeyS
});
};
$scope.canCreateRepo = function(namespace) {
if (!$scope.user) { return false; }
if (namespace == $scope.user.username) {
return true;
}
for (var i = 0; i < $scope.user.organizations.length; ++i) {
var org = $scope.user.organizations[i];
if (org.name == namespace) {
return org.can_create_repo;
}
}
return false;
};
var loadMyRepos = function(namespace) {
if (!$scope.user || $scope.user.anonymous || !namespace) {
return;

7
static/lib/angular-cookies.min.js vendored Normal file
View file

@ -0,0 +1,7 @@
/*
AngularJS v1.2.0-ed8640b
(c) 2010-2012 Google, Inc. http://angularjs.org
License: MIT
*/
(function(p,f,n){'use strict';f.module("ngCookies",["ng"]).factory("$cookies",["$rootScope","$browser",function(d,b){var c={},g={},h,k=!1,l=f.copy,m=f.isUndefined;b.addPollFn(function(){var a=b.cookies();h!=a&&(h=a,l(a,g),l(a,c),k&&d.$apply())})();k=!0;d.$watch(function(){var a,e,d;for(a in g)m(c[a])&&b.cookies(a,n);for(a in c)(e=c[a],f.isString(e))?e!==g[a]&&(b.cookies(a,e),d=!0):f.isDefined(g[a])?c[a]=g[a]:delete c[a];if(d)for(a in e=b.cookies(),c)c[a]!==e[a]&&(m(e[a])?delete c[a]:c[a]=e[a])});
return c}]).factory("$cookieStore",["$cookies",function(d){return{get:function(b){return(b=d[b])?f.fromJson(b):b},put:function(b,c){d[b]=f.toJson(c)},remove:function(b){delete d[b]}}}])})(window,window.angular);

View file

@ -29,7 +29,7 @@
<div class="options">
<a class="btn btn-primary" href="/repository/">Browse all repositories</a>
<a class="btn btn-success" href="/new/">Create a new repository</a>
<a class="btn btn-success" href="/new/" ng-show="canCreateRepo(namespace)">Create a new repository</a>
</div>
</div>
</div>

View file

@ -29,7 +29,7 @@
<div class="section">
<div class="new-header">
<span style="color: #444;">
<span class="namespace-selector" user="user" namespace="repo.namespace"></span>
<span class="namespace-selector" user="user" namespace="repo.namespace" require-create="true"></span>
<span style="color: #ccc">/</span>
<span class="name-container">
<input id="repoName" name="repoName" type="text" class="form-control" placeholder="Repository Name" ng-model="repo.name" required autofocus data-trigger="manual" data-content="{{ createError }}" data-placement="right">

View file

@ -47,6 +47,7 @@
<script src="static/lib/angulartics-mixpanel.js"></script>
<script src="static/lib/angular-moment.min.js"></script>
<script src="static/lib/angular-cookies.min.js"></script>
<script src="static/lib/typeahead.min.js"></script>