Redmine 4.1.1

This commit is contained in:
Manuel Cillero 2020-11-22 21:20:06 +01:00
parent 33e7b881a5
commit 3d976f1b3b
1593 changed files with 36180 additions and 19489 deletions

View file

@ -1,5 +1,7 @@
# frozen_string_literal: true
# Redmine - project management software
# Copyright (C) 2006-2017 Jean-Philippe Lang
# Copyright (C) 2006-2019 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
@ -67,8 +69,6 @@ class Project < ActiveRecord::Base
:url => Proc.new {|o| {:controller => 'projects', :action => 'show', :id => o}},
:author => nil
attr_protected :status
validates_presence_of :name, :identifier
validates_uniqueness_of :identifier, :if => Proc.new {|p| p.identifier_changed?}
validates_length_of :name, :maximum => 255
@ -80,9 +80,9 @@ class Project < ActiveRecord::Base
validates_exclusion_of :identifier, :in => %w( new )
validate :validate_parent
after_save :update_inherited_members, :if => Proc.new {|project| project.inherit_members_changed?}
after_save :remove_inherited_member_roles, :add_inherited_member_roles, :if => Proc.new {|project| project.parent_id_changed?}
after_update :update_versions_from_hierarchy_change, :if => Proc.new {|project| project.parent_id_changed?}
after_save :update_inherited_members, :if => Proc.new {|project| project.saved_change_to_inherit_members?}
after_save :remove_inherited_member_roles, :add_inherited_member_roles, :if => Proc.new {|project| project.saved_change_to_parent_id?}
after_update :update_versions_from_hierarchy_change, :if => Proc.new {|project| project.saved_change_to_parent_id?}
before_destroy :delete_all_members
scope :has_module, lambda {|mod|
@ -93,14 +93,8 @@ class Project < ActiveRecord::Base
scope :all_public, lambda { where(:is_public => true) }
scope :visible, lambda {|*args| where(Project.visible_condition(args.shift || User.current, *args)) }
scope :allowed_to, lambda {|*args|
user = User.current
permission = nil
if args.first.is_a?(Symbol)
permission = args.shift
else
user = args.shift
permission = args.shift
end
user = args.first.is_a?(Symbol) ? User.current : args.shift
permission = args.shift
where(Project.allowed_to_condition(user, permission, *args))
}
scope :like, lambda {|arg|
@ -181,7 +175,7 @@ class Project < ActiveRecord::Base
base_statement = (perm && perm.read? ? "#{Project.table_name}.status <> #{Project::STATUS_ARCHIVED}" : "#{Project.table_name}.status = #{Project::STATUS_ACTIVE}")
if !options[:skip_pre_condition] && perm && perm.project_module
# If the permission belongs to a project module, make sure the module is enabled
base_statement << " AND EXISTS (SELECT 1 AS one FROM #{EnabledModule.table_name} em WHERE em.project_id = #{Project.table_name}.id AND em.name='#{perm.project_module}')"
base_statement += " AND EXISTS (SELECT 1 AS one FROM #{EnabledModule.table_name} em WHERE em.project_id = #{Project.table_name}.id AND em.name='#{perm.project_module}')"
end
if project = options[:project]
project_statement = project.project_condition(options[:with_subprojects])
@ -257,6 +251,15 @@ class Project < ActiveRecord::Base
scope
end
# Creates or updates project time entry activities
def update_or_create_time_entry_activities(activities)
transaction do
activities.each do |id, activity|
update_or_create_time_entry_activity(id, activity)
end
end
end
# Will create a new Project specific Activity or update an existing one
#
# This will raise a ActiveRecord::Rollback if the TimeEntryActivity
@ -266,7 +269,7 @@ class Project < ActiveRecord::Base
self.create_time_entry_activity_if_needed(activity_hash)
else
activity = project.time_entry_activities.find_by_id(id.to_i)
activity.update_attributes(activity_hash) if activity
activity.update(activity_hash) if activity
end
end
@ -304,7 +307,7 @@ class Project < ActiveRecord::Base
end
def self.find(*args)
if args.first && args.first.is_a?(String) && !args.first.match(/^\d*$/)
if args.first && args.first.is_a?(String) && !/^\d*$/.match?(args.first)
project = find_by_identifier(*args)
raise ActiveRecord::RecordNotFound, "Couldn't find Project with identifier=#{args.first}" if project.nil?
project
@ -344,7 +347,7 @@ class Project < ActiveRecord::Base
nil
else
# id is used for projects with a numeric identifier (compatibility)
@to_param ||= (identifier.to_s =~ %r{^\d*$} ? id.to_s : identifier)
@to_param ||= (%r{^\d*$}.match?(identifier.to_s) ? id.to_s : identifier)
end
end
@ -380,15 +383,11 @@ class Project < ActiveRecord::Base
true
end
# Unarchives the project
# All its ancestors must be active
# Unarchives the project and its archived ancestors
def unarchive
return false if ancestors.detect {|a| a.archived?}
new_status = STATUS_ACTIVE
if parent
new_status = parent.status
end
update_attribute :status, new_status
new_status = ancestors.any?(&:closed?) ? STATUS_CLOSED : STATUS_ACTIVE
self_and_ancestors.status(STATUS_ARCHIVED).update_all :status => new_status
reload
end
def close
@ -414,14 +413,6 @@ class Project < ActiveRecord::Base
@allowed_parents
end
# Sets the parent of the project with authorization check
def set_allowed_parent!(p)
ActiveSupport::Deprecation.warn "Project#set_allowed_parent! is deprecated and will be removed in Redmine 4, use #safe_attributes= instead."
p = p.id if p.is_a?(Project)
send :safe_attributes, {:project_id => p}
save
end
# Sets the parent of the project and saves the project
# Argument can be either a Project, a String, a Fixnum or nil
def set_parent!(p)
@ -527,8 +518,8 @@ class Project < ActiveRecord::Base
member
end
# Default role that is given to non-admin users that
# create a project
# Default role that is given to non-admin users that
# create a project
def self.default_member_role
Role.givable.find_by_id(Setting.new_project_user_role_id.to_i) || Role.givable.first
end
@ -625,10 +616,11 @@ class Project < ActiveRecord::Base
end
def css_classes
s = 'project'
s = +'project'
s << ' root' if root?
s << ' child' if child?
s << (leaf? ? ' leaf' : ' parent')
s << ' public' if is_public?
unless active?
if archived?
s << ' archived'
@ -641,20 +633,22 @@ class Project < ActiveRecord::Base
# The earliest start date of a project, based on it's issues and versions
def start_date
@start_date ||= [
issues.minimum('start_date'),
shared_versions.minimum('effective_date'),
Issue.fixed_version(shared_versions).minimum('start_date')
].compact.min
@start_date ||=
[
issues.minimum('start_date'),
shared_versions.minimum('effective_date'),
Issue.fixed_version(shared_versions).minimum('start_date')
].compact.min
end
# The latest due date of an issue or version
def due_date
@due_date ||= [
issues.maximum('due_date'),
shared_versions.maximum('effective_date'),
Issue.fixed_version(shared_versions).maximum('due_date')
].compact.max
@due_date ||=
[
issues.maximum('due_date'),
shared_versions.maximum('effective_date'),
Issue.fixed_version(shared_versions).maximum('due_date')
].compact.max
end
def overdue?
@ -746,7 +740,8 @@ class Project < ActiveRecord::Base
target.destroy unless target.blank?
end
safe_attributes 'name',
safe_attributes(
'name',
'description',
'homepage',
'is_public',
@ -757,10 +752,12 @@ class Project < ActiveRecord::Base
'issue_custom_field_ids',
'parent_id',
'default_version_id',
'default_assigned_to_id'
'default_assigned_to_id')
safe_attributes 'enabled_module_names',
:if => lambda {|project, user|
safe_attributes(
'enabled_module_names',
:if =>
lambda {|project, user|
if project.new_record?
if user.admin?
true
@ -770,12 +767,17 @@ class Project < ActiveRecord::Base
else
user.allowed_to?(:select_project_modules, project)
end
}
})
safe_attributes 'inherit_members',
:if => lambda {|project, user| project.parent.nil? || project.parent.visible?(user)}
safe_attributes(
'inherit_members',
:if => lambda {|project, user| project.parent.nil? || project.parent.visible?(user)})
def safe_attributes=(attrs, user=User.current)
if attrs.respond_to?(:to_unsafe_hash)
attrs = attrs.to_unsafe_hash
end
return unless attrs.is_a?(Hash)
attrs = attrs.deep_dup
@ -791,6 +793,18 @@ class Project < ActiveRecord::Base
end
end
# Reject custom fields values not visible by the user
if attrs['custom_field_values'].present?
editable_custom_field_ids = editable_custom_field_values(user).map {|v| v.custom_field_id.to_s}
attrs['custom_field_values'].reject! {|k, v| !editable_custom_field_ids.include?(k.to_s)}
end
# Reject custom fields not visible by the user
if attrs['custom_fields'].present?
editable_custom_field_ids = editable_custom_field_values(user).map {|v| v.custom_field_id.to_s}
attrs['custom_fields'].reject! {|c| !editable_custom_field_ids.include?(c['id'].to_s)}
end
super(attrs, user)
end
@ -818,12 +832,17 @@ class Project < ActiveRecord::Base
def copy(project, options={})
project = project.is_a?(Project) ? project : Project.find(project)
to_be_copied = %w(members wiki versions issue_categories issues queries boards)
to_be_copied = %w(members wiki versions issue_categories issues queries boards documents)
to_be_copied = to_be_copied & Array.wrap(options[:only]) unless options[:only].nil?
Project.transaction do
if save
reload
self.attachments = project.attachments.map do |attachment|
attachment.copy(:container => self)
end
to_be_copied.each do |name|
send "copy_#{name}", project
end
@ -835,11 +854,6 @@ class Project < ActiveRecord::Base
end
end
def member_principals
ActiveSupport::Deprecation.warn "Project#member_principals is deprecated and will be removed in Redmine 4.0. Use #memberships.active instead."
memberships.active
end
# Returns a new unsaved Project instance with attributes copied from +project+
def self.copy_from(project)
project = project.is_a?(Project) ? project : Project.find(project)
@ -860,7 +874,8 @@ class Project < ActiveRecord::Base
ancestors = projects.first.ancestors.to_a
end
projects.sort_by(&:lft).each do |project|
while (ancestors.any? && !project.is_descendant_of?(ancestors.last))
while ancestors.any? &&
!project.is_descendant_of?(ancestors.last)
ancestors.pop
end
yield project, ancestors.size
@ -868,14 +883,26 @@ class Project < ActiveRecord::Base
end
end
# Returns the custom_field_values that can be edited by the given user
def editable_custom_field_values(user=nil)
visible_custom_field_values(user)
end
def visible_custom_field_values(user = nil)
user ||= User.current
custom_field_values.select do |value|
value.custom_field.visible_by?(project, user)
end
end
private
def update_inherited_members
if parent
if inherit_members? && !inherit_members_was
if inherit_members? && !inherit_members_before_last_save
remove_inherited_member_roles
add_inherited_member_roles
elsif !inherit_members? && inherit_members_was
elsif !inherit_members? && inherit_members_before_last_save
remove_inherited_member_roles
end
end
@ -932,6 +959,7 @@ class Project < ActiveRecord::Base
new_wiki_page = WikiPage.new(page.attributes.dup.except("id", "wiki_id", "created_on", "parent_id"))
new_wiki_page.content = new_wiki_content
wiki.pages << new_wiki_page
new_wiki_page.attachments = page.attachments.map{|attachement| attachement.copy(:container => new_wiki_page)}
wiki_pages_map[page.id] = new_wiki_page
end
@ -952,6 +980,11 @@ class Project < ActiveRecord::Base
project.versions.each do |version|
new_version = Version.new
new_version.attributes = version.attributes.dup.except("id", "project_id", "created_on", "updated_on")
new_version.attachments = version.attachments.map do |attachment|
attachment.copy(:container => new_version)
end
self.versions << new_version
end
end
@ -1104,6 +1137,21 @@ class Project < ActiveRecord::Base
end
end
# Copies documents from +project+
def copy_documents(project)
project.documents.each do |document|
new_document = Document.new
new_document.attributes = document.attributes.dup.except("id", "project_id")
new_document.project = self
new_document.attachments = document.attachments.map do |attachement|
attachement.copy(:container => new_document)
end
self.documents << new_document
end
end
def allowed_permissions
@allowed_permissions ||= begin
module_names = enabled_modules.loaded? ? enabled_modules.map(&:name) : enabled_modules.pluck(:name)