summaryrefslogtreecommitdiffstats
path: root/app
diff options
context:
space:
mode:
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/application-legacy.js2
-rw-r--r--app/assets/stylesheets/application.css4
-rw-r--r--app/controllers/reactions_controller.rb2
-rw-r--r--app/helpers/avatars_helper.rb1
-rw-r--r--app/helpers/reactions_helper.rb14
-rw-r--r--app/helpers/settings_helper.rb3
-rw-r--r--app/models/reaction.rb19
-rw-r--r--app/models/user.rb17
-rw-r--r--app/views/settings/_display.html.erb22
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