This commit is contained in:
Cameron Dawson 2014-02-27 08:24:07 -08:00
Родитель ac548fe1eb ea9c3b46bb
Коммит f1d7c9f4aa
7 изменённых файлов: 385 добавлений и 2 удалений

229
ui/css/persona-buttons.css Normal file
Просмотреть файл

@ -0,0 +1,229 @@
/* Link body */
.persona-button{
color: #fff;
display: inline-block;
font-size: 14px;
font-family: Helvetica, Arial, sans-serif;
font-weight: bold;
line-height: 1.1;
overflow: hidden;
position: relative;
text-decoration: none;
text-shadow: 0 1px rgba(0,0,0,0.5), 0 0 2px rgba(0,0,0,0.2);
margin-bottom: -9px;
background: #297dc3;
background: -moz-linear-gradient(top, #43a6e2, #287cc2);
background: -ms-linear-gradient(top, #43a6e2, #287cc2);
background: -o-linear-gradient(top, #43a6e2, #287cc2);
background: -webkit-linear-gradient(top, #43a6e2, #287cc2);
background: linear-gradient(top, #43a6e2, #287cc2);
-moz-border-radius: 3px;
-ms-border-radius: 3px;
-o-border-radius: 3px;
-webkit-border-radius: 3px;
border-radius: 3px;
-moz-box-shadow: 0 1px 0 rgba(0,0,0,0.2);
-ms-box-shadow: 0 1px 0 rgba(0,0,0,0.2);
-o-box-shadow: 0 1px 0 rgba(0,0,0,0.2);
-webkit-box-shadow: 0 1px 0 rgba(0,0,0,0.2);
box-shadow: 0 1px 0 rgba(0,0,0,0.2);
}
.persona-button:hover{
background: #21669f;
background: -moz-linear-gradient(top, #3788b9, #21669f);
background: -ms-linear-gradient(top, #3788b9, #21669f);
background: -o-linear-gradient(top, #3788b9, #21669f);
background: -webkit-linear-gradient(top, #3788b9, #21669f);
background: linear-gradient(top, #3788b9, #21669f);
}
.persona-button:active, .persona-button:focus{
top: 1px;
-moz-box-shadow: none;
-ms-box-shadow: none;
-o-box-shadow: none;
-webkit-box-shadow: none;
box-shadow: none;
}
.persona-button span{
display: inline-block;
padding: 5px 10px 5px 40px;
}
/* Icon */
.persona-button span:after{
background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA0AAAAPCAYAAAA/I0V3AAAA4klEQVR42o2RWaqEMBRE3YaCiDjPwQGcd9CrysLv4wTyoLFD90dxqbp1EgdPRB7Kskznea6Zn/aPoKoqUUrJOI5m4l2QBfSyLHKep1zXZSae3An1fS/7vst931bGkzuhaZrsLVbGkzuheZ7lOI6HyJ2QUkqv6yrbtv0LT+6E7G0UrfBfP3lZlpoXH4ZBmHgn5Pv+KwxDfqp0XQdgJp6c/RsUBIGOokiSJDE/s21bACbe5Ozp0TdAHMdSFIXUdS1N01C2wpObPT36HifwCJzI0iX29Oh7XP0E3CB9L01TzM+i/wePv4ZE5RtAngAAAABJRU5ErkJggg==) 10px center no-repeat;
content: '';
display: block;
width: 31px;
position: absolute;
bottom: 0;
left: -3px;
top: 0;
z-index: 10;
}
/* Icon background */
.persona-button span:before{
content: '';
display: block;
height: 100%;
width: 20px;
position: absolute;
bottom: 0;
left: 0;
top: 0;
z-index: 1;
background: #42a9dd;
background: -moz-linear-gradient(top, #50b8e8, #3095ce);
background: -ms-linear-gradient(top, #50b8e8, #3095ce);
background: -o-linear-gradient(top, #50b8e8, #3095ce);
background: -webkit-linear-gradient(top, #50b8e8, #3095ce);
background: linear-gradient(top, #50b8e8, #3095ce);
-moz-border-radius: 3px 0 0 3px;
-ms-border-radius: 3px 0 0 3px;
-o-border-radius: 3px 0 0 3px;
-webkit-border-radius: 3px 0 0 3px;
border-radius: 3px 0 0 3px;
}
/* Triangle */
.persona-button:before{
background: #42a9dd;
content: '';
display: block;
height: 26px;
width: 26px;
position: absolute;
left: 2px;
top: 50%;
margin-top: -13px;
z-index: 0;
background: -moz-linear-gradient(-45deg, #50b8e8, #3095ce);
background: -ms-linear-gradient(-45deg, #50b8e8, #3095ce);
background: -o-linear-gradient(-45deg, #50b8e8, #3095ce);
background: -webkit-linear-gradient(-45deg, #50b8e8, #3095ce);
background: linear-gradient(-45deg, #3095ce, #50b8e8); /* flipped for updated spec */
-moz-box-shadow: 1px -1px 1px rgba(0,0,0,0.1);
-ms-box-shadow: 1px -1px 1px rgba(0,0,0,0.1);
-o-box-shadow: 1px -1px 1px rgba(0,0,0,0.1);
-webkit-box-shadow: 1px -1px 1px rgba(0,0,0,0.1);
box-shadow: 1px -1px 1px rgba(0,0,0,0.1);
-moz-transform: rotate(45deg);
-ms-transform: rotate(45deg);
-o-transform: rotate(45deg);
-webkit-transform: rotate(45deg);
transform: rotate(45deg);
}
/* Inset shadow (required here because the icon background clips it when on the `a` element) */
.persona-button:after{
content: '';
display: block;
height: 100%;
width: 100%;
position: absolute;
left: 0;
top: 0;
bottom: 0;
right: 0;
z-index: 10;
-moz-border-radius: 3px;
-ms-border-radius: 3px;
-o-border-radius: 3px;
-webkit-border-radius: 3px;
border-radius: 3px;
-moz-box-shadow: inset 0 -1px 0 rgba(0,0,0,0.3);
-ms-box-shadow: inset 0 -1px 0 rgba(0,0,0,0.3);
-o-box-shadow: inset 0 -1px 0 rgba(0,0,0,0.3);
-webkit-box-shadow: inset 0 -1px 0 rgba(0,0,0,0.3);
box-shadow: inset 0 -1px 0 rgba(0,0,0,0.3);
}
/* ========================================================
* Dark button
* ===================================================== */
.persona-button.dark{
background: #3c3c3c;
background: -moz-linear-gradient(top, #606060, #3c3c3c);
background: -ms-linear-gradient(top, #606060, #3c3c3c);
background: -o-linear-gradient(top, #606060, #3c3c3c);
background: -webkit-linear-gradient(top, #606060, #3c3c3c);
background: linear-gradient(top, #606060, #3c3c3c);
}
.persona-button.dark:hover{
background: #2d2d2d;
background: -moz-linear-gradient(top, #484848, #2d2d2d);
background: -ms-linear-gradient(top, #484848, #2d2d2d);
background: -o-linear-gradient(top, #484848, #2d2d2d);
background: -webkit-linear-gradient(top, #484848, #2d2d2d);
background: linear-gradient(top, #484848, #2d2d2d);
}
.persona-button.dark span:before{ /* Icon BG */
background: #d34f2d;
background: -moz-linear-gradient(top, #ebac45, #d34f2d);
background: -ms-linear-gradient(top, #ebac45, #d34f2d);
background: -o-linear-gradient(top, #ebac45, #d34f2d);
background: -webkit-linear-gradient(top, #ebac45, #d34f2d);
background: linear-gradient(top, #ebac45, #d34f2d);
}
.persona-button.dark:before{ /* Triangle */
background: #d34f2d;
background: -moz-linear-gradient(-45deg, #ebac45, #d34f2d);
background: -ms-linear-gradient(-45deg, #ebac45, #d34f2d);
background: -o-linear-gradient(-45deg, #ebac45, #d34f2d);
background: -webkit-linear-gradient(-45deg, #ebac45, #d34f2d);
background: linear-gradient(-45deg, #d34f2d, #ebac45); /* flipped for updated spec */
}
/* ========================================================
* Orange button
* ===================================================== */
.persona-button.orange{
background: #ee731a;
background: -moz-linear-gradient(top, #ee731a, #d03116);
background: -ms-linear-gradient(top, #ee731a, #d03116);
background: -o-linear-gradient(top, #ee731a, #d03116);
background: -webkit-linear-gradient(top, #ee731a, #d03116);
background: linear-gradient(top, #ee731a, #d03116);
}
.persona-button.orange:hover{
background: #cb6216;
background: -moz-linear-gradient(top, #cb6216, #b12a13);
background: -ms-linear-gradient(top, #cb6216, #b12a13);
background: -o-linear-gradient(top, #cb6216, #b12a13);
background: -webkit-linear-gradient(top, #cb6216, #b12a13);
background: linear-gradient(top, #cb6216, #b12a13);
}
.persona-button.orange span:before{ /* Icon BG */
background: #e84a21;
background: -moz-linear-gradient(top, #f7ad27, #e84a21);
background: -ms-linear-gradient(top, #f7ad27, #e84a21);
background: -o-linear-gradient(top, #f7ad27, #e84a21);
background: -webkit-linear-gradient(top, #f7ad27, #e84a21);
background: linear-gradient(top, #f7ad27, #e84a21);
}
.persona-button.orange:before{ /* Triangle */
background: #e84a21;
background: -moz-linear-gradient(-45deg, #f7ad27, #e84a21);
background: -ms-linear-gradient(-45deg, #f7ad27, #e84a21);
background: -o-linear-gradient(-45deg, #f7ad27, #e84a21);
background: -webkit-linear-gradient(-45deg, #f7ad27, #e84a21);
background: linear-gradient(-45deg, #e84a21, #f7ad27); /* flipped for updated spec */
}

Просмотреть файл

@ -3,10 +3,13 @@
<head>
<title ng-bind="repoName"></title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=Edge">
<!-- Bootstrap -->
<link href="css/bootstrap.css" rel="stylesheet" media="screen">
<link href="css/treeherder.css" rel="stylesheet" media="screen">
<link href="css/persona-buttons.css" rel="stylesheet" media="screen">
</head>
<body ng-controller="MainCtrl">
<th-global-top-nav-panel></th-global-top-nav-panel>
@ -61,6 +64,7 @@
<script src="plugins/closed_bugs_suggestions/controller.js"></script>
<script src="js/filters.js"></script>
<script src="vendor/Config.js"></script>
<script src="https://login.persona.org/include.js"></script>
<!-- Clone targets -->

Просмотреть файл

@ -3,7 +3,7 @@
treeherder.controller('MainCtrl',
function MainController($scope, $rootScope, $routeParams, $location, $log,
localStorageService, thReposModel, thSocket,
thResultStatusList) {
thResultStatusList, thServiceDomain) {
$scope.query="";
$scope.statusError = function(msg) {
$rootScope.statusMsg = msg;
@ -51,5 +51,9 @@ treeherder.controller('MainCtrl',
$location.search({repo: repo_name});
};
$scope.user = {};
$scope.user.email = localStorageService.get("user.email");
$scope.user.loggedin = $scope.user.email == null ? false : true;
}
);

Просмотреть файл

@ -752,3 +752,73 @@ treeherder.directive('resizablePanel', function($document, $log) {
};
});
treeherder.directive('personaButtons', function($http, $q, $log, $rootScope, localStorageService, thServiceDomain, BrowserId) {
return {
restrict: "E",
link: function(scope, element, attrs) {
localStorageService.clearAll()
scope.user = scope.user || {};
// check if already know who the current user is
// if the user.email value is null, it means that he's not logged in
scope.user.email = scope.user.email || localStorageService.get('user.email');
scope.user.loggedin = scope.user.email == null ? false : true;
scope.login = function(){
/*
* BrowserID.login returns a promise of the verification.
* If successful, we will find the user email in the response
*/
BrowserId.login()
.then(function(response){
scope.user.loggedin = true;
scope.user.email = response.data.email;
localStorageService.add('user.email', scope.user.email);
},function(){
// logout if the verification failed
scope.logout();
});
};
scope.logout = function(){
BrowserId.logout().then(function(response){
scope.user.loggedin = false;
scope.user.email = null;
localStorageService.remove('user.loggedin');
localStorageService.remove('user.email');
});
};
navigator.id.watch({
/*
* loggedinUser is all that we know about the user before
* the interaction with persona. This value could come from a cookie to persist the authentication
* among page reloads. If the value is null, the user is considered logged out.
*/
loggedInUser: scope.user.email,
/*
* We need a watch call to interact with persona.
* onLogin is called when persona provides an assertion
* This is the only way we can know the assertion from persona,
* so we resolve BrowserId.requestDeferred with the assertion retrieved
*/
onlogin: function(assertion){
if (BrowserId.requestDeferred) {
BrowserId.requestDeferred.resolve(assertion);
}
},
/*
* Resolve BrowserId.logoutDeferred once the user is logged out from persona
*/
onlogout: function(){
if (BrowserId.logoutDeferred) {
BrowserId.logoutDeferred.resolve();
}
}
});
},
templateUrl: 'partials/persona_buttons.html'
};
});

Просмотреть файл

@ -155,3 +155,76 @@ treeherder.factory('thCloneHtml', function($interpolate) {
};
});
treeherder.factory('BrowserId', function($http, $q, $log, thServiceDomain){
/*
* BrowserId is a wrapper for the persona authentication service
* it handles the navigator.id.request and navigator.id.logout calls
* and exposes the related promises via requestDeferred and logoutDeferred.
* This is mostly inspired by the django_browserid jquery implementation.
*/
var browserid = {
info: $http.get(thServiceDomain+'/browserid/info/'),
requestDeferred: null,
logoutDeferred: null,
/*
* Retrieve an assertion from the persona service and
* and send it to the treeherder verification endpoints.
*
*/
login: function(requestArgs){
return browserid.getAssertion(requestArgs)
.then(function(response) {
return browserid.verifyAssertion(response);
});
},
/*
* Logout from persona and notify treeherder of the change
* The logoutDeferred promise is resolved by the onLogout callback
* of navigator.id.watch
*/
logout: function(){
return browserid.info.then(function(response){
browserid.logoutDeferred = $q.defer();
navigator.id.logout();
return browserid.logoutDeferred.promise.then(function(){
return $http.post(response.data.logoutUrl);
})
});
},
/*
* Ask persona to provide an assetion and return a promise of the response
* The requestDeferred promise is resolved by the onLogin callback
* of navigator.id.watch.
*/
getAssertion: function(requestArgs){
return browserid.info.then(function(response){
requestArgs = _.extend({}, response.data.requestArgs, requestArgs);
browserid.requestDeferred = $q.defer();
navigator.id.request(requestArgs);
return browserid.requestDeferred.promise;
});
},
/*
* Verify the assertion provided by persona against the treeherder verification endpoint.
* The django_browserid endpoint accept a post request with form-urlencoded fields.
*/
verifyAssertion: function(assertion){
return browserid.info.then(function(response){
return $http.post(
response.data.loginUrl, {assertion: assertion},{
headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'},
transformRequest: browserid.transform_data
});
});
},
transform_data: function(data){
return $.param(data);
}
}
return browserid;
});

Просмотреть файл

@ -0,0 +1,2 @@
<a class="persona-button orange" ng-if="user.loggedin" ng-click="logout()"><span>Logout</span></a>
<a class="persona-button orange" ng-if="!user.loggedin" ng-click="login()"><span>Login with persona</span></a>

Просмотреть файл

@ -13,6 +13,7 @@
</span>
</span>
<span class="pull-right">
<persona-buttons></persona-buttons>
<span class="btn btn-view-nav"
ng-class="{'active': (isRepoPanelShowing)}"
ng-click="isRepoPanelShowing=!isRepoPanelShowing"><span>repos</span>