summaryrefslogtreecommitdiffstats
path: root/app/models
diff options
context:
space:
mode:
Diffstat (limited to 'app/models')
-rw-r--r--app/models/issue.rb2
-rw-r--r--app/models/reaction.rb19
-rw-r--r--app/models/role.rb26
-rw-r--r--app/models/user.rb36
-rw-r--r--app/models/user_preference.rb2
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