diff options
Diffstat (limited to 'app/models')
-rw-r--r-- | app/models/comment.rb | 8 | ||||
-rw-r--r-- | app/models/custom_field.rb | 7 | ||||
-rw-r--r-- | app/models/email_address.rb | 6 | ||||
-rw-r--r-- | app/models/group.rb | 4 | ||||
-rw-r--r-- | app/models/issue.rb | 23 | ||||
-rw-r--r-- | app/models/issue_relation.rb | 4 | ||||
-rw-r--r-- | app/models/journal.rb | 5 | ||||
-rw-r--r-- | app/models/mail_handler.rb | 4 | ||||
-rw-r--r-- | app/models/member.rb | 4 | ||||
-rw-r--r-- | app/models/message.rb | 2 | ||||
-rw-r--r-- | app/models/news.rb | 2 | ||||
-rw-r--r-- | app/models/principal.rb | 7 | ||||
-rw-r--r-- | app/models/project.rb | 8 | ||||
-rw-r--r-- | app/models/reaction.rb | 60 | ||||
-rw-r--r-- | app/models/repository.rb | 8 | ||||
-rw-r--r-- | app/models/repository/bazaar.rb | 4 | ||||
-rw-r--r-- | app/models/repository/cvs.rb | 4 | ||||
-rw-r--r-- | app/models/repository/filesystem.rb | 4 | ||||
-rw-r--r-- | app/models/repository/git.rb | 4 | ||||
-rw-r--r-- | app/models/repository/mercurial.rb | 4 | ||||
-rw-r--r-- | app/models/role.rb | 26 | ||||
-rw-r--r-- | app/models/user.rb | 50 | ||||
-rw-r--r-- | app/models/user_preference.rb | 2 | ||||
-rw-r--r-- | app/models/version.rb | 10 |
24 files changed, 194 insertions, 66 deletions
diff --git a/app/models/comment.rb b/app/models/comment.rb index 79eb59748..1716537af 100644 --- a/app/models/comment.rb +++ b/app/models/comment.rb @@ -19,6 +19,8 @@ class Comment < ApplicationRecord include Redmine::SafeAttributes + include Redmine::Reaction::Reactable + belongs_to :commented, :polymorphic => true, :counter_cache => true belongs_to :author, :class_name => 'User' @@ -28,6 +30,8 @@ class Comment < ApplicationRecord safe_attributes 'comments' + delegate :visible?, to: :commented + def comments=(arg) self.content = arg end @@ -36,6 +40,10 @@ class Comment < ApplicationRecord content end + def project + commented.respond_to?(:project) ? commented.project : nil + end + private def send_notification diff --git a/app/models/custom_field.rb b/app/models/custom_field.rb index ec8c5de8d..d14b67bdb 100644 --- a/app/models/custom_field.rb +++ b/app/models/custom_field.rb @@ -101,7 +101,8 @@ class CustomField < ApplicationRecord 'version_status', 'extensions_allowed', 'full_width_layout', - 'thousands_delimiter' + 'thousands_delimiter', + 'ratio_interval' ) def copy_from(arg, options={}) @@ -335,12 +336,12 @@ class CustomField < ApplicationRecord args.include?(field_format) end - def self.human_attribute_name(attribute_key_name, *args) + def self.human_attribute_name(attribute_key_name, *) attr_name = attribute_key_name.to_s if attr_name == 'url_pattern' attr_name = "url" end - super(attr_name, *args) + super(attr_name, *) end def css_classes diff --git a/app/models/email_address.rb b/app/models/email_address.rb index 69ae8a066..de8c86531 100644 --- a/app/models/email_address.rb +++ b/app/models/email_address.rb @@ -74,7 +74,7 @@ class EmailAddress < ApplicationRecord # Returns true if domain belongs to domains list. def self.domain_in?(domain, domains) - domain = domain.downcase + domain = domain.to_s.downcase domains = domains.to_s.split(/[\s,]+/) unless domains.is_a?(Array) domains.reject(&:blank?).map(&:downcase).any? do |s| s.start_with?('.') ? domain.end_with?(s) : domain == s @@ -150,6 +150,10 @@ class EmailAddress < ApplicationRecord def validate_email_domain domain = address.partition('@').last + # Skip domain validation if the email does not contain a domain part, + # to avoid an incomplete error message like "domain not allowed ()" + return if domain.empty? + return if self.class.valid_domain?(domain) if User.current.logged? diff --git a/app/models/group.rb b/app/models/group.rb index ea5454558..300b59b46 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -94,12 +94,12 @@ class Group < Principal destroy_all end - def self.human_attribute_name(attribute_key_name, *args) + def self.human_attribute_name(attribute_key_name, *) attr_name = attribute_key_name.to_s if attr_name == 'lastname' attr_name = "name" end - super(attr_name, *args) + super(attr_name, *) end def self.anonymous diff --git a/app/models/issue.rb b/app/models/issue.rb index 2d004a78d..576840843 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -25,6 +25,7 @@ class Issue < ApplicationRecord before_validation :clear_disabled_fields before_save :set_parent_id include Redmine::NestedSet::IssueNestedSet + include Redmine::Reaction::Reactable belongs_to :project belongs_to :tracker @@ -268,7 +269,7 @@ class Issue < ApplicationRecord end alias :base_reload :reload - def reload(*args) + def reload(*) @workflow_rule_by_attribute = nil @assignable_versions = nil @relations = nil @@ -277,7 +278,7 @@ class Issue < ApplicationRecord @total_estimated_hours = nil @last_updated_by = nil @last_notes = nil - base_reload(*args) + base_reload(*) end # Overrides Redmine::Acts::Customizable::InstanceMethods#available_custom_fields @@ -469,7 +470,7 @@ class Issue < ApplicationRecord end # Overrides assign_attributes so that project and tracker get assigned first - def assign_attributes(new_attributes, *args) + def assign_attributes(new_attributes, *) return if new_attributes.nil? attrs = new_attributes.dup @@ -480,7 +481,7 @@ class Issue < ApplicationRecord send :"#{attr}=", attrs.delete(attr) end end - super(attrs, *args) + super(attrs, *) end def attributes=(new_attributes) @@ -916,7 +917,8 @@ class Issue < ApplicationRecord result = journals. preload(:details). preload(:user => :email_address). - reorder(:created_on, :id).to_a + reorder(:created_on, :id). + to_a result.each_with_index {|j, i| j.indice = i + 1} @@ -927,6 +929,9 @@ class Issue < ApplicationRecord end Journal.preload_journals_details_custom_fields(result) result.select! {|journal| journal.notes? || journal.visible_details.any?} + + Journal.preload_reaction_details(result) + result end @@ -1170,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 @@ -1203,11 +1208,7 @@ class Issue < ApplicationRecord end def last_notes - if @last_notes - @last_notes - else - journals.visible.where.not(notes: '').reorder(:id => :desc).first.try(:notes) - end + @last_notes || journals.visible.where.not(notes: '').reorder(:id => :desc).first.try(:notes) end # Preloads relations for a collection of issues diff --git a/app/models/issue_relation.rb b/app/models/issue_relation.rb index e55875e4d..80af22b89 100644 --- a/app/models/issue_relation.rb +++ b/app/models/issue_relation.rb @@ -22,9 +22,9 @@ class IssueRelation < ApplicationRecord class Relations < Array include Redmine::I18n - def initialize(issue, *args) + def initialize(issue, *) @issue = issue - super(*args) + super(*) end def to_s(*args) diff --git a/app/models/journal.rb b/app/models/journal.rb index 179e60c24..12f2beec8 100644 --- a/app/models/journal.rb +++ b/app/models/journal.rb @@ -19,6 +19,7 @@ class Journal < ApplicationRecord include Redmine::SafeAttributes + include Redmine::Reaction::Reactable belongs_to :journalized, :polymorphic => true # added as a quick fix to allow eager loading of the polymorphic association @@ -157,8 +158,8 @@ class Journal < ApplicationRecord end end - def visible?(*args) - journalized.visible?(*args) + def visible?(*) + journalized.visible?(*) end # Returns a string of css classes diff --git a/app/models/mail_handler.rb b/app/models/mail_handler.rb index b6858d96a..5d246a572 100644 --- a/app/models/mail_handler.rb +++ b/app/models/mail_handler.rb @@ -55,8 +55,8 @@ class MailHandler < ActionMailer::Base end # Receives an email and rescues any exception - def self.safe_receive(*args) - receive(*args) + def self.safe_receive(*) + receive(*) rescue => e Rails.logger.error "MailHandler: an unexpected error occurred when receiving email: #{e.message}" return false diff --git a/app/models/member.rb b/app/models/member.rb index b0d5c35fc..1f597c96c 100644 --- a/app/models/member.rb +++ b/app/models/member.rb @@ -45,9 +45,9 @@ class Member < ApplicationRecord end) alias :base_reload :reload - def reload(*args) + def reload(*) @managed_roles = nil - base_reload(*args) + base_reload(*) end def role diff --git a/app/models/message.rb b/app/models/message.rb index c7f78d2d9..9ac88c7d1 100644 --- a/app/models/message.rb +++ b/app/models/message.rb @@ -19,6 +19,8 @@ class Message < ApplicationRecord include Redmine::SafeAttributes + include Redmine::Reaction::Reactable + belongs_to :board belongs_to :author, :class_name => 'User' acts_as_tree :counter_cache => :replies_count, :order => "#{Message.table_name}.created_on ASC" diff --git a/app/models/news.rb b/app/models/news.rb index 40cd63db9..174e4c5ac 100644 --- a/app/models/news.rb +++ b/app/models/news.rb @@ -19,6 +19,8 @@ class News < ApplicationRecord include Redmine::SafeAttributes + include Redmine::Reaction::Reactable + belongs_to :project belongs_to :author, :class_name => 'User' has_many :comments, lambda {order("created_on")}, :as => :commented, :dependent => :delete_all diff --git a/app/models/principal.rb b/app/models/principal.rb index 77f599c73..0a6c32ba2 100644 --- a/app/models/principal.rb +++ b/app/models/principal.rb @@ -35,6 +35,8 @@ class Principal < ApplicationRecord :foreign_key => 'user_id' has_many :projects, :through => :memberships has_many :issue_categories, :foreign_key => 'assigned_to_id', :dependent => :nullify + # Always returns nil for groups + has_one :email_address, lambda {where :is_default => true}, :autosave => true, :foreign_key => 'user_id' validate :validate_status @@ -128,6 +130,11 @@ class Principal < ApplicationRecord to_s end + # Returns nil by default, subclasses should implement this method + def initials(formatter = nil) + nil + end + def mail=(*args) nil end diff --git a/app/models/project.rb b/app/models/project.rb index c438be16d..b3bf88c94 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -358,12 +358,12 @@ class Project < ApplicationRecord end end - def self.find_by_param(*args) - self.find(*args) + def self.find_by_param(*) + self.find(*) end alias :base_reload :reload - def reload(*args) + def reload(*) @principals = nil @users = nil @shared_versions = nil @@ -382,7 +382,7 @@ class Project < ApplicationRecord @override_members = nil @assignable_users = nil @last_activity_date = nil - base_reload(*args) + base_reload(*) end def to_param diff --git a/app/models/reaction.rb b/app/models/reaction.rb new file mode 100644 index 000000000..184ed2d6e --- /dev/null +++ b/app/models/reaction.rb @@ -0,0 +1,60 @@ +# frozen_string_literal: true + +# Redmine - project management software +# Copyright (C) 2006- Jean-Philippe Lang +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +class Reaction < ApplicationRecord + belongs_to :reactable, polymorphic: true + belongs_to :user + + validates :reactable_type, inclusion: { in: Redmine::Reaction::REACTABLE_TYPES } + + 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( + # Users who reacted and are visible to the target user + :visible_users, + # Reaction of the target user + :user_reaction + ) do + 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 = visible(user) + .for_reactable(reactables) + .preload(:user) + .select(:id, :reactable_id, :user_id) + .order(id: :desc) + + reactions.each_with_object({}) do |reaction, m| + m[reaction.reactable_id] ||= Detail.new + + m[reaction.reactable_id].then do |detail| + detail.visible_users << reaction.user + detail.user_reaction = reaction if reaction.user == user + end + end + end +end diff --git a/app/models/repository.rb b/app/models/repository.rb index a1e81baf3..f4092fc96 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -69,12 +69,12 @@ class Repository < ApplicationRecord end end - def self.human_attribute_name(attribute_key_name, *args) + def self.human_attribute_name(attribute_key_name, *) attr_name = attribute_key_name.to_s if attr_name == "log_encoding" attr_name = "commit_logs_encoding" end - super(attr_name, *args) + super(attr_name, *) end # Removes leading and trailing whitespace @@ -369,8 +369,8 @@ class Repository < ApplicationRecord subclasses.collect {|klass| [klass.scm_name, klass.name]} end - def self.factory(klass_name, *args) - repository_class(klass_name).new(*args) rescue nil + def self.factory(klass_name, *) + repository_class(klass_name).new(*) rescue nil end def self.repository_class(class_name) diff --git a/app/models/repository/bazaar.rb b/app/models/repository/bazaar.rb index d9cffe810..fc42c1235 100644 --- a/app/models/repository/bazaar.rb +++ b/app/models/repository/bazaar.rb @@ -22,12 +22,12 @@ require 'redmine/scm/adapters/bazaar_adapter' class Repository::Bazaar < Repository validates_presence_of :url, :log_encoding - def self.human_attribute_name(attribute_key_name, *args) + def self.human_attribute_name(attribute_key_name, *) attr_name = attribute_key_name.to_s if attr_name == "url" attr_name = "path_to_repository" end - super(attr_name, *args) + super(attr_name, *) end def self.scm_adapter_class diff --git a/app/models/repository/cvs.rb b/app/models/repository/cvs.rb index a5fce91bb..d055428a5 100644 --- a/app/models/repository/cvs.rb +++ b/app/models/repository/cvs.rb @@ -27,14 +27,14 @@ class Repository::Cvs < Repository 'root_url', :if => lambda {|repository, user| repository.new_record?}) - def self.human_attribute_name(attribute_key_name, *args) + def self.human_attribute_name(attribute_key_name, *) attr_name = attribute_key_name.to_s if attr_name == "root_url" attr_name = "cvsroot" elsif attr_name == "url" attr_name = "cvs_module" end - super(attr_name, *args) + super(attr_name, *) end def self.scm_adapter_class diff --git a/app/models/repository/filesystem.rb b/app/models/repository/filesystem.rb index 9347de0f3..c27044a9a 100644 --- a/app/models/repository/filesystem.rb +++ b/app/models/repository/filesystem.rb @@ -25,12 +25,12 @@ require 'redmine/scm/adapters/filesystem_adapter' class Repository::Filesystem < Repository validates_presence_of :url - def self.human_attribute_name(attribute_key_name, *args) + def self.human_attribute_name(attribute_key_name, *) attr_name = attribute_key_name.to_s if attr_name == "url" attr_name = "root_directory" end - super(attr_name, *args) + super(attr_name, *) end def self.scm_adapter_class diff --git a/app/models/repository/git.rb b/app/models/repository/git.rb index b6b3c8336..c94acb01d 100644 --- a/app/models/repository/git.rb +++ b/app/models/repository/git.rb @@ -25,10 +25,10 @@ class Repository::Git < Repository safe_attributes 'report_last_commit' - def self.human_attribute_name(attribute_key_name, *args) + def self.human_attribute_name(attribute_key_name, *) attr_name = attribute_key_name.to_s attr_name = 'path_to_repository' if attr_name == 'url' - super(attr_name, *args) + super(attr_name, *) end def self.scm_adapter_class diff --git a/app/models/repository/mercurial.rb b/app/models/repository/mercurial.rb index 8794cde75..1d1a3c4ff 100644 --- a/app/models/repository/mercurial.rb +++ b/app/models/repository/mercurial.rb @@ -30,12 +30,12 @@ class Repository::Mercurial < Repository # number of changesets to fetch at once FETCH_AT_ONCE = 100 - def self.human_attribute_name(attribute_key_name, *args) + def self.human_attribute_name(attribute_key_name, *) attr_name = attribute_key_name.to_s if attr_name == "url" attr_name = "path_to_repository" end - super(attr_name, *args) + super(attr_name, *) end def self.scm_adapter_class 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 4ce63f809..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 }, @@ -89,10 +98,10 @@ class User < Principal :after_remove => Proc.new {|user, group| group.user_removed(user)} has_many :changesets, :dependent => :nullify has_one :preference, :dependent => :destroy, :class_name => 'UserPreference' - has_one :atom_token, lambda {where "action='feeds'"}, :class_name => 'Token' - has_one :api_token, lambda {where "action='api'"}, :class_name => 'Token' - has_one :email_address, lambda {where :is_default => true}, :autosave => true + has_one :atom_token, lambda {where "#{table.name}.action='feeds'"}, :class_name => 'Token' + has_one :api_token, lambda {where "#{table.name}.action='api'"}, :class_name => 'Token' has_many :email_addresses, :dependent => :delete_all + has_many :reactions, dependent: :delete_all belongs_to :auth_source scope :logged, lambda {where("#{User.table_name}.status <> #{STATUS_ANONYMOUS}")} @@ -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 @@ -170,7 +180,7 @@ class User < Principal end alias :base_reload :reload - def reload(*args) + def reload(*) @name = nil @roles = nil @projects_by_role = nil @@ -181,7 +191,7 @@ class User < Principal @builtin_role = nil @visible_project_ids = nil @managed_roles = nil - base_reload(*args) + base_reload(*) end def mail @@ -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 @@ -643,7 +661,7 @@ class User < Principal def projects_by_role return @projects_by_role if @projects_by_role - result = Hash.new([]) + result = Hash.new {|_h, _k| []} project_ids_by_role.each do |role, ids| result[role] = Project.where(:id => ids).to_a end @@ -676,7 +694,7 @@ class User < Principal hash[role_id] << project_id end - result = Hash.new([]) + result = Hash.new {|_h, _k| []} if hash.present? roles = Role.where(:id => hash.keys).to_a hash.each do |role_id, proj_ids| @@ -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 diff --git a/app/models/version.rb b/app/models/version.rb index 51c7c0417..3ca4f2bff 100644 --- a/app/models/version.rb +++ b/app/models/version.rb @@ -106,7 +106,7 @@ module FixedIssuesExtension done = self.open(open).sum do |c| estimated = c.total_estimated_hours.to_f estimated = estimated_average unless estimated > 0.0 - ratio = c.closed? ? 100 : (c.done_ratio || 0) + ratio = open ? (c.done_ratio || 0) : 100 estimated * ratio end progress = done / (estimated_average * issues_count) @@ -211,8 +211,8 @@ class Version < ApplicationRecord end # Version files have same visibility as project files - def attachments_visible?(*args) - project.present? && project.attachments_visible?(*args) + def attachments_visible?(*) + project.present? && project.attachments_visible?(*) end def attachments_deletable?(usr=User.current) @@ -220,10 +220,10 @@ class Version < ApplicationRecord end alias :base_reload :reload - def reload(*args) + def reload(*) @default_project_version = nil @visible_fixed_issues = nil - base_reload(*args) + base_reload(*) end def start_date |