diff options
Diffstat (limited to 'app/models')
-rw-r--r-- | app/models/issue.rb | 2 | ||||
-rw-r--r-- | app/models/reaction.rb | 19 | ||||
-rw-r--r-- | app/models/role.rb | 26 | ||||
-rw-r--r-- | app/models/user.rb | 36 | ||||
-rw-r--r-- | app/models/user_preference.rb | 2 |
5 files changed, 61 insertions, 24 deletions
diff --git a/app/models/issue.rb b/app/models/issue.rb index bfef3533a..576840843 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -1175,7 +1175,7 @@ class Issue < ApplicationRecord if leaf? spent_hours else - self_and_descendants.joins(:time_entries).sum("#{TimeEntry.table_name}.hours").to_f || 0.0 + self_and_descendants.joins(:time_entries).sum("#{TimeEntry.table_name}.hours").to_f 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/role.rb b/app/models/role.rb index 3ca4f92a1..870bbe945 100644 --- a/app/models/role.rb +++ b/app/models/role.rb @@ -198,11 +198,14 @@ class Role < ApplicationRecord # action can be: # * a parameter-like Hash (eg. :controller => 'projects', :action => 'edit') # * a permission Symbol (eg. :edit_project) - def allowed_to?(action) + # scope can be: + # * an array of permissions which will be used as filter (logical AND) + + def allowed_to?(action, scope=nil) if action.is_a? Hash - allowed_actions.include? "#{action[:controller]}/#{action[:action]}" + allowed_actions(scope).include? "#{action[:controller]}/#{action[:action]}" else - allowed_permissions.include? action + allowed_permissions(scope).include? action end end @@ -298,13 +301,20 @@ class Role < ApplicationRecord private - def allowed_permissions - @allowed_permissions ||= permissions + Redmine::AccessControl.public_permissions.collect {|p| p.name} + def allowed_permissions(scope = nil) + scope = scope.sort if scope.present? # to maintain stable cache keys + @allowed_permissions ||= {} + @allowed_permissions[scope] ||= begin + unscoped = permissions + Redmine::AccessControl.public_permissions.collect {|p| p.name} + scope.present? ? unscoped & scope : unscoped + end end - def allowed_actions - @actions_allowed ||= - allowed_permissions.inject([]) do |actions, permission| + def allowed_actions(scope = nil) + scope = scope.sort if scope.present? # to maintain stable cache keys + @actions_allowed ||= {} + @actions_allowed[scope] ||= + allowed_permissions(scope).inject([]) do |actions, permission| actions += Redmine::AccessControl.allowed_actions(permission) end.flatten end diff --git a/app/models/user.rb b/app/models/user.rb index 9f74a60fb..496084ceb 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 }, @@ -103,6 +112,7 @@ class User < Principal attr_accessor :password, :password_confirmation, :generate_password attr_accessor :last_before_login_on attr_accessor :remote_ip + attr_writer :oauth_scope LOGIN_LENGTH_LIMIT = 60 MAIL_LENGTH_LIMIT = 254 @@ -275,6 +285,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 @@ -715,6 +733,20 @@ class User < Principal end end + def admin? + if authorized_by_oauth? + # when signed in via oauth, the user only acts as admin when the admin scope is set + super and @oauth_scope.include?(:admin) + else + super + end + end + + # true if the user has signed in via oauth + def authorized_by_oauth? + !@oauth_scope.nil? + end + # Return true if the user is allowed to do the specified action on a specific context # Action can be: # * a parameter-like Hash (eg. :controller => 'projects', :action => 'edit') @@ -735,7 +767,7 @@ class User < Principal roles.any? do |role| (context.is_public? || role.member?) && - role.allowed_to?(action) && + role.allowed_to?(action, @oauth_scope) && (block ? yield(role, self) : true) end elsif context && context.is_a?(Array) @@ -754,7 +786,7 @@ class User < Principal # authorize if user has at least one role that has this permission roles = self.roles.to_a | [builtin_role] roles.any? do |role| - role.allowed_to?(action) && + role.allowed_to?(action, @oauth_scope) && (block ? yield(role, self) : true) end else diff --git a/app/models/user_preference.rb b/app/models/user_preference.rb index 8b19d9a5a..e1842b131 100644 --- a/app/models/user_preference.rb +++ b/app/models/user_preference.rb @@ -73,7 +73,7 @@ class UserPreference < ApplicationRecord if has_attribute? attr_name super else - others ? others[attr_name] : nil + others&.[](attr_name) end end |