mirror of
https://github.com/redmine/redmine.git
synced 2025-10-26 07:46:17 +01:00
When the Gravatar service is disabled, display the user's initials as a fallback avatar. This provides a consistent user interface than the generic icon (#29824).
git-svn-id: https://svn.redmine.org/redmine/trunk@23903 e93f8b46-1217-0410-a6f0-8f06a7374b81
This commit is contained in:
@@ -433,7 +433,7 @@ tr.entry.file td.filename a { margin-left: 26px; }
|
||||
tr.entry.file td.filename_no_report a { margin-left: 16px; }
|
||||
|
||||
tr span.expander, .gantt_subjects div > span.expander {margin-left: 0; cursor: pointer;}
|
||||
.gantt_subjects div > span .icon-gravatar {float: none;}
|
||||
.gantt_subjects .avatar {margin-right: 4px;}
|
||||
.gantt_subjects div.project-name a, .gantt_subjects div.version-name a {margin-left: 4px;}
|
||||
|
||||
tr.changeset { height: 20px }
|
||||
@@ -461,7 +461,7 @@ tr.version:not(.shared) td.name { padding-left: 20px; }
|
||||
tr.version td.date, tr.version td.status, tr.version td.sharing { text-align: center; white-space:nowrap; }
|
||||
|
||||
#principals_for_new_member .icon-user, #users_for_watcher .icon-user {background:transparent;}
|
||||
#principals_for_new_member svg, #principals_for_new_member img {margin-right: 4px;}
|
||||
#principals_for_new_member svg, #principals_for_new_member .avatar {margin-right: 4px;}
|
||||
|
||||
tr.user td {width:13%;white-space: nowrap;}
|
||||
td.username, td.firstname, td.lastname, td.email {text-align:left !important;}
|
||||
@@ -565,9 +565,9 @@ body.controller-gantts fieldset#options > div > div {
|
||||
td.center {text-align:center;}
|
||||
|
||||
#watchers select {width: 95%; display: block;}
|
||||
#watchers img.gravatar {margin: 0 4px 2px 0;}
|
||||
#watchers .avatar {margin: 0 4px 2px 0;}
|
||||
#watchers svg.icon-svg {margin: 0 2px 2px 0;}
|
||||
#users_for_watcher img.gravatar {padding-bottom: 2px; margin-right: 4px;}
|
||||
#users_for_watcher .avatar {padding-bottom: 2px; margin-right: 4px;}
|
||||
#users_for_watcher svg {margin-right: 4px;}
|
||||
#users_for_watcher span.icon-user {display: inline;}
|
||||
|
||||
@@ -1439,7 +1439,7 @@ p.cal.legend span {display:flex;}
|
||||
.tooltip span.tip{display: none; text-align:left;}
|
||||
.tooltip span.tip a { color: #169 !important; }
|
||||
|
||||
.tooltip span.tip img.gravatar {
|
||||
.tooltip span.tip .avatar {
|
||||
float: none;
|
||||
margin: 0;
|
||||
}
|
||||
@@ -1794,9 +1794,6 @@ table.gantt-table td {
|
||||
}
|
||||
.gantt_subjects div.issue-subject:hover { background-color:#ffffdd; }
|
||||
.gantt_selected_column_content > div { padding-left: 3px; box-sizing: border-box; }
|
||||
.gantt_subjects .issue-subject img.icon-gravatar {
|
||||
margin: 2px 5px 0px 2px;
|
||||
}
|
||||
|
||||
.gantt_hdr_selected_column_name {
|
||||
position: absolute;
|
||||
@@ -2184,21 +2181,16 @@ tr.ui-sortable-helper { border:1px solid #e4e4e4; }
|
||||
|
||||
.contextual>*:not(:first-child), .buttons>.icon:not(:first-child), .contextual .journal-actions>*:not(:first-child) { margin-left: 5px; }
|
||||
|
||||
img.gravatar {
|
||||
vertical-align: middle;
|
||||
border-radius: 20%;
|
||||
}
|
||||
|
||||
div.issue img.gravatar {
|
||||
div.issue .avatar {
|
||||
float: left;
|
||||
margin: 0 12px 6px 0;
|
||||
}
|
||||
|
||||
div.gravatar-with-child {
|
||||
div.avatar-with-child {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
div.gravatar-with-child > img.gravatar:nth-child(2) {
|
||||
div.avatar-with-child > .avatar:nth-child(2) {
|
||||
position: absolute;
|
||||
top: 30px;
|
||||
left: 30px;
|
||||
@@ -2206,11 +2198,11 @@ div.gravatar-with-child > img.gravatar:nth-child(2) {
|
||||
border: 2px solid rgba(255, 255, 255, 0.9);
|
||||
}
|
||||
|
||||
h2 img.gravatar, h3 img.gravatar {margin-right: 4px;}
|
||||
h2 .avatar, h3 .avatar {margin-right: 4px;}
|
||||
h4 img.gravatar {margin: -2px 4px -4px 0;}
|
||||
/*# TODO: check where this rule is still used*/
|
||||
td.username img.gravatar {margin: 0 0.5em 0 0; vertical-align: top;}
|
||||
#activity dt img.gravatar {margin: 0 1em 0 0;}
|
||||
/* Used on 12px Gravatar img tags without the icon background */
|
||||
.icon-gravatar {float: left; margin-right: 4px;}
|
||||
#activity dt .avatar {margin: 0 1em 0 0;}
|
||||
|
||||
#activity dt, .journal {clear: left;}
|
||||
|
||||
@@ -2233,6 +2225,98 @@ color: #555; text-shadow: 1px 1px 0 #fff;
|
||||
|
||||
img.filecontent.image {background-image: url(/transparent.png);}
|
||||
|
||||
/* Avatar styles */
|
||||
.avatar {
|
||||
border-radius: 20%;
|
||||
display: inline-flex;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
span[role="img"].avatar {
|
||||
font-family: 'Roboto', 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
||||
align-items: center;
|
||||
display: inline-flex;
|
||||
font-size: calc(24px * .4);
|
||||
justify-content: center;
|
||||
user-select: none;
|
||||
font-weight: 700;
|
||||
}
|
||||
.avatar.s13 {
|
||||
block-size: 13px;
|
||||
inline-size: 13px;
|
||||
}
|
||||
span[role="img"].avatar.s13 {
|
||||
font-size: calc(16px * .3);
|
||||
}
|
||||
.avatar.s16 {
|
||||
block-size: 16px;
|
||||
inline-size: 16px;
|
||||
}
|
||||
span[role="img"].avatar.s16 {
|
||||
font-size: calc(16px * .4);
|
||||
}
|
||||
.avatar.s22 {
|
||||
block-size: 22px;
|
||||
inline-size: 22px;
|
||||
}
|
||||
span[role="img"].avatar.s22 {
|
||||
font-size: calc(22px * .4);
|
||||
}
|
||||
.avatar.s24 {
|
||||
block-size: 24px;
|
||||
inline-size: 24px;
|
||||
}
|
||||
span[role="img"].avatar.s24 {
|
||||
font-size: calc(24px * .4);
|
||||
}
|
||||
.avatar.s40 {
|
||||
block-size: 40px;
|
||||
inline-size: 40px;
|
||||
}
|
||||
span[role="img"].avatar.s40 {
|
||||
font-size: calc(40px * .4);
|
||||
}
|
||||
.avatar.s50 {
|
||||
block-size: 50px;
|
||||
inline-size: 50px;
|
||||
}
|
||||
span[role="img"].avatar.s50 {
|
||||
font-size: calc(50px * .4);
|
||||
}
|
||||
|
||||
.avatar-color-0 {
|
||||
background-color: #880000;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
.avatar-color-1 {
|
||||
background-color: #ff0000;
|
||||
color: #000000;
|
||||
}
|
||||
.avatar-color-2 {
|
||||
background-color: #00ff00;
|
||||
color: #000000;
|
||||
}
|
||||
.avatar-color-3 {
|
||||
background-color: #008800;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
.avatar-color-4 {
|
||||
background-color: #0000ff;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
.avatar-color-5 {
|
||||
background-color: #000088;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
.avatar-color-6 {
|
||||
background-color: #ff8800;
|
||||
color: #000000;
|
||||
}
|
||||
.avatar-color-7 {
|
||||
background-color: #ff0088;
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
/* Reaction styles */
|
||||
.reaction-button:hover, .reaction-button:active {
|
||||
text-decoration: none;
|
||||
|
||||
@@ -57,6 +57,11 @@ module AvatarsHelper
|
||||
else
|
||||
nil
|
||||
end
|
||||
elsif user.respond_to?(:initials)
|
||||
size = options.delete(:size) || GravatarHelper::DEFAULT_OPTIONS[:size]
|
||||
css_class = "avatar-color-#{user.id % 8} avatar s#{size}"
|
||||
css_class += " #{options[:class]}" if options[:class]
|
||||
content_tag('span', user.initials, role: 'img', class: css_class)
|
||||
else
|
||||
''
|
||||
end
|
||||
|
||||
@@ -53,6 +53,10 @@ class Group < Principal
|
||||
name.to_s
|
||||
end
|
||||
|
||||
def initials
|
||||
name[0, 1]
|
||||
end
|
||||
|
||||
def builtin_type
|
||||
nil
|
||||
end
|
||||
|
||||
@@ -28,9 +28,9 @@
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<div class="gravatar-with-child">
|
||||
<div class="avatar-with-child">
|
||||
<%= author_avatar(@issue.author, :size => "50") %>
|
||||
<%= assignee_avatar(@issue.assigned_to, :size => "22", :class => "gravatar-child") if @issue.assigned_to %>
|
||||
<%= assignee_avatar(@issue.assigned_to, :size => "22", :class => "avatar-child") if @issue.assigned_to %>
|
||||
</div>
|
||||
|
||||
<div data-controller="sticky-issue-header">
|
||||
|
||||
@@ -36,11 +36,9 @@
|
||||
<% end %>
|
||||
|
||||
<% if User.current.logged? %>
|
||||
<div class="flyout-menu__avatar <% if !Setting.gravatar_enabled? %>flyout-menu__avatar--no-avatar<% end %>">
|
||||
<% if Setting.gravatar_enabled? %>
|
||||
<%= link_to(avatar(User.current, :size => "80"), user_path(User.current)) %>
|
||||
<% end %>
|
||||
<%= link_to_user(User.current, :format => :username) %>
|
||||
<div class="flyout-menu__avatar">
|
||||
<%= link_to(avatar(User.current, :size => "40"), user_path(User.current)) %>
|
||||
<%= link_to_user(User.current, :format => :username) %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ module GravatarHelper
|
||||
:title => '',
|
||||
|
||||
# The class to assign to the img tag for the gravatar.
|
||||
:class => 'gravatar',
|
||||
:class => 'gravatar avatar',
|
||||
}
|
||||
|
||||
# The methods that will be made available to your views.
|
||||
|
||||
@@ -731,7 +731,7 @@ module Redmine
|
||||
css_classes = +''
|
||||
css_classes << ' issue-overdue' if issue.overdue?
|
||||
css_classes << ' issue-behind-schedule' if issue.behind_schedule?
|
||||
css_classes << ' icon icon-issue' unless Setting.gravatar_enabled? && issue.assigned_to
|
||||
css_classes << ' icon icon-issue' unless issue.assigned_to
|
||||
css_classes << ' issue-closed' if issue.closed?
|
||||
if issue.start_date && issue.due_before && issue.done_ratio
|
||||
progress_date = calc_progress_date(issue.start_date,
|
||||
@@ -740,8 +740,8 @@ module Redmine
|
||||
css_classes << ' over-end-date' if progress_date > self.date_to && issue.done_ratio > 0
|
||||
end
|
||||
s = (+"").html_safe
|
||||
s << view.sprite_icon('issue').html_safe unless Setting.gravatar_enabled? && issue.assigned_to
|
||||
s << view.assignee_avatar(issue.assigned_to, :size => 13, :class => 'icon-gravatar')
|
||||
s << view.sprite_icon('issue').html_safe unless issue.assigned_to
|
||||
s << view.assignee_avatar(issue.assigned_to, :size => 13, :class => 'icon-avatar')
|
||||
s << view.link_to_issue(issue).html_safe
|
||||
s << view.content_tag(:input, nil, :type => 'checkbox', :name => 'ids[]',
|
||||
:value => issue.id, :style => 'display:none;',
|
||||
|
||||
@@ -57,7 +57,7 @@ class CalendarsControllerTest < Redmine::ControllerTest
|
||||
) do
|
||||
assert_select 'a.issue[href=?]', '/issues/2', :text => 'Feature request #2'
|
||||
assert_select 'span.tip' do
|
||||
assert_select 'img[class="gravatar"]'
|
||||
assert_select 'img[class="gravatar avatar"]'
|
||||
end
|
||||
assert_select 'input[name=?][type=?][value=?]', 'ids[]', 'checkbox', '2'
|
||||
end
|
||||
|
||||
@@ -58,7 +58,7 @@ class GanttsControllerTest < Redmine::ControllerTest
|
||||
# Assert context menu on issues subject and gantt bar
|
||||
assert_select 'div[class=?]', 'issue-subject hascontextmenu'
|
||||
assert_select 'div.tooltip.hascontextmenu' do
|
||||
assert_select 'img[class="gravatar"]'
|
||||
assert_select 'img[class="gravatar avatar"]'
|
||||
end
|
||||
assert_select "form[data-cm-url=?]", '/issues/context_menu'
|
||||
|
||||
|
||||
@@ -2816,7 +2816,7 @@ class IssuesControllerTest < Redmine::ControllerTest
|
||||
assert_select 'h3', {text: /Watchers \(\d*\)/, count: 0}
|
||||
end
|
||||
|
||||
def test_show_should_display_watchers_with_gravatars
|
||||
def test_show_should_display_watchers_with_avatars
|
||||
@request.session[:user_id] = 2
|
||||
issue = Issue.find(1)
|
||||
issue.add_watcher User.find(2)
|
||||
@@ -2824,9 +2824,10 @@ class IssuesControllerTest < Redmine::ControllerTest
|
||||
with_settings :gravatar_enabled => '1' do
|
||||
get(:show, :params => {:id => 1})
|
||||
end
|
||||
|
||||
assert_select 'div#watchers ul' do
|
||||
assert_select 'li.user-2' do
|
||||
assert_select 'img.gravatar[title=?]', 'John Smith'
|
||||
assert_select '.avatar[title=?]', 'John Smith'
|
||||
assert_select 'a[href="/users/2"]'
|
||||
assert_select 'a[class*=delete]'
|
||||
end
|
||||
@@ -8786,31 +8787,27 @@ class IssuesControllerTest < Redmine::ControllerTest
|
||||
assert_select 'a[href=?][onclick=?]', "/issues/1", "", :text => 'Cancel'
|
||||
end
|
||||
|
||||
def test_show_should_display_author_gravatar_only_when_not_assigned
|
||||
def test_show_should_display_author_avatar_only_when_not_assigned
|
||||
issue = Issue.find(1)
|
||||
assert_nil issue.assigned_to_id
|
||||
@request.session[:user_id] = 1
|
||||
|
||||
with_settings :gravatar_enabled => '1' do
|
||||
get :show, :params => {:id => issue.id}
|
||||
assert_select 'div.gravatar-with-child' do
|
||||
assert_select 'img.gravatar', 1
|
||||
end
|
||||
get :show, :params => {:id => issue.id}
|
||||
assert_select 'div.avatar-with-child' do
|
||||
assert_select '.avatar', 1
|
||||
end
|
||||
end
|
||||
|
||||
def test_show_should_display_author_and_assignee_gravatars_when_assigned
|
||||
def test_show_should_display_author_and_assignee_avatars_when_assigned
|
||||
issue = Issue.find(1)
|
||||
issue.assigned_to_id = 2
|
||||
issue.save!
|
||||
@request.session[:user_id] = 1
|
||||
|
||||
with_settings :gravatar_enabled => '1' do
|
||||
get :show, :params => {:id => issue.id}
|
||||
assert_select 'div.gravatar-with-child' do
|
||||
assert_select 'img.gravatar', 2
|
||||
assert_select 'img.gravatar-child', 1
|
||||
end
|
||||
get :show, :params => {:id => issue.id}
|
||||
assert_select 'div.avatar-with-child' do
|
||||
assert_select '.avatar', 2
|
||||
assert_select '.avatar-child', 1
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ class MessagesControllerTest < Redmine::ControllerTest
|
||||
get(:show, :params => {:board_id => 1, :id => 1})
|
||||
assert_response :success
|
||||
|
||||
assert_select 'h2', :text => 'First post'
|
||||
assert_select 'h2', :text => "RAFirst post"
|
||||
end
|
||||
|
||||
def test_show_should_contain_reply_field_tags_for_quoting
|
||||
|
||||
@@ -75,7 +75,7 @@ class NewsControllerTest < Redmine::ControllerTest
|
||||
get(:show, :params => {:id => 1})
|
||||
assert_response :success
|
||||
assert_select 'p.breadcrumb a[href=?]', '/projects/ecookbook/news', :text => 'News'
|
||||
assert_select 'h2', :text => 'eCookbook first release !'
|
||||
assert_select 'h2', :text => 'JS eCookbook first release !'
|
||||
end
|
||||
|
||||
def test_show_should_show_attachments
|
||||
|
||||
@@ -2053,20 +2053,6 @@ class ApplicationHelperTest < Redmine::HelperTest
|
||||
end
|
||||
end
|
||||
|
||||
def test_principals_check_box_tag_without_avatar
|
||||
principals = [User.find(1), Group.find(10)]
|
||||
Setting.gravatar_enabled = '1'
|
||||
avatar_tags = principals.collect{|p| avatar(p, :size => 16)}
|
||||
|
||||
with_settings :gravatar_enabled => '0' do
|
||||
tags = principals_check_box_tags(name, principals)
|
||||
principals.each_with_index do |principal, i|
|
||||
assert_not_include avatar_tags[i], tags
|
||||
assert_include content_tag('span', principal_icon(principal), :class => "name icon icon-#{principal.class.name.downcase}"), tags
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_principals_options_for_select_with_users
|
||||
User.current = nil
|
||||
users = [User.find(2), User.find(4)]
|
||||
|
||||
@@ -63,9 +63,9 @@ class AvatarsHelperTest < Redmine::HelperTest
|
||||
end
|
||||
|
||||
def test_avatar_css_class
|
||||
# The default class of the img tag should be gravatar
|
||||
assert_include 'class="gravatar"', avatar('jsmith <jsmith@somenet.foo>')
|
||||
assert_include 'class="gravatar picture"', avatar('jsmith <jsmith@somenet.foo>', :class => 'picture')
|
||||
# The default classes of the img tag should be gravatar and avatar
|
||||
assert_include 'class="gravatar avatar"', avatar('jsmith <jsmith@somenet.foo>')
|
||||
assert_include 'class="gravatar avatar picture"', avatar('jsmith <jsmith@somenet.foo>', :class => 'picture')
|
||||
end
|
||||
|
||||
def test_avatar_with_initials
|
||||
@@ -80,9 +80,9 @@ class AvatarsHelperTest < Redmine::HelperTest
|
||||
end
|
||||
end
|
||||
|
||||
def test_avatar_disabled
|
||||
def test_avatar_disabled_should_display_user_initials
|
||||
with_settings :gravatar_enabled => '0' do
|
||||
assert_equal '', avatar(User.find_by_mail('jsmith@somenet.foo'))
|
||||
assert_equal "<span role=\"img\" class=\"avatar-color-2 avatar s24\">JS</span>", avatar(User.find_by_mail('jsmith@somenet.foo'))
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -127,20 +127,23 @@ class Redmine::ApiTest::AuthenticationTest < Redmine::ApiTest::Base
|
||||
assert_response :unauthorized
|
||||
end
|
||||
|
||||
# TODO: check why this test does not use the API endpoint
|
||||
def test_api_should_accept_switch_user_header_for_admin_user
|
||||
user = User.find(1)
|
||||
su = User.find(4)
|
||||
|
||||
get '/users/current', :headers => {'X-Redmine-API-Key' => user.api_key, 'X-Redmine-Switch-User' => su.login}
|
||||
assert_response :success
|
||||
assert_select 'h2', :text => su.name
|
||||
assert_select 'h2', :text => "#{su.initials} #{su.name}"
|
||||
end
|
||||
|
||||
# TODO: check why this test does not use the API endpoint
|
||||
def test_api_should_respond_with_412_when_trying_to_switch_to_a_invalid_user
|
||||
get '/users/current', :headers => {'X-Redmine-API-Key' => User.find(1).api_key, 'X-Redmine-Switch-User' => 'foobar'}
|
||||
assert_response :precondition_failed
|
||||
end
|
||||
|
||||
# TODO: check why this test does not use the API endpoint
|
||||
def test_api_should_respond_with_412_when_trying_to_switch_to_a_locked_user
|
||||
user = User.find(5)
|
||||
assert user.locked?
|
||||
@@ -149,12 +152,13 @@ class Redmine::ApiTest::AuthenticationTest < Redmine::ApiTest::Base
|
||||
assert_response :precondition_failed
|
||||
end
|
||||
|
||||
# TODO: check why this test does not use the API endpoint
|
||||
def test_api_should_not_accept_switch_user_header_for_non_admin_user
|
||||
user = User.find(2)
|
||||
su = User.find(4)
|
||||
|
||||
get '/users/current', :headers => {'X-Redmine-API-Key' => user.api_key, 'X-Redmine-Switch-User' => su.login}
|
||||
assert_response :success
|
||||
assert_select 'h2', :text => user.name
|
||||
assert_select 'h2', :text => "#{user.initials} #{user.name}"
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user