зеркало из https://github.com/mozilla/treeherder.git
merge from master
This commit is contained in:
Коммит
f1d7c9f4aa
|
@ -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>
|
||||
|
|
Загрузка…
Ссылка в новой задаче