diff options
Diffstat (limited to 'app')
-rw-r--r-- | app/assets/javascripts/application-legacy.js | 2 | ||||
-rw-r--r-- | app/assets/stylesheets/application.css | 4 | ||||
-rw-r--r-- | app/controllers/reactions_controller.rb | 2 | ||||
-rw-r--r-- | app/helpers/avatars_helper.rb | 1 | ||||
-rw-r--r-- | app/helpers/reactions_helper.rb | 14 | ||||
-rw-r--r-- | app/helpers/settings_helper.rb | 3 | ||||
-rw-r--r-- | app/models/reaction.rb | 19 | ||||
-rw-r--r-- | app/models/user.rb | 17 | ||||
-rw-r--r-- | app/views/settings/_display.html.erb | 22 |
9 files changed, 61 insertions, 23 deletions
diff --git a/app/assets/javascripts/application-legacy.js b/app/assets/javascripts/application-legacy.js index 286e3e2e6..deaaa66b6 100644 --- a/app/assets/javascripts/application-legacy.js +++ b/app/assets/javascripts/application-legacy.js @@ -679,7 +679,7 @@ function copyDataClipboardTextToClipboard(target) { } function setupCopyButtonsToPreElements() { - document.querySelectorAll('pre:not(.pre-wrapper pre)').forEach((pre) => { + document.querySelectorAll('.wiki pre:not(.pre-wrapper pre)').forEach((pre) => { // Wrap the <pre> element with a container and add a copy button const wrapper = document.createElement("div"); wrapper.classList.add("pre-wrapper"); diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.css index 40e82b8da..2ce5ea286 100644 --- a/app/assets/stylesheets/application.css +++ b/app/assets/stylesheets/application.css @@ -2116,9 +2116,13 @@ img.filecontent.image {background-image: url(/transparent.png);} .reaction-button.reacted:hover .icon-svg { fill: #c61a1a; } +.reaction-button:hover, .reaction-button:active { + text-decoration: none; +} .reaction-button .icon-label { margin-left: 3px; margin-bottom: -1px; + font-weight: bold; } .reaction-button.readonly { cursor: default; diff --git a/app/controllers/reactions_controller.rb b/app/controllers/reactions_controller.rb index f768f939d..71b37e5f8 100644 --- a/app/controllers/reactions_controller.rb +++ b/app/controllers/reactions_controller.rb @@ -60,6 +60,6 @@ class ReactionsController < ApplicationController end def authorize_reactable - render_403 unless Redmine::Reaction.writable?(@object, User.current) + render_403 unless Redmine::Reaction.editable?(@object, User.current) end end diff --git a/app/helpers/avatars_helper.rb b/app/helpers/avatars_helper.rb index b39427bda..88a571b62 100644 --- a/app/helpers/avatars_helper.rb +++ b/app/helpers/avatars_helper.rb @@ -44,6 +44,7 @@ module AvatarsHelper if user.respond_to?(:mail) email = user.mail options[:title] = user.name unless options[:title] + options[:initials] = user.initials if options[:default] == "initials" elsif user.to_s =~ %r{<(.+?)>} email = $1 end diff --git a/app/helpers/reactions_helper.rb b/app/helpers/reactions_helper.rb index 911da7127..88d7f5c35 100644 --- a/app/helpers/reactions_helper.rb +++ b/app/helpers/reactions_helper.rb @@ -26,15 +26,15 @@ module ReactionsHelper detail = object.reaction_detail - reaction = detail.user_reaction + user_reaction = detail.user_reaction count = detail.reaction_count visible_user_names = detail.visible_users.take(DISPLAY_REACTION_USERS_LIMIT).map(&:name) tooltip = build_reaction_tooltip(visible_user_names, count) - if Redmine::Reaction.writable?(object, User.current) - if reaction&.persisted? - reaction_button_reacted(object, reaction, count, tooltip) + if Redmine::Reaction.editable?(object, User.current) + if user_reaction.present? + reaction_button_reacted(object, user_reaction, count, tooltip) else reaction_button_not_reacted(object, count, tooltip) end @@ -52,7 +52,7 @@ module ReactionsHelper def reaction_button_reacted(object, reaction, count, tooltip) reaction_button_wrapper object do link_to( - sprite_icon('thumb-up-filled', count), + sprite_icon('thumb-up-filled', count.nonzero?), reaction_path(reaction, object_type: object.class.name, object_id: object), remote: true, method: :delete, class: ['icon', 'reaction-button', 'reacted'], @@ -64,7 +64,7 @@ module ReactionsHelper def reaction_button_not_reacted(object, count, tooltip) reaction_button_wrapper object do link_to( - sprite_icon('thumb-up', count), + sprite_icon('thumb-up', count.nonzero?), reactions_path(object_type: object.class.name, object_id: object), remote: true, method: :post, class: 'icon reaction-button', @@ -76,7 +76,7 @@ module ReactionsHelper def reaction_button_readonly(object, count, tooltip) reaction_button_wrapper object do tag.span(class: 'icon reaction-button readonly', title: tooltip) do - sprite_icon('thumb-up', count) + sprite_icon('thumb-up', count.nonzero?) end end end diff --git a/app/helpers/settings_helper.rb b/app/helpers/settings_helper.rb index 39a836a03..c1f989805 100644 --- a/app/helpers/settings_helper.rb +++ b/app/helpers/settings_helper.rb @@ -244,6 +244,7 @@ module SettingsHelper ['Mystery man', 'mm'], ['Retro', 'retro'], ['Robohash', 'robohash'], - ['Wavatars', 'wavatar']] + ['Wavatars', 'wavatar'], + ['Initials', 'initials']] end end diff --git a/app/models/reaction.rb b/app/models/reaction.rb index 84c982043..184ed2d6e 100644 --- a/app/models/reaction.rb +++ b/app/models/reaction.rb @@ -25,39 +25,34 @@ class Reaction < ApplicationRecord scope :by, ->(user) { where(user: user) } scope :for_reactable, ->(reactable) { where(reactable: reactable) } + scope :visible, ->(user) { where(user: User.visible(user)) } # Represents reaction details for a reactable object Detail = Struct.new( - # Total number of reactions - :reaction_count, # Users who reacted and are visible to the target user :visible_users, # Reaction of the target user :user_reaction ) do - def initialize(reaction_count: 0, visible_users: [], user_reaction: nil) + def initialize(visible_users: [], user_reaction: nil) super end + + def reaction_count = visible_users.size end def self.build_detail_map_for(reactables, user) - reactions = preload(:user) + reactions = visible(user) .for_reactable(reactables) + .preload(:user) .select(:id, :reactable_id, :user_id) .order(id: :desc) - # Prepare IDs of users who reacted and are visible to the user - visible_user_ids = User.visible(user) - .joins(:reactions) - .where(reactions: for_reactable(reactables)) - .pluck(:id).to_set - reactions.each_with_object({}) do |reaction, m| m[reaction.reactable_id] ||= Detail.new m[reaction.reactable_id].then do |detail| - detail.reaction_count += 1 - detail.visible_users << reaction.user if visible_user_ids.include?(reaction.user.id) + detail.visible_users << reaction.user detail.user_reaction = reaction if reaction.user == user end end diff --git a/app/models/user.rb b/app/models/user.rb index 9f74a60fb..c1a860f5a 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -28,46 +28,55 @@ class User < Principal USER_FORMATS = { :firstname_lastname => { :string => '#{firstname} #{lastname}', + :initials => '#{firstname.to_s.first}#{lastname.to_s.first}', :order => %w(firstname lastname id), :setting_order => 1 }, :firstname_lastinitial => { :string => '#{firstname} #{lastname.to_s.chars.first}.', + :initials => '#{firstname.to_s.first}#{lastname.to_s.first}', :order => %w(firstname lastname id), :setting_order => 2 }, :firstinitial_lastname => { :string => '#{firstname.to_s.gsub(/(([[:alpha:]])[[:alpha:]]*\.?)/, \'\2.\')} #{lastname}', + :initials => '#{firstname.to_s.gsub(/(([[:alpha:]])[[:alpha:]]*\.?)/, \'\2.\').first}#{lastname.to_s.first}', :order => %w(firstname lastname id), :setting_order => 2 }, :firstname => { :string => '#{firstname}', + :initials => '#{firstname.to_s.first(2)}', :order => %w(firstname id), :setting_order => 3 }, :lastname_firstname => { :string => '#{lastname} #{firstname}', + :initials => '#{lastname.to_s.first}#{firstname.to_s.first}', :order => %w(lastname firstname id), :setting_order => 4 }, :lastnamefirstname => { :string => '#{lastname}#{firstname}', + :initials => '#{lastname.to_s.first}#{firstname.to_s.first}', :order => %w(lastname firstname id), :setting_order => 5 }, :lastname_comma_firstname => { :string => '#{lastname}, #{firstname}', + :initials => '#{lastname.to_s.first}#{firstname.to_s.first}', :order => %w(lastname firstname id), :setting_order => 6 }, :lastname => { :string => '#{lastname}', + :initials => '#{lastname.to_s.first(2)}', :order => %w(lastname id), :setting_order => 7 }, :username => { :string => '#{login}', + :initials => '#{login.to_s.first(2)}', :order => %w(login id), :setting_order => 8 }, @@ -275,6 +284,14 @@ class User < Principal end end + # Return user's initials based on name format + def initials(formatter = nil) + f = self.class.name_formatter(formatter) + format = f[:initials] || USER_FORMATS[:firstname_lastname][:initials] + initials = eval('"' + format + '"') + initials.upcase + end + def registered? self.status == STATUS_REGISTERED end diff --git a/app/views/settings/_display.html.erb b/app/views/settings/_display.html.erb index 62c53dfbb..3b2f95798 100644 --- a/app/views/settings/_display.html.erb +++ b/app/views/settings/_display.html.erb @@ -22,7 +22,12 @@ <p><%= setting_check_box :gravatar_enabled, :data => {:enables => '#settings_gravatar_default'} %> <em class="info"><%= t(:text_avatar_server_config_html, :url => Redmine::Configuration['avatar_server_url']) %></em></p> -<p><%= setting_select :gravatar_default, gravatar_default_setting_options, :blank => :label_none %></p> +<p> + <%= setting_select :gravatar_default, gravatar_default_setting_options, :blank => :label_none %> + <em class="<%= Setting.gravatar_default == "initials" ? "info" : "hidden" %>"> + <%= t(:text_setting_gravatar_default_initials_html) %> + </em> +</p> <p><%= setting_check_box :thumbnails_enabled, :data => {:enables => '#settings_thumbnails_size'} %></p> @@ -35,3 +40,18 @@ <%= submit_tag l(:button_save) %> <% end %> + +<%= javascript_tag do %> + $('#settings_gravatar_default').on('change', function(e){ + const gravatar_default = e.target.value; + const em = e.target.parentElement.getElementsByTagName('em')[0]; + + if (gravatar_default === 'initials') { + em.classList.remove('hidden'); + em.classList.add('info'); + } else { + em.classList.add('hidden'); + em.classList.remove('info'); + } + }); +<% end %>
\ No newline at end of file |