diff options
Diffstat (limited to 'app')
-rw-r--r-- | app/assets/javascripts/application-legacy.js | 2 | ||||
-rw-r--r-- | app/assets/stylesheets/wiki_syntax.css | 11 | ||||
-rw-r--r-- | app/assets/stylesheets/wiki_syntax_detailed.css | 20 | ||||
-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 | 8 | ||||
-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/help/wiki_syntax/common_mark/en/wiki_syntax_common_mark.html.erb | 8 | ||||
-rw-r--r-- | app/views/help/wiki_syntax/common_mark/en/wiki_syntax_detailed_common_mark.html.erb | 48 | ||||
-rw-r--r-- | app/views/settings/_display.html.erb | 22 |
12 files changed, 141 insertions, 20 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/wiki_syntax.css b/app/assets/stylesheets/wiki_syntax.css index 41e780c75..89b117419 100644 --- a/app/assets/stylesheets/wiki_syntax.css +++ b/app/assets/stylesheets/wiki_syntax.css @@ -72,3 +72,14 @@ a:hover, a:active{ color: #c61a1a; text-decoration: underline;} .syntaxhl .s1 { background-color: #fff0f0 } span.more_info { font-weight: normal; } + +.markdown-alert { + border-left: 4px solid; + padding-left: 10px; + margin-left: 10px; +} +.markdown-alert-title { + font-weight: bold; +} +.markdown-alert-note { border-color: #169; } +.markdown-alert-note .markdown-alert-title { color: #1e40af; }
\ No newline at end of file diff --git a/app/assets/stylesheets/wiki_syntax_detailed.css b/app/assets/stylesheets/wiki_syntax_detailed.css index e90279641..ad3c8c65f 100644 --- a/app/assets/stylesheets/wiki_syntax_detailed.css +++ b/app/assets/stylesheets/wiki_syntax_detailed.css @@ -63,3 +63,23 @@ table.list td { background-color: #f5f5f5; vertical-align: middle; padding: 0.3e .syntaxhl .o { color: #333333 } .syntaxhl .s2 { background-color: #fff0f0 } .syntaxhl .si { background-color: #eeeeee } + + +.markdown-alert { + border-left: 4px solid; + padding-left: 10px; + margin-left: 20px; +} +.markdown-alert-title { + font-weight: bold; +} +.markdown-alert-tip { border-color: #5db651; } +.markdown-alert-tip .markdown-alert-title { color: #005f00; } +.markdown-alert-important { border-color: #800080; } +.markdown-alert-important .markdown-alert-title { color: #4b006e; } +.markdown-alert-caution { border-color: #c22; } +.markdown-alert-caution .markdown-alert-title { color: #880000; } +.markdown-alert-warning { border-color: #e4bc4b; } +.markdown-alert-warning .markdown-alert-title { color: #a7760c; } +.markdown-alert-note { border-color: #169; } +.markdown-alert-note .markdown-alert-title { color: #1e40af; }
\ No newline at end of file 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..5c0b807ee 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 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/help/wiki_syntax/common_mark/en/wiki_syntax_common_mark.html.erb b/app/views/help/wiki_syntax/common_mark/en/wiki_syntax_common_mark.html.erb index 486b96424..a650b2751 100644 --- a/app/views/help/wiki_syntax/common_mark/en/wiki_syntax_common_mark.html.erb +++ b/app/views/help/wiki_syntax/common_mark/en/wiki_syntax_common_mark.html.erb @@ -81,6 +81,14 @@ <th></th><td>HTML is <del>not</del> <u>allowed</u>.</td><td>HTML is <del>not</del> <u>allowed</u>.</td> </tr> +<tr><th colspan="3">Alerts <span class="more_info">(<a href="<%= help_wiki_syntax_path(:detailed, anchor: "16") %>" target="_blank">more</a>)</span></th></tr> +<tr><th></th><td>> [!NOTE]<br>> You can use alerts like [!NOTE], [!TIP], [!IMPORTANT], [!WARNING], and [!CAUTION].</td><td> +<div class="markdown-alert markdown-alert-note"> +<p class="markdown-alert-title">Note</p> +<p>You can use alerts like [!NOTE], [!TIP], [!IMPORTANT], [!WARNING], and [!CAUTION].</p> +</div> +</td></tr> + </table> <p><a href="<%= help_wiki_syntax_path(:detailed) %>" onclick="window.open('<%= help_wiki_syntax_path(:detailed) %>', '', ''); return false;">More Information</a></p> diff --git a/app/views/help/wiki_syntax/common_mark/en/wiki_syntax_detailed_common_mark.html.erb b/app/views/help/wiki_syntax/common_mark/en/wiki_syntax_detailed_common_mark.html.erb index 193606ab2..a74094460 100644 --- a/app/views/help/wiki_syntax/common_mark/en/wiki_syntax_detailed_common_mark.html.erb +++ b/app/views/help/wiki_syntax/common_mark/en/wiki_syntax_detailed_common_mark.html.erb @@ -27,6 +27,7 @@ <li><a href='#12'>Macros</a></li> <li><a href='#13'>Code highlighting</a></li> <li><a href='#15'>Raw HTML</a></li> + <li><a href='#16'>Alerts</a></li> </ul> <h2><a name="2" class="wiki-page"></a>Links</h2> @@ -369,5 +370,52 @@ It can be expanded by clicking a link. float </code></pre> + <h2><a name="16" class="wiki-page"></a>Alerts</h2> + + <p> + <dl> + <dt><code>NOTE</code></dt> + <dd> + <pre><code>> [!NOTE]<br>> Wiki page edits are preserved as history, allowing you to restore previous versions if needed.</code></pre> + <div class="markdown-alert markdown-alert-note"> + <p class="markdown-alert-title">Note</p> + <p>Wiki page edits are preserved as history, allowing you to restore previous versions if needed.</p> + </div> + </dd> + <dt><code>TIP</code></dt> + <dd> + <pre><code>> [!TIP]<br>> To quickly review the update history of an issue, use the "History" tab for convenient access.</code></pre> + <div class="markdown-alert markdown-alert-tip"> + <p class="markdown-alert-title">Tip</p> + <p>To quickly review the update history of an issue, use the "History" tab for convenient access.</p> + </div> + </dd> + <dt><code>WARNING</code></dt> + <dd> + <pre><code>> [!WARNING]<br>> Deleting an issue is a permanent action. Be certain it is truly necessary before proceeding.</code></pre> + <div class="markdown-alert markdown-alert-warning"> + <p class="markdown-alert-title">Warning</p> + <p>Deleting an issue is a permanent action. Be certain it is truly necessary before proceeding.</p> + </div> + </dd> + <dt><code>IMPORTANT</code></dt> + <dd> + <pre><code>> [!IMPORTANT]<br>> Changing role permissions can affect user access across all projects, so be mindful of potential impacts.</code></pre> + <div class="markdown-alert markdown-alert-important"> + <p class="markdown-alert-title">Important</p> + <p>Changing role permissions can affect user access across all projects, so be mindful of potential impacts.</p> + </div> + </dd> + <dt><code>CAUTION</code></dt> + <dd> + <pre><code>> [!CAUTION]<br>> When installing plugins, make sure to verify compatibility. Version differences can cause unexpected behavior.</code></pre> + <div class="markdown-alert markdown-alert-caution"> + <p class="markdown-alert-title">Caution</p> + <p>When installing plugins, make sure to verify compatibility. Version differences can cause unexpected behavior.</p> + </div> + </dd> + </dl> + </p> + </body> </html> 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 |