Feature #12197 » 0002-Add-pages-to-change-the-avatar-for-users-and-adminis.patch
| app/assets/images/icons.svg | ||
|---|---|---|
| 92 | 92 |
<symbol viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" id="icon--attachment"> |
| 93 | 93 |
<path d="M15 7l-6.5 6.5a1.5 1.5 0 0 0 3 3l6.5 -6.5a3 3 0 0 0 -6 -6l-6.5 6.5a4.5 4.5 0 0 0 9 9l6.5 -6.5"/> |
| 94 | 94 |
</symbol> |
| 95 |
<symbol viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" id="icon--avatar"> |
|
| 96 |
<path d="M12 13a3 3 0 1 0 0 -6a3 3 0 0 0 0 6"/> |
|
| 97 |
<path d="M12 3c7.2 0 9 1.8 9 9c0 7.2 -1.8 9 -9 9c-7.2 0 -9 -1.8 -9 -9c0 -7.2 1.8 -9 9 -9"/> |
|
| 98 |
<path d="M6 20.05v-.05a4 4 0 0 1 4 -4h4a4 4 0 0 1 4 4v.05"/> |
|
| 99 |
</symbol> |
|
| 95 | 100 |
<symbol viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" id="icon--bookmark-add"> |
| 96 | 101 |
<path d="M12 17l-6 4v-14a4 4 0 0 1 4 -4h4a4 4 0 0 1 4 4v5"/> |
| 97 | 102 |
<path d="M16 19h6"/> |
| app/assets/stylesheets/application.css | ||
|---|---|---|
| 2404 | 2404 |
flex-shrink: 0; |
| 2405 | 2405 |
} |
| 2406 | 2406 |
.avatar.locked, .avatar.registered {opacity: .6;}
|
| 2407 |
.avatar-preview {display: block; margin-block-start: 8px;}
|
|
| 2407 | 2408 |
span[role="img"].avatar {
|
| 2408 | 2409 |
font-family: 'Roboto', 'Helvetica Neue', Helvetica, Arial, sans-serif; |
| ... | ... | |
| 2456 | 2457 |
span[role="img"].avatar.s50 {
|
| 2457 | 2458 |
font-size: calc(50px * .4); |
| 2458 | 2459 |
} |
| 2460 |
.avatar.s100 {
|
|
| 2461 |
block-size: 100px; |
|
| 2462 |
inline-size: 100px; |
|
| 2463 |
} |
|
| 2464 |
span[role="img"].avatar.s100 {
|
|
| 2465 |
font-size: calc(100px * .4); |
|
| 2466 |
} |
|
| 2459 | 2467 |
.avatar-color-0 {
|
| 2460 | 2468 |
background-color: var(--oc-red-7); |
| app/controllers/avatars_controller.rb | ||
|---|---|---|
| 18 | 18 |
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
| 19 | 19 |
class AvatarsController < ApplicationController |
| 20 |
before_action :find_avatar |
|
| 21 |
before_action :check_avatar_available |
|
| 20 |
layout :avatar_layout |
|
| 21 |
self.main_menu = false |
|
| 22 |
menu_item :users |
|
| 23 | ||
| 24 |
before_action :find_avatar, :only => :show |
|
| 25 |
before_action :find_user, :except => :show |
|
| 26 |
before_action :check_avatar_available, :except => :edit |
|
| 22 | 27 |
def show |
| 23 | 28 |
# The original image is never served since it may contain metadata |
| ... | ... | |
| 40 | 45 |
end |
| 41 | 46 |
end |
| 47 |
def edit |
|
| 48 |
end |
|
| 49 | ||
| 50 |
def update |
|
| 51 |
file = params[:avatar] |
|
| 52 |
if !file.respond_to?(:original_filename) |
|
| 53 |
@user.errors.add(:avatar, :blank) |
|
| 54 |
elsif @user.update_avatar(file) |
|
| 55 |
flash[:notice] = l(:notice_successful_update) |
|
| 56 |
redirect_to @avatar_path |
|
| 57 |
return |
|
| 58 |
end |
|
| 59 | ||
| 60 |
render :edit, :status => :unprocessable_content |
|
| 61 |
end |
|
| 62 | ||
| 63 |
def destroy |
|
| 64 |
@user.delete_avatar |
|
| 65 |
flash[:notice] = l(:notice_successful_delete) |
|
| 66 |
redirect_to @avatar_path |
|
| 67 |
end |
|
| 68 | ||
| 42 | 69 |
private |
| 43 | 70 |
def find_avatar |
| ... | ... | |
| 48 | 75 |
render_404 unless @avatar&.readable? |
| 49 | 76 |
end |
| 77 |
# /my/avatar is for the current user and /users/:user_id/avatar is for |
|
| 78 |
# administrators |
|
| 79 |
def find_user |
|
| 80 |
if params[:user_id] |
|
| 81 |
return unless require_admin |
|
| 82 | ||
| 83 |
@user = User.find_by(:id => params[:user_id]) |
|
| 84 |
if @user |
|
| 85 |
@avatar_path = edit_user_avatar_path(@user) |
|
| 86 |
else |
|
| 87 |
render_404 |
|
| 88 |
end |
|
| 89 |
elsif require_login |
|
| 90 |
@user = User.current |
|
| 91 |
@avatar_path = my_avatar_path |
|
| 92 |
end |
|
| 93 |
end |
|
| 94 | ||
| 50 | 95 |
def check_avatar_available |
| 51 | 96 |
render_404 unless User.avatar_available? |
| 52 | 97 |
end |
| 98 | ||
| 99 |
def avatar_layout |
|
| 100 |
params[:user_id] ? 'admin' : 'base' |
|
| 101 |
end |
|
| 53 | 102 |
end |
| app/helpers/avatars_helper.rb | ||
|---|---|---|
| 66 | 66 |
end |
| 67 | 67 |
end |
| 68 |
# Returns a link to edit user's avatar if avatars are enabled |
|
| 68 |
# Returns true if the avatar set in Redmine is displayed for +user+ |
|
| 69 |
def local_avatar?(user) |
|
| 70 |
user.is_a?(User) && user.avatar_id && User.avatar_available? |
|
| 71 |
end |
|
| 72 | ||
| 73 |
# Returns the path to the page to change the avatar of +user+, or nil if |
|
| 74 |
# the current user cannot change it |
|
| 75 |
def avatar_edit_path(user) |
|
| 76 |
if user == User.current |
|
| 77 |
my_avatar_path |
|
| 78 |
elsif User.current.admin? && user.logged? |
|
| 79 |
edit_user_avatar_path(user) |
|
| 80 |
end |
|
| 81 |
end |
|
| 82 | ||
| 83 |
# Returns the avatar of +user+ linked to the page to change it if the |
|
| 84 |
# current user can change it |
|
| 69 | 85 |
def avatar_edit_link(user, options={})
|
| 70 |
if Setting.gravatar_enabled? |
|
| 71 |
url = Redmine::Configuration['avatar_server_url'] |
|
| 72 |
link_to avatar(user, {:title => l(:button_edit)}.merge(options)), url, :target => '_blank'
|
|
| 86 |
if (path = avatar_edit_path(user)) |
|
| 87 |
link_to avatar(user, {:title => l(:button_change_avatar)}.merge(options)), path
|
|
| 88 |
else |
|
| 89 |
avatar(user, options) |
|
| 73 | 90 |
end |
| 74 | 91 |
end |
| 92 |
# Returns a text that explains where the avatar of +user+ comes from |
|
| 93 |
def avatar_source_info(user) |
|
| 94 |
return l(:text_avatar_uploaded) if local_avatar?(user) |
|
| 95 | ||
| 96 |
source = |
|
| 97 |
if Setting.gravatar_enabled? |
|
| 98 |
t(:text_avatar_from_server_html, :url => Redmine::Configuration['avatar_server_url']) |
|
| 99 |
else |
|
| 100 |
l(:text_avatar_initials) |
|
| 101 |
end |
|
| 102 |
upload = User.avatar_available? ? l(:text_avatar_upload_instead) : l(:text_avatar_upload_disabled) |
|
| 103 |
safe_join([source, upload], ' ') |
|
| 104 |
end |
|
| 105 | ||
| 75 | 106 |
def anonymous_avatar(options={})
|
| 76 | 107 |
options[:class] = ['anonymous-avatar', options[:class]].compact.join(' ')
|
| 77 | 108 |
image_tag 'anonymous.png', GravatarHelper::DEFAULT_OPTIONS.except(:default, :rating, :ssl).merge(options) |
| ... | ... | |
| 86 | 117 |
private |
| 87 |
def local_avatar?(user) |
|
| 88 |
user.is_a?(User) && user.avatar_id && User.avatar_available? |
|
| 89 |
end |
|
| 90 | ||
| 91 | 118 |
def local_avatar_tag(user, options) |
| 92 | 119 |
size = (options.delete(:size) || GravatarHelper::DEFAULT_OPTIONS[:size]).to_i |
| 93 | 120 |
options[:title] ||= user.name |
| app/views/avatars/edit.html.erb | ||
|---|---|---|
| 1 |
<% if params[:user_id] %> |
|
| 2 |
<%= title [l(:label_user_plural), users_path], [@user.login, edit_user_path(@user)], l(:field_avatar) %> |
|
| 3 |
<% else %> |
|
| 4 |
<%= title l(:button_change_avatar) %> |
|
| 5 |
<% end %> |
|
| 6 | ||
| 7 |
<%= error_messages_for 'user' %> |
|
| 8 | ||
| 9 |
<div class="box tabular"> |
|
| 10 |
<p> |
|
| 11 |
<label><%= l(:field_avatar) %></label> |
|
| 12 |
<%= avatar_source_info(@user) %> |
|
| 13 |
<span class="avatar-preview"> |
|
| 14 |
<%= avatar(@user, :size => 100) %> |
|
| 15 |
<% if local_avatar?(@user) %> |
|
| 16 |
<span class="buttons"> |
|
| 17 |
<%= link_to_attachment @user.avatar, :text => l(:button_download), :download => true, |
|
| 18 |
:class => 'icon icon-download', :icon => 'download' %> |
|
| 19 |
<%= link_to sprite_icon('del', l(:button_delete)), @avatar_path,
|
|
| 20 |
:method => :delete, :data => {:confirm => l(:text_are_you_sure)}, :class => 'icon icon-del' %>
|
|
| 21 |
</span> |
|
| 22 |
<% end %> |
|
| 23 |
</span> |
|
| 24 |
</p> |
|
| 25 |
<% if User.avatar_available? %> |
|
| 26 |
<hr> |
|
| 27 |
<%= form_tag(@avatar_path, :method => :put, :multipart => true) do %> |
|
| 28 |
<p> |
|
| 29 |
<label for="avatar"><%= l(:label_attachment_new) %> <span class="required">*</span></label> |
|
| 30 |
<%= file_field_tag 'avatar', :required => true, :accept => User::AVATAR_CONTENT_TYPES.join(',') %>
|
|
| 31 |
<%= submit_tag l(:button_update) %> |
|
| 32 |
<em class="info"><%= l(:label_max_size) %>: <%= number_to_human_size(Setting.attachment_max_size.to_i.kilobytes) %></em> |
|
| 33 |
</p> |
|
| 34 |
<% end %> |
|
| 35 |
<% end %> |
|
| 36 |
</div> |
|
| 37 | ||
| 38 |
<% unless params[:user_id] %> |
|
| 39 |
<% content_for :sidebar do %> |
|
| 40 |
<%= render :partial => 'my/sidebar' %> |
|
| 41 |
<% end %> |
|
| 42 |
<% end %> |
|
| app/views/my/account.html.erb | ||
|---|---|---|
| 1 | 1 |
<div class="contextual"> |
| 2 | 2 |
<%= additional_emails_link(@user) %> |
| 3 | 3 |
<%= link_to(sprite_icon('key', l(:button_change_password)), { :action => 'password'}, :class => 'icon icon-passwd') if @user.change_password_allowed? %>
|
| 4 |
<%= link_to(sprite_icon('avatar', l(:button_change_avatar)), my_avatar_path, :class => 'icon icon-avatar') %>
|
|
| 4 | 5 |
<%= link_to sprite_icon('webhook', l(:label_webhook_plural)), webhooks_path, class: 'icon icon-webhook' if Webhook.enabled? && @user.allowed_to?(:use_webhooks, nil, global: true) %>
|
| 5 | 6 |
<%= link_to(sprite_icon('apps', l('label_oauth_authorized_application_plural')), oauth_authorized_applications_path, :class => 'icon icon-applications') if Setting.rest_api_enabled? %>
|
| 6 | 7 |
<%= call_hook(:view_my_account_contextual, :user => @user)%> |
| app/views/settings/_display.html.erb | ||
|---|---|---|
| 25 | 25 |
<% end %></p> |
| 26 | 26 |
<p><%= setting_check_box :gravatar_enabled, :data => {:enables => '#settings_gravatar_default'} %>
|
| 27 |
<em class="info"><%= t(:text_avatar_server_config_html, :url => Redmine::Configuration['avatar_server_url']) %></em></p> |
|
| 27 |
<em class="info"><%= l(:text_setting_gravatar_enabled_fallback) %> |
|
| 28 |
<%= t(:text_avatar_server_config_html, :url => Redmine::Configuration['avatar_server_url']) %></em></p> |
|
| 28 | 29 |
<p> |
| 29 | 30 |
<%= setting_select :gravatar_default, gravatar_default_setting_options, :blank => :label_none %> |
| app/views/users/edit.html.erb | ||
|---|---|---|
| 1 | 1 |
<div class="contextual"> |
| 2 | 2 |
<%= link_to sprite_icon('user', l(:label_profile)), user_path(@user), :class => 'icon icon-user' %>
|
| 3 | 3 |
<%= additional_emails_link(@user) %> |
| 4 |
<%= link_to sprite_icon('avatar', l(:button_change_avatar)), edit_user_avatar_path(@user), :class => 'icon icon-avatar' %>
|
|
| 4 | 5 |
<%= change_status_link(@user) %> |
| 5 | 6 |
<%= delete_link user_path(@user) if User.current != @user %> |
| 6 | 7 |
</div> |
| app/views/users/show.html.erb | ||
|---|---|---|
| 2 | 2 |
<%= link_to(sprite_icon('edit', l(:button_edit)), edit_user_path(@user), :class => 'icon icon-edit') if User.current.admin? && @user.logged? %>
|
| 3 | 3 |
</div> |
| 4 |
<h2><%= avatar @user, :size => "50" %> <%= @user.name %></h2>
|
|
| 4 |
<h2><%= avatar_edit_link(@user, :size => "50") %> <%= @user.name %></h2>
|
|
| 5 | 5 |
<div class="splitcontent"> |
| 6 | 6 |
<div class="splitcontentleft"> |
| config/icon_source.yml | ||
|---|---|---|
| 69 | 69 |
svg: news |
| 70 | 70 |
- name: user |
| 71 | 71 |
svg: user |
| 72 |
- name: avatar |
|
| 73 |
svg: user-square-rounded |
|
| 72 | 74 |
- name: group |
| 73 | 75 |
svg: users-group |
| 74 | 76 |
- name: bookmarked |
| config/locales/en.yml | ||
|---|---|---|
| 1254 | 1254 |
button_reset: Reset |
| 1255 | 1255 |
button_rename: Rename |
| 1256 | 1256 |
button_change_password: Change password |
| 1257 |
button_change_avatar: Change avatar |
|
| 1257 | 1258 |
button_copy: Copy |
| 1258 | 1259 |
button_copy_and_follow: Copy and follow |
| 1259 | 1260 |
button_copy_link: Copy link |
| ... | ... | |
| 1396 | 1397 |
text_select_apply_tracker: "Select tracker" |
| 1397 | 1398 |
text_select_apply_issue_status: "Select issue status" |
| 1398 | 1399 |
text_avatar_server_config_html: The current avatar server is <a href="%{url}">%{url}</a>. You can configure it in config/configuration.yml.
|
| 1400 |
text_avatar_uploaded: The uploaded image is currently used as the avatar. |
|
| 1401 |
text_avatar_from_server_html: The image from <a href="%{url}">%{url}</a> associated with the user's email address is currently used as the avatar. It can be changed on that site.
|
|
| 1402 |
text_avatar_initials: The image generated from the user's name is currently used as the avatar. |
|
| 1403 |
text_avatar_upload_instead: You can upload an image to use instead. |
|
| 1404 |
text_avatar_upload_disabled: Uploading an avatar image to Redmine has been disabled by the administrator. |
|
| 1399 | 1405 |
text_no_subject: no subject |
| 1400 | 1406 |
text_allowed_queries_to_select: Public (to any users) queries only selectable |
| 1401 | 1407 |
text_setting_config_change: You can configure the behaviour in config/configuration.yml. Please restart the application after editing it. |
| 1402 | 1408 |
text_setting_gravatar_default_initials_html: Users' initials are sent to <a href="%{url}">%{url}</a> to generate their avatars.
|
| 1403 | 1409 |
text_setting_avatar_enabled_imagemagick_unavailable: ImageMagick is not available, so users cannot set their avatar. |
| 1410 |
text_setting_gravatar_enabled_fallback: If enabled, Gravatar is used for users who have not set their avatar in Redmine. |
|
| 1404 | 1411 |
text_oauth_admin_permission: Full administrative access. When authorized by an Administrator, this application will be able to read and write all data and impersonate other users. |
| 1405 | 1412 |
text_oauth_admin_permission_info: This application requests full administrative access. If you are an Administrator (or become one in the future), it will be able to read and write all data and impersonate other users on your behalf. If you want to avoid this, authorize it as a user without Administrator privileges instead. |
| 1406 | 1413 |
text_oauth_copy_secret_now: Copy the secret to a safe place now, it will not be shown again. |
| config/routes.rb | ||
|---|---|---|
| 102 | 102 |
post 'my/api_key', :to => 'my#reset_api_key' |
| 103 | 103 |
post 'my/atom_key', :to => 'my#reset_atom_key', :as => 'my_atom_key' |
| 104 | 104 |
match 'my/password', :controller => 'my', :action => 'password', :via => [:get, :post] |
| 105 |
get 'my/avatar', :to => 'avatars#edit', :as => 'my_avatar', :format => false |
|
| 106 |
put 'my/avatar', :to => 'avatars#update', :format => false |
|
| 107 |
delete 'my/avatar', :to => 'avatars#destroy', :format => false |
|
| 105 | 108 |
match 'my/add_block', :controller => 'my', :action => 'add_block', :via => :post |
| 106 | 109 |
match 'my/remove_block', :controller => 'my', :action => 'remove_block', :via => :post |
| 107 | 110 |
match 'my/order_blocks', :controller => 'my', :action => 'order_blocks', :via => :post |
| ... | ... | |
| 130 | 133 |
resources :email_addresses, :only => [:index, :create, :update, :destroy] |
| 131 | 134 |
end |
| 132 | 135 |
get 'users/:user_id/avatar/:id(/:size)', :to => 'avatars#show', :user_id => /\d+/, :id => /\d+/, :size => /\d+/, :as => 'user_avatar' |
| 136 |
get 'users/:user_id/avatar', :to => 'avatars#edit', :user_id => /\d+/, :as => 'edit_user_avatar', :format => false |
|
| 137 |
put 'users/:user_id/avatar', :to => 'avatars#update', :user_id => /\d+/, :format => false |
|
| 138 |
delete 'users/:user_id/avatar', :to => 'avatars#destroy', :user_id => /\d+/, :format => false |
|
| 133 | 139 |
post 'watchers/watch', :to => 'watchers#watch', :as => 'watch' |
| 134 | 140 |
delete 'watchers/watch', :to => 'watchers#unwatch' |
| test/functional/avatars_controller_test.rb | ||
|---|---|---|
| 65 | 65 |
assert_response :redirect |
| 66 | 66 |
end |
| 67 | 67 |
end |
| 68 | ||
| 69 |
def test_edit |
|
| 70 |
Redmine::Thumbnail.stubs(:convert_available?).returns(true) |
|
| 71 |
@request.session[:user_id] = 2 |
|
| 72 |
get :edit |
|
| 73 |
assert_response :success |
|
| 74 |
assert_select 'img.avatar[src=?]', '/users/2/avatar/16/100' |
|
| 75 |
assert_select 'a.icon-download[href=?]', '/attachments/download/16/testfile.png' |
|
| 76 |
assert_select 'a.icon-del[href="/my/avatar"][data-method="delete"]' |
|
| 77 |
assert_select 'form[action="/my/avatar"][enctype="multipart/form-data"] p' do |
|
| 78 |
assert_select 'input[type=file][name=avatar] + input[type=submit][value=Update]' |
|
| 79 |
end |
|
| 80 |
end |
|
| 81 | ||
| 82 |
def test_edit_with_avatars_disabled |
|
| 83 |
@request.session[:user_id] = 2 |
|
| 84 |
with_settings :avatar_enabled => '0' do |
|
| 85 |
get :edit |
|
| 86 |
end |
|
| 87 |
assert_response :success |
|
| 88 |
assert_select 'p', :text => /disabled by the administrator/ |
|
| 89 |
assert_select 'a.icon-del[href="/my/avatar"]', 0 |
|
| 90 |
assert_select 'input[type=file]', 0 |
|
| 91 |
end |
|
| 92 | ||
| 93 |
def test_update_should_return_404_with_avatars_disabled |
|
| 94 |
@request.session[:user_id] = 2 |
|
| 95 |
with_settings :avatar_enabled => '0' do |
|
| 96 |
put :update, :params => {:avatar => fixture_file_upload('2010/11/101123161450_testfile_1.png', 'image/png')}
|
|
| 97 |
end |
|
| 98 |
assert_response :not_found |
|
| 99 |
end |
|
| 100 | ||
| 101 |
def test_edit_with_user_id |
|
| 102 |
Redmine::Thumbnail.stubs(:convert_available?).returns(true) |
|
| 103 |
@request.session[:user_id] = 1 |
|
| 104 |
get :edit, :params => {:user_id => 2}
|
|
| 105 |
assert_response :success |
|
| 106 |
assert_select 'h2 a[href="/users/2/edit"]' |
|
| 107 |
assert_select 'form[action="/users/2/avatar"]' |
|
| 108 |
end |
|
| 109 | ||
| 110 |
def test_edit_with_user_id_should_require_admin |
|
| 111 |
@request.session[:user_id] = 2 |
|
| 112 |
get :edit, :params => {:user_id => 2}
|
|
| 113 |
assert_response :forbidden |
|
| 114 |
end |
|
| 115 | ||
| 116 |
def test_update |
|
| 117 |
Redmine::Thumbnail.stubs(:convert_available?).returns(true) |
|
| 118 |
set_tmp_attachments_directory |
|
| 119 |
@request.session[:user_id] = 2 |
|
| 120 |
put :update, :params => {:avatar => fixture_file_upload('2010/11/101123161450_testfile_1.png', 'image/png')}
|
|
| 121 |
assert_redirected_to '/my/avatar' |
|
| 122 |
assert_equal 'Successful update.', flash[:notice] |
|
| 123 | ||
| 124 |
avatar = User.find(2).avatar |
|
| 125 |
assert_equal '101123161450_testfile_1.png', avatar.filename |
|
| 126 |
assert_equal User.find(2), avatar.author |
|
| 127 |
end |
|
| 128 | ||
| 129 |
def test_destroy |
|
| 130 |
Redmine::Thumbnail.stubs(:convert_available?).returns(true) |
|
| 131 |
set_tmp_attachments_directory |
|
| 132 |
@request.session[:user_id] = 1 |
|
| 133 |
delete :destroy, :params => {:user_id => 2}
|
|
| 134 |
assert_redirected_to '/users/2/avatar' |
|
| 135 |
assert_equal 'Successful deletion.', flash[:notice] |
|
| 136 |
assert_nil User.find(2).avatar_id |
|
| 137 |
end |
|
| 68 | 138 |
end |
| test/functional/my_controller_test.rb | ||
|---|---|---|
| 423 | 423 |
end |
| 424 | 424 |
end |
| 425 |
def test_my_account_with_avatar_enabled_should_link_to_edit_avatar
|
|
| 425 |
def test_my_account_should_link_to_avatar_page
|
|
| 426 | 426 |
with_settings :gravatar_enabled => '1' do |
| 427 |
Redmine::Configuration.with 'avatar_server_url' => 'https://gravatar.com' do |
|
| 428 |
get :account |
|
| 429 |
assert_response :success |
|
| 430 |
assert_select 'a[href=?] img.gravatar', 'https://gravatar.com' |
|
| 431 |
end |
|
| 427 |
get :account |
|
| 428 |
assert_response :success |
|
| 429 |
assert_select 'h2 a[href="/my/avatar"] img.gravatar' |
|
| 432 | 430 |
end |
| 433 | 431 |
end |
| test/functional/users_controller_test.rb | ||
|---|---|---|
| 402 | 402 |
get :show, :params => {:id => 'current'}
|
| 403 | 403 |
assert_response :success |
| 404 | 404 |
assert_select 'h2', :text => /John Smith/ |
| 405 |
assert_select 'h2 a[href="/my/avatar"] .avatar' |
|
| 405 | 406 |
end |
| 406 | 407 |
def test_show_issues_counts |
| test/helpers/avatars_helper_test.rb | ||
|---|---|---|
| 50 | 50 |
assert_include Digest::SHA256.hexdigest('jsmith@somenet.foo'), avatar(user)
|
| 51 | 51 |
end |
| 52 |
def test_avatar_source_info |
|
| 53 |
Redmine::Thumbnail.stubs(:convert_available?).returns(true) |
|
| 54 |
user = User.find(2) |
|
| 55 |
assert_include 'It can be changed on that site. You can upload an image to use instead.', avatar_source_info(user) |
|
| 56 |
with_settings :gravatar_enabled => '0' do |
|
| 57 |
assert_include 'generated from the user', avatar_source_info(user) |
|
| 58 |
end |
|
| 59 |
user.avatar_id = 16 |
|
| 60 |
assert_equal 'The uploaded image is currently used as the avatar.', avatar_source_info(user) |
|
| 61 |
with_settings :avatar_enabled => '0' do |
|
| 62 |
assert_include 'Uploading an avatar image to Redmine has been disabled by the administrator.', avatar_source_info(user) |
|
| 63 |
end |
|
| 64 |
end |
|
| 65 | ||
| 52 | 66 |
def test_avatar_with_email_string |
| 53 | 67 |
assert_include Digest::SHA256.hexdigest('jsmith@somenet.foo'), avatar('jsmith <jsmith@somenet.foo>')
|
| 54 | 68 |
end |
| test/integration/routing/my_test.rb | ||
|---|---|---|
| 38 | 38 |
should_route 'GET /my/password' => 'my#password' |
| 39 | 39 |
should_route 'POST /my/password' => 'my#password' |
| 40 |
should_route 'GET /my/avatar' => 'avatars#edit' |
|
| 41 |
should_route 'PUT /my/avatar' => 'avatars#update' |
|
| 42 |
should_route 'DELETE /my/avatar' => 'avatars#destroy' |
|
| 43 | ||
| 40 | 44 |
should_route 'POST /my/add_block' => 'my#add_block' |
| 41 | 45 |
should_route 'POST /my/remove_block' => 'my#remove_block' |
| 42 | 46 |
should_route 'POST /my/order_blocks' => 'my#order_blocks' |
| test/integration/routing/users_test.rb | ||
|---|---|---|
| 35 | 35 |
should_route 'POST /users/bulk_lock' => 'users#bulk_lock' |
| 36 | 36 |
should_route 'POST /users/bulk_unlock' => 'users#bulk_unlock' |
| 37 |
should_route 'GET /users/44/avatar' => 'avatars#edit', :user_id => '44' |
|
| 38 |
should_route 'PUT /users/44/avatar' => 'avatars#update', :user_id => '44' |
|
| 39 |
should_route 'DELETE /users/44/avatar' => 'avatars#destroy', :user_id => '44' |
|
| 37 | 40 |
should_route 'GET /users/44/avatar/2/48' => 'avatars#show', :user_id => '44', :id => '2', :size => '48' |
| 38 | 41 |
end |
| 39 | 42 |
end |