Add a configurable avatar system and add an internal avatar system for enterprise
This commit is contained in:
parent
f6dd8b0a4d
commit
e9cac407df
36 changed files with 241 additions and 92 deletions
|
@ -4819,7 +4819,7 @@ i.slack-icon {
|
|||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.member-listing .gravatar {
|
||||
.member-listing .avatar {
|
||||
vertical-align: middle;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<div class="application-info-element" style="padding-bottom: 18px">
|
||||
<div class="auth-header">
|
||||
<img src="//www.gravatar.com/avatar/{{ application.gravatar }}?s=48&d=identicon">
|
||||
<span class="avatar" size="48" hash="application.avatar"></span>
|
||||
<h2><a href="{{ application.url }}" target="_blank">{{ application.name }}</a></h2>
|
||||
<h4>
|
||||
{{ application.organization.name }}
|
||||
|
|
1
static/directives/avatar.html
Normal file
1
static/directives/avatar.html
Normal file
|
@ -0,0 +1 @@
|
|||
<img class="avatar-element" ng-src="{{ AvatarService.getAvatar(_hash, size) }}">
|
|
@ -7,15 +7,15 @@
|
|||
</span>
|
||||
</span>
|
||||
<span ng-if="entity.kind == 'org'">
|
||||
<img ng-src="//www.gravatar.com/avatar/{{ entity.gravatar }}?s={{ gravatarSize || '16' }}&d=identicon">
|
||||
<span class="avatar" size="avatarSize || 16" hash="entity.avatar"></span>
|
||||
<span class="entity-name">
|
||||
<span ng-if="!getIsAdmin(entity.name)">{{entity.name}}</span>
|
||||
<span ng-if="getIsAdmin(entity.name)"><a href="/organization/{{ entity.name }}">{{entity.name}}</a></span>
|
||||
</span>
|
||||
</span>
|
||||
<span ng-if="entity.kind != 'team' && entity.kind != 'org'">
|
||||
<img class="gravatar" ng-if="showGravatar == 'true' && entity.gravatar" ng-src="//www.gravatar.com/avatar/{{ entity.gravatar }}?s={{ gravatarSize || '16' }}&d=identicon">
|
||||
<span ng-if="showGravatar != 'true' || !entity.gravatar">
|
||||
<span class="avatar" size="avatarSize || 16" hash="entity.avatar" ng-if="showAvatar == 'true' && entity.avatar"></span>
|
||||
<span ng-if="showAvatar != 'true' || !entity.avatar">
|
||||
<i class="fa fa-user" ng-show="!entity.is_robot" data-title="User" bs-tooltip="tooltip.title" data-container="body"></i>
|
||||
<i class="fa fa-wrench" ng-show="entity.is_robot" data-title="Robot Account" bs-tooltip="tooltip.title" data-container="body"></i>
|
||||
</span>
|
||||
|
|
|
@ -35,7 +35,7 @@
|
|||
|
||||
<li class="dropdown" ng-switch-when="false">
|
||||
<a href="javascript:void(0)" class="dropdown-toggle user-dropdown user-view" data-toggle="dropdown">
|
||||
<img src="//www.gravatar.com/avatar/{{ user.gravatar }}?s=32&d=identicon" />
|
||||
<span class="avatar" size="32" hash="user.avatar"></span>
|
||||
{{ user.username }}
|
||||
<span class="notifications-bubble"></span>
|
||||
<b class="caret"></b>
|
||||
|
|
|
@ -1,20 +1,20 @@
|
|||
<span class="namespace-selector-dropdown">
|
||||
<span ng-show="user.organizations.length == 0">
|
||||
<img src="//www.gravatar.com/avatar/{{ user.gravatar }}?s=24&d=identicon" />
|
||||
<span class="avatar" size="24" hash="user.avatar"></span>
|
||||
<span class="namespace-name">{{user.username}}</span>
|
||||
</span>
|
||||
|
||||
<div class="btn-group" ng-show="user.organizations.length > 0">
|
||||
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown">
|
||||
<img src="//www.gravatar.com/avatar/{{ namespaceObj.gravatar }}?s=16&d=identicon" />
|
||||
<span class="avatar" size="16" hash="namespaceObj.avatar"></span>
|
||||
{{namespace}} <span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu" role="menu">
|
||||
<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>
|
||||
<span class="avatar" size="24" hash="org.avatar"></span>
|
||||
<span class="namespace-name">{{ org.name }}</span>
|
||||
</a>
|
||||
|
||||
<i class="fa fa-exclamation-triangle" ng-show="requireCreate && !namespaces[org.name].can_create_repo"
|
||||
|
@ -25,7 +25,7 @@
|
|||
<li class="divider"></li>
|
||||
<li>
|
||||
<a class="namespace" href="javascript:void(0)" ng-click="setNamespace(user)">
|
||||
<img src="//www.gravatar.com/avatar/{{ user.gravatar }}?s=24&d=identicon" />
|
||||
<span class="avatar" size="24" hash="user.avatar"></span>
|
||||
<span class="namespace-name">{{ user.username }}</span>
|
||||
</a>
|
||||
</li>
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<div class="circle" ng-class="getClass(notification)"></div>
|
||||
<div class="message" ng-bind-html="getMessage(notification)"></div>
|
||||
<div class="orginfo" ng-if="notification.organization">
|
||||
<img src="//www.gravatar.com/avatar/{{ getGravatar(notification.organization) }}?s=24&d=identicon" />
|
||||
<span class="avatar" size="24" hash="getAvatar(notification.organization)"></span>
|
||||
<span class="orgname">{{ notification.organization }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<div class="organization-header-element">
|
||||
<img src="//www.gravatar.com/avatar/{{ organization.gravatar }}?s=24&d=identicon">
|
||||
<span class="avatar" size="24" hash="organization.avatar"></span>
|
||||
<span class="organization-name" ng-show="teamName || clickable">
|
||||
<a href="/organization/{{ organization.name }}">{{ organization.name }}</a>
|
||||
</span>
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
<td>
|
||||
<div class="current-repo">
|
||||
<img class="dropdown-select-icon github-org-icon"
|
||||
ng-src="{{ state.currentRepo.avatar_url ? state.currentRepo.avatar_url : '//www.gravatar.com/avatar/' }}">
|
||||
ng-src="{{ state.currentRepo.avatar_url ? state.currentRepo.avatar_url : '/static/img/empty.png' }}">
|
||||
<a ng-href="https://github.com/{{ state.currentRepo.repo }}" target="_blank">{{ state.currentRepo.repo }}</a>
|
||||
</div>
|
||||
</td>
|
||||
|
@ -53,7 +53,7 @@
|
|||
<!-- Icons -->
|
||||
<i class="dropdown-select-icon none-icon fa fa-github fa-lg"></i>
|
||||
<img class="dropdown-select-icon github-org-icon"
|
||||
ng-src="{{ state.currentRepo.avatar_url ? state.currentRepo.avatar_url : '//www.gravatar.com/avatar/' }}">
|
||||
ng-src="{{ state.currentRepo.avatar_url ? state.currentRepo.avatar_url : '/static/image/empty.png' }}">
|
||||
|
||||
<!-- Dropdown menu -->
|
||||
<ul class="dropdown-select-menu scrollable-menu" role="menu">
|
||||
|
|
BIN
static/img/empty.png
Normal file
BIN
static/img/empty.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 95 B |
|
@ -620,6 +620,51 @@ quayApp = angular.module('quay', quayDependencies, function($provide, cfpLoading
|
|||
return pingService;
|
||||
}]);
|
||||
|
||||
$provide.factory('AvatarService', ['Config', '$sanitize', 'md5',
|
||||
function(Config, $sanitize, md5) {
|
||||
var avatarService = {};
|
||||
var cache = {};
|
||||
|
||||
avatarService.getAvatar = function(hash, opt_size) {
|
||||
var size = opt_size || 16;
|
||||
switch (Config['AVATAR_KIND']) {
|
||||
case 'local':
|
||||
return '/avatar/' + hash + '?size=' + size;
|
||||
break;
|
||||
|
||||
case 'gravatar':
|
||||
return '//www.gravatar.com/avatar/' + hash + '?d=identicon&size=' + size;
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
avatarService.computeHash = function(opt_email, opt_name) {
|
||||
var email = opt_email || '';
|
||||
var name = opt_name || '';
|
||||
|
||||
var cacheKey = email + ':' + name;
|
||||
if (!cacheKey) { return '-'; }
|
||||
|
||||
if (cache[cacheKey]) {
|
||||
return cache[cacheKey];
|
||||
}
|
||||
|
||||
var hash = md5.createHash(email.toString().toLowerCase());
|
||||
switch (Config['AVATAR_KIND']) {
|
||||
case 'local':
|
||||
if (name) {
|
||||
hash = name[0] + hash;
|
||||
} else if (email) {
|
||||
hash = email[0] + hash;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return cache[cacheKey] = hash;
|
||||
};
|
||||
|
||||
return avatarService;
|
||||
}]);
|
||||
|
||||
$provide.factory('TriggerService', ['UtilService', '$sanitize', 'KeyService',
|
||||
function(UtilService, $sanitize, KeyService) {
|
||||
|
@ -2441,8 +2486,8 @@ quayApp.directive('entityReference', function () {
|
|||
scope: {
|
||||
'entity': '=entity',
|
||||
'namespace': '=namespace',
|
||||
'showGravatar': '@showGravatar',
|
||||
'gravatarSize': '@gravatarSize'
|
||||
'showAvatar': '@showAvatar',
|
||||
'avatarSize': '@avatarSize'
|
||||
},
|
||||
controller: function($scope, $element, UserService, UtilService) {
|
||||
$scope.getIsAdmin = function(namespace) {
|
||||
|
@ -4319,8 +4364,7 @@ quayApp.directive('entitySearch', function () {
|
|||
} else if (datum.entity.kind == 'team') {
|
||||
template += '<i class="fa fa-group fa-lg"></i>';
|
||||
} else if (datum.entity.kind == 'org') {
|
||||
template += '<i class="fa"><img src="//www.gravatar.com/avatar/' +
|
||||
datum.entity.gravatar + '?s=16&d=identicon"></i>';
|
||||
template += '<i class="fa">' + AvatarService.getAvatar(datum.entity.avatar, 16) + '</i>';
|
||||
}
|
||||
|
||||
template += '<span class="name">' + datum.value + '</span>';
|
||||
|
@ -6043,9 +6087,9 @@ quayApp.directive('notificationView', function () {
|
|||
return NotificationService.getMessage(notification);
|
||||
};
|
||||
|
||||
$scope.getGravatar = function(orgname) {
|
||||
$scope.getAvatar = function(orgname) {
|
||||
var organization = UserService.getOrganization(orgname);
|
||||
return organization['gravatar'] || '';
|
||||
return organization['avatar'] || '';
|
||||
};
|
||||
|
||||
$scope.parseDate = function(dateString) {
|
||||
|
@ -6427,6 +6471,39 @@ quayApp.directive('locationView', function () {
|
|||
});
|
||||
|
||||
|
||||
quayApp.directive('avatar', function () {
|
||||
var directiveDefinitionObject = {
|
||||
priority: 0,
|
||||
templateUrl: '/static/directives/avatar.html',
|
||||
replace: false,
|
||||
transclude: true,
|
||||
restrict: 'C',
|
||||
scope: {
|
||||
'hash': '=hash',
|
||||
'email': '=email',
|
||||
'name': '=name',
|
||||
'size': '=size'
|
||||
},
|
||||
controller: function($scope, $element, AvatarService) {
|
||||
$scope.AvatarService = AvatarService;
|
||||
|
||||
var refreshHash = function() {
|
||||
if (!$scope.name && !$scope.email) { return; }
|
||||
$scope._hash = AvatarService.computeHash($scope.email, $scope.name);
|
||||
};
|
||||
|
||||
$scope.$watch('hash', function(hash) {
|
||||
$scope._hash = hash;
|
||||
});
|
||||
|
||||
$scope.$watch('name', refreshHash);
|
||||
$scope.$watch('email', refreshHash);
|
||||
}
|
||||
};
|
||||
return directiveDefinitionObject;
|
||||
});
|
||||
|
||||
|
||||
quayApp.directive('tagSpecificImagesView', function () {
|
||||
var directiveDefinitionObject = {
|
||||
priority: 0,
|
||||
|
|
|
@ -2736,8 +2736,8 @@ function ManageApplicationCtrl($scope, $routeParams, $rootScope, $location, $tim
|
|||
delete $scope.application['description'];
|
||||
}
|
||||
|
||||
if (!$scope.application['gravatar_email']) {
|
||||
delete $scope.application['gravatar_email'];
|
||||
if (!$scope.application['avatar_email']) {
|
||||
delete $scope.application['avatar_email'];
|
||||
}
|
||||
|
||||
var errorHandler = ApiService.errorDisplay('Could not update application', function(resp) {
|
||||
|
|
|
@ -58,9 +58,6 @@
|
|||
<small>Co-Founder</small></h3>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-3 col-md-2">
|
||||
<img class="img-rounded founder-photo" src="http://www.gravatar.com/avatar/342ea83fd68d33f90b1f06f466d533c6?s=128&d=identicon">
|
||||
</div>
|
||||
<div class="col-sm-7 col-md-10">
|
||||
<p>Jacob graduated from The University of Michigan with a Bachelors in Computer Engineering. From there he allowed his love of flight and mountains to lure him to Seattle where he took a job with Boeing Commercial Airplanes working on the world's most accurate flight simulator. When he realized how much he also loved web development, he moved to Amazon to work on the e-commerce back-end. Finally, desiring to move to New York City, he moved to Google, where he worked on several products related to Google APIs.</p>
|
||||
</div>
|
||||
|
@ -71,9 +68,6 @@
|
|||
<small>Co-Founder</small></h3>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-3 col-md-2">
|
||||
<img class="img-rounded founder-photo" src="http://www.gravatar.com/avatar/9fc3232622773fb2e8f71c0027601bc5?s=128&d=mm">
|
||||
</div>
|
||||
<div class="col-sm-7 col-md-10">
|
||||
<p>Joseph graduated from University of Pennsylvania with a Bachelors and Masters in Computer Science. After a record setting (probably) five internships with Google, he took a full time position there to continue his work on exciting products such as Google Spreadsheets, the Google Closure Compiler, and Google APIs. </p>
|
||||
</div>
|
||||
|
|
|
@ -47,7 +47,7 @@
|
|||
<div class="signup-form"></div>
|
||||
</div>
|
||||
<div ng-show="!user.anonymous" class="user-welcome">
|
||||
<img class="gravatar" src="//www.gravatar.com/avatar/{{ user.gravatar }}?s=128&d=identicon" />
|
||||
<span class="avatar" size="128" hash="user.avatar"></span>
|
||||
<div class="sub-message">Welcome <b>{{ user.username }}</b>!</div>
|
||||
<a ng-show="my_repositories.value" class="btn btn-primary" href="/repository/">Browse all repositories</a>
|
||||
<a class="btn btn-success" href="/new/">Create a new repository</a>
|
||||
|
|
|
@ -57,7 +57,7 @@
|
|||
<div class="signup-form"></div>
|
||||
</div>
|
||||
<div ng-show="!user.anonymous" class="user-welcome">
|
||||
<img class="gravatar" src="//www.gravatar.com/avatar/{{ user.gravatar }}?s=128&d=identicon" />
|
||||
<span class="avatar" size="128" hash="user.avatar"></span>
|
||||
<div class="sub-message">Welcome <b>{{ user.username }}</b>!</div>
|
||||
<a ng-show="my_repositories.value" class="btn btn-primary" href="/repository/">Browse all repositories</a>
|
||||
<a class="btn btn-success" href="/new/">Create a new repository</a>
|
||||
|
|
|
@ -6,11 +6,11 @@
|
|||
<!-- Header -->
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="auth-header">
|
||||
<img src="//www.gravatar.com/avatar/{{ application.gravatar_email | gravatar }}?s=48&d=identicon">
|
||||
<h2>{{ application.name || '(Untitled)' }}</h2>
|
||||
<div class="auth-header">
|
||||
<h2><span class="avatar" size="48" email="application.avatar_email" name="application.name"></span>
|
||||
{{ application.name || '(Untitled)' }}</h2>
|
||||
<h4>
|
||||
<img src="//www.gravatar.com/avatar/{{ organization.gravatar }}?s=24&d=identicon" style="vertical-align: middle; margin-right: 4px;">
|
||||
<span class="avatar" size="24" hash="organization.avatar" style="vertical-align: middle; margin-right: 4px;"></span>
|
||||
<span style="vertical-align: middle"><a href="/organization/{{ organization.name }}/admin">{{ organization.name }}</a></span>
|
||||
</h4>
|
||||
</div>
|
||||
|
@ -59,9 +59,9 @@
|
|||
</div>
|
||||
|
||||
<div class="form-group nested">
|
||||
<label for="fieldAppGravatar">Gravatar E-mail (optional)</label>
|
||||
<input type="email" class="form-control" id="fieldAppGravatar" placeholder="Gravatar E-mail" ng-model="application.gravatar_email">
|
||||
<div class="description">An e-mail address representing the <a href="http://en.gravatar.com/" target="_blank">Gravatar</a> for the application. See above for the icon.</div>
|
||||
<label for="fieldAppAvatar">Avatar E-mail (optional)</label>
|
||||
<input type="email" class="form-control" id="fieldAppAvatar" placeholder="Avatar E-mail" ng-model="application.avatar_email">
|
||||
<div class="description">An e-mail address representing the <a href="http://docs.quay.io/glossary/avatar" target="_blank">Avatar</a> for the application. See above for the icon.</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group nested" style="margin-top: 10px; padding-top: 10px; border-top: 1px solid #eee;">
|
||||
|
|
|
@ -43,7 +43,7 @@
|
|||
<div class="panel-content" style="padding-left: 20px; margin-top: 10px;">
|
||||
<form class="form-change" id="changeEmailForm" name="changeEmailForm" ng-submit="changeEmail()" data-trigger="manual"
|
||||
data-content="{{ changeEmailError }}" data-placement="bottom" ng-show="!updatingOrganization">
|
||||
<img src="//www.gravatar.com/avatar/{{ organizationEmail | gravatar }}?s=24&d=identicon">
|
||||
<span class="avatar" size="24" email="organizationEmail" name="orgname"></span>
|
||||
<input type="email" class="form-control" ng-model="organizationEmail"
|
||||
style="margin-left: 10px; margin-right: 10px; width: 400px; display: inline-block;" required>
|
||||
<button class="btn btn-primary" type="submit" ng-disabled="changeEmailForm.$invalid || organizationEmail == organization.email">
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
<h2>Organizations</h2>
|
||||
|
||||
<div class="organization-listing" ng-repeat="organization in user.organizations">
|
||||
<img class="gravatar" src="//www.gravatar.com/avatar/{{ organization.gravatar }}?s=32&d=identicon">
|
||||
<span class="avatar" size="32" hash="organization.avatar"></span>
|
||||
<a class="org-title" href="/organization/{{ organization.name }}">{{ organization.name }}</a>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -36,7 +36,7 @@
|
|||
|
||||
<tr ng-repeat="member in members | filter:search | filter: filterFunction(false, false) | orderBy: 'name'">
|
||||
<td class="user entity">
|
||||
<span class="entity-reference" entity="member" namespace="organization.name" show-gravatar="true" gravatar-size="32"></span>
|
||||
<span class="entity-reference" entity="member" namespace="organization.name" show-avatar="true" avatar-size="32"></span>
|
||||
</td>
|
||||
<td>
|
||||
<span class="delete-ui" delete-title="'Remove ' + member.name + ' from team'" button-title="'Remove'"
|
||||
|
@ -67,10 +67,10 @@
|
|||
<tr ng-repeat="member in members | filter:search | filter: filterFunction(true, false) | orderBy: 'name'">
|
||||
<td class="user entity">
|
||||
<span ng-if="member.kind != 'invite'">
|
||||
<span class="entity-reference" entity="member" namespace="organization.name" show-gravatar="true" gravatar-size="32"></span>
|
||||
<span class="entity-reference" entity="member" namespace="organization.name" show-avatar="true" avatar-size="32"></span>
|
||||
</span>
|
||||
<span class="invite-listing" ng-if="member.kind == 'invite'">
|
||||
<img class="gravatar"ng-src="//www.gravatar.com/avatar/{{ member.gravatar }}?s=32&d=identicon">
|
||||
<span class="avatar" size="32" hash="member.avatar"></span>
|
||||
{{ member.email }}
|
||||
</span>
|
||||
</td>
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
<div class="user-admin container" ng-show="!user.anonymous">
|
||||
<div class="row">
|
||||
<div class="organization-header-element">
|
||||
<img src="//www.gravatar.com/avatar/{{ user.gravatar }}?s=24&d=identicon">
|
||||
<span class="avatar" size="24" hash="user.avatar"></span>
|
||||
<span class="organization-name">
|
||||
{{ user.username }}
|
||||
</span>
|
||||
|
@ -72,7 +72,7 @@
|
|||
|
||||
<tr class="auth-info" ng-repeat="authInfo in authorizedApps">
|
||||
<td>
|
||||
<img src="//www.gravatar.com/avatar/{{ authInfo.gravatar }}?s=16&d=identicon">
|
||||
<span class="avatar" size="16" hash="authInfo.application.avatar"></span>
|
||||
<a href="{{ authInfo.application.url }}" ng-if="authInfo.application.url" target="_blank"
|
||||
data-title="{{ authInfo.application.description || authInfo.application.name }}" bs-tooltip>
|
||||
{{ authInfo.application.name }}
|
||||
|
@ -291,7 +291,7 @@
|
|||
<div class="form-group">
|
||||
<label for="orgName">Organization Name</label>
|
||||
<div class="existing-data">
|
||||
<img src="//www.gravatar.com/avatar/{{ user.gravatar }}?s=24&d=identicon">
|
||||
<span class="avatar" size="24" hash="user.avatar"></span>
|
||||
{{ user.username }}</div>
|
||||
<span class="description">This will continue to be the namespace for your repositories</span>
|
||||
</div>
|
||||
|
|
Reference in a new issue