Skip to content

Commit 08a94e2

Browse files
Release OpenProject 12.3.1
2 parents b821152 + 72765a9 commit 08a94e2

File tree

149 files changed

+1428
-666
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

149 files changed

+1428
-666
lines changed

app/models/custom_option.rb

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,6 @@
3030
# A custom option is a possible value for a given custom field
3131
# which is restricted to a set of specific values.
3232
class CustomOption < ApplicationRecord
33-
acts_as_list
34-
3533
belongs_to :custom_field, touch: true
3634

3735
validates :value, presence: true, length: { maximum: 255 }

app/models/token/expirable_token.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ module ExpirableToken
2222

2323
included do
2424
# Set the expiration time
25-
before_create :set_expiration_time
25+
after_initialize :set_expiration_time, if: :new_record?
2626

2727
# Remove outdated token
2828
after_save :delete_expired_tokens
@@ -34,7 +34,7 @@ def valid_plaintext?(input)
3434
end
3535

3636
def expired?
37-
expires_on && Time.now > expires_on
37+
expires_on.nil? || Time.now > expires_on
3838
end
3939

4040
def validity_time

app/models/user_preference.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ class UserPreference < ApplicationRecord
3333

3434
validates :user,
3535
presence: true
36+
37+
WORKDAYS_FROM_MONDAY_TO_FRIDAY = [1, 2, 3, 4, 5].freeze
38+
3639
##
3740
# Retrieve keys from settings, and allow accessing
3841
# as boolean with ? suffix
@@ -124,6 +127,10 @@ def daily_reminders
124127
super.presence || { enabled: true, times: ["08:00:00+00:00"] }.with_indifferent_access
125128
end
126129

130+
def workdays
131+
super || WORKDAYS_FROM_MONDAY_TO_FRIDAY
132+
end
133+
127134
def immediate_reminders
128135
super.presence || { mentioned: false }.with_indifferent_access
129136
end

app/services/base_services/copy.rb

Lines changed: 23 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
#++
2828

2929
module BaseServices
30-
class Copy < ::BaseServices::BaseContracted
30+
class Copy < ::BaseServices::Write
3131
alias_attribute(:source, :model)
3232

3333
##
@@ -39,7 +39,9 @@ def self.copy_dependencies
3939
##
4040
# collect copyable associated modules
4141
def self.copyable_dependencies
42-
copy_dependencies.map do |service_cls|
42+
copy_dependencies
43+
.flat_map { |dependency| [dependency] + dependency.copy_dependencies }
44+
.map do |service_cls|
4345
{
4446
identifier: service_cls.identifier,
4547
name_source: -> { service_cls.human_name },
@@ -61,33 +63,30 @@ def initialize(user:, source: nil, model: nil, contract_class: nil, contract_opt
6163
end
6264

6365
def call(params)
64-
User.execute_as(user) do
65-
prepare(params)
66-
perform(params)
67-
end
68-
end
66+
prepare_state(params)
6967

70-
def after_validate(params, _call)
71-
# Initialize the target resource to copy into
72-
call = initialize_copy(source, params)
68+
super
69+
end
7370

71+
def persist(call)
7472
# Return only the unsaved copy
7573
return call if params[:attributes_only]
7674

77-
# Try to save the result or return its errors
78-
copy_instance = call.result
79-
unless copy_instance.save
80-
return ServiceResult.failure(result: copy_instance, errors: copy_instance.errors)
81-
end
82-
83-
self.class.copy_dependencies.each do |service_cls|
84-
next if skip_dependency?(params, service_cls)
75+
super.tap do |super_call|
76+
copy_instance = super_call.result
77+
self.class.copy_dependencies.each do |service_cls|
78+
next if skip_dependency?(params, service_cls)
8579

86-
call.merge! call_dependent_service(service_cls, target: copy_instance, params:),
87-
without_success: true
80+
super_call.merge! call_dependent_service(service_cls, target: copy_instance, params:),
81+
without_success: true
82+
end
8883
end
84+
end
85+
86+
def after_perform(call)
87+
return call if params[:attributes_only]
8988

90-
call
89+
super
9190
end
9291

9392
protected
@@ -110,7 +109,7 @@ def skip_dependency?(_params, _dependency_cls)
110109
#
111110
# Note that for dependent copy services to be called
112111
# this will already be present.
113-
def prepare(_params)
112+
def prepare_state(_params)
114113
# Retain the source project itself
115114
state.source = source
116115
end
@@ -124,8 +123,8 @@ def call_dependent_service(service_cls, target:, params:)
124123
.call(params:)
125124
end
126125

127-
def initialize_copy(source, params)
128-
raise NotImplementedError
126+
def instance(_params)
127+
source.class.new
129128
end
130129

131130
def default_contract_class

app/services/copy/concerns/copy_attachments.rb

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,28 @@ module CopyAttachments
66
def copy_attachments(container_type, from_id:, to_id:)
77
Attachment
88
.where(container_type:, container_id: from_id)
9-
.find_each do |old_attachment|
10-
copied = old_attachment.dup
11-
old_attachment.file.copy_to(copied)
9+
.find_each do |source|
1210

13-
copied.author = user
14-
copied.container_type = old_attachment.container_type
15-
copied.container_id = to_id
11+
copy = Attachment
12+
.new(attachment_copy_attributes(source, to_id))
13+
source.file.copy_to(copy)
1614

17-
unless copied.save
18-
Rails.logger.error { "Attachments ##{old_attachment.id} could not be copied: #{copied.errors.full_messages} " }
15+
unless copy.save
16+
Rails.logger.error { "Attachments ##{source.id} could not be copy: #{copy.errors.full_messages} " }
1917
end
2018
rescue StandardError => e
21-
Rails.logger.error { "Failed to copy attachments from ##{from_container_id} to ##{to_container_id}: #{e}" }
19+
Rails.logger.error { "Failed to copy attachments from ##{from_id} to ##{to_id}: #{e}" }
2220
end
2321
end
22+
23+
def attachment_copy_attributes(source, to_id)
24+
source
25+
.dup
26+
.attributes
27+
.except('file')
28+
.merge('author_id' => user.id,
29+
'container_id' => to_id)
30+
end
2431
end
2532
end
2633
end

app/services/copy/dependency.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,13 @@ def self.human_name
4747
identifier.capitalize
4848
end
4949

50+
##
51+
# Dependencies the current dependency itself supports.
52+
# The most common case for this are attachment services.
53+
def self.copy_dependencies
54+
[]
55+
end
56+
5057
def initialize(source:, target:, user:)
5158
@source = source
5259
@target = target

app/services/grids/copy_service.rb

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -51,14 +51,8 @@ def initialize(user:, source:, contract_class: ::EmptyContract)
5151

5252
protected
5353

54-
def initialize_copy(source, params)
55-
grid = source.dup
56-
57-
initialize_new_grid! grid, source, params
58-
59-
ServiceResult.new success: grid.save, result: grid
54+
def set_attributes_params(_params)
55+
source.dup.attributes
6056
end
61-
62-
def initialize_new_grid!(_new_grid, _original_grid, _params); end
6357
end
6458
end
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# OpenProject is an open source project management software.
2+
# Copyright (C) 2010-2022 the OpenProject GmbH
3+
#
4+
# This program is free software; you can redistribute it and/or
5+
# modify it under the terms of the GNU General Public License version 3.
6+
#
7+
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
8+
# Copyright (C) 2006-2013 Jean-Philippe Lang
9+
# Copyright (C) 2010-2013 the ChiliProject Team
10+
#
11+
# This program is free software; you can redistribute it and/or
12+
# modify it under the terms of the GNU General Public License
13+
# as published by the Free Software Foundation; either version 2
14+
# of the License, or (at your option) any later version.
15+
#
16+
# This program is distributed in the hope that it will be useful,
17+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
18+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19+
# GNU General Public License for more details.
20+
#
21+
# You should have received a copy of the GNU General Public License
22+
# along with this program; if not, write to the Free Software
23+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
24+
#
25+
# See COPYRIGHT and LICENSE files for more details.
26+
27+
module Projects::Copy
28+
# Can be included in case the dependent service needs to be listed in the copy_dependency section for:
29+
# * Being an option in the UI
30+
# * Calculating the count
31+
# but does not really need to do anything beyond that.
32+
#
33+
# This is currently employed to turn the attachment dependent services into NoCreate services as attachment
34+
# handling has been moved into the create services e.g. of WorkPackage and WikiPage.
35+
module AttachmentCopier
36+
extend ActiveSupport::Concern
37+
38+
class_methods do
39+
def attachment_dependent_service(service_const = nil)
40+
@attachment_dependent_service = service_const if service_const
41+
42+
@attachment_dependent_service
43+
end
44+
45+
def copy_dependencies
46+
super + [attachment_dependent_service]
47+
end
48+
end
49+
50+
protected
51+
52+
def copy_attachments?
53+
(params.dig(:params, :only) || [])
54+
.any? { |k| k.to_s == self.class.attachment_dependent_service.identifier }
55+
end
56+
end
57+
end

app/services/projects/copy/dependency.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ def source_count
4141
# Check whether this dependency should be copied
4242
# as it was selected
4343
def self.should_copy?(params, check)
44-
return true unless params[:only].present?
44+
return true if params[:only].blank?
4545

4646
params[:only].any? { |key| key.to_sym == check }
4747
end
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# OpenProject is an open source project management software.
2+
# Copyright (C) 2010-2022 the OpenProject GmbH
3+
#
4+
# This program is free software; you can redistribute it and/or
5+
# modify it under the terms of the GNU General Public License version 3.
6+
#
7+
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
8+
# Copyright (C) 2006-2013 Jean-Philippe Lang
9+
# Copyright (C) 2010-2013 the ChiliProject Team
10+
#
11+
# This program is free software; you can redistribute it and/or
12+
# modify it under the terms of the GNU General Public License
13+
# as published by the Free Software Foundation; either version 2
14+
# of the License, or (at your option) any later version.
15+
#
16+
# This program is distributed in the hope that it will be useful,
17+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
18+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19+
# GNU General Public License for more details.
20+
#
21+
# You should have received a copy of the GNU General Public License
22+
# along with this program; if not, write to the Free Software
23+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
24+
#
25+
# See COPYRIGHT and LICENSE files for more details.
26+
27+
module Projects::Copy
28+
# Can be included in case the dependent service needs to be listed in the copy_dependency section for:
29+
# * Being an option in the UI
30+
# * Calculating the count
31+
# but does not really need to do anything beyond that.
32+
#
33+
# This is currently employed to turn the attachment dependent services into NoCreate services as attachment
34+
# handling has been moved into the create services e.g. of WorkPackage and WikiPage.
35+
module NoCopier
36+
protected
37+
38+
def copy_dependency(params:)
39+
# Not actually doing any copying.
40+
end
41+
end
42+
end

app/services/projects/copy/wiki_dependent_service.rb

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@
2828

2929
module Projects::Copy
3030
class WikiDependentService < Dependency
31+
include AttachmentCopier
32+
33+
attachment_dependent_service ::Projects::Copy::WikiPageAttachmentsDependentService
34+
3135
def self.human_name
3236
I18n.t(:label_wiki_page_plural)
3337
end
@@ -74,7 +78,8 @@ def copy_wiki_page(source_page, new_parent_id)
7478
.new(user:, model: source_page, contract_class: WikiPages::CopyContract)
7579
.call(wiki: target.wiki,
7680
parent_id: new_parent_id,
77-
send_notifications: ActionMailer::Base.perform_deliveries)
81+
send_notifications: ActionMailer::Base.perform_deliveries,
82+
copy_attachments: copy_attachments?)
7883

7984
if service_call.success?
8085
service_call.result

app/services/projects/copy/wiki_page_attachments_dependent_service.rb

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828

2929
module Projects::Copy
3030
class WikiPageAttachmentsDependentService < Dependency
31-
include ::Copy::Concerns::CopyAttachments
31+
include ::Projects::Copy::NoCopier
3232

3333
def self.human_name
3434
I18n.t(:label_wiki_page_attachments)
@@ -37,16 +37,5 @@ def self.human_name
3737
def source_count
3838
source.wiki && source.wiki.pages.joins(:attachments).count('attachments.id')
3939
end
40-
41-
protected
42-
43-
def copy_dependency(params:)
44-
# If no wiki pages copied, we cannot copy their attachments
45-
return unless state.wiki_page_id_lookup
46-
47-
state.wiki_page_id_lookup.each do |old_id, new_id|
48-
copy_attachments('WikiPage', from_id: old_id, to_id: new_id)
49-
end
50-
end
5140
end
5241
end

app/services/projects/copy/work_package_attachments_dependent_service.rb

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
#-- copyright
1+
#--copyright
22
# OpenProject is an open source project management software.
3-
# Copyright (C) 2012-2022 the OpenProject GmbH
3+
# Copyright (C) 2010-2022 the OpenProject GmbH
44
#
55
# This program is free software; you can redistribute it and/or
66
# modify it under the terms of the GNU General Public License version 3.
@@ -28,7 +28,7 @@
2828

2929
module Projects::Copy
3030
class WorkPackageAttachmentsDependentService < Dependency
31-
include ::Copy::Concerns::CopyAttachments
31+
include ::Projects::Copy::NoCopier
3232

3333
def self.human_name
3434
I18n.t(:label_work_package_attachments)
@@ -37,16 +37,5 @@ def self.human_name
3737
def source_count
3838
source.work_packages.joins(:attachments).count('attachments.id')
3939
end
40-
41-
protected
42-
43-
def copy_dependency(params:)
44-
# If no work packages were copied, we cannot copy their attachments
45-
return unless state.work_package_id_lookup
46-
47-
state.work_package_id_lookup.each do |old_wp_id, new_wp_id|
48-
copy_attachments('WorkPackage', from_id: old_wp_id, to_id: new_wp_id)
49-
end
50-
end
5140
end
5241
end

0 commit comments

Comments
 (0)