Skip to content

Commit a50dfa0

Browse files
Release OpenProject 12.4.2
2 parents ed1c162 + 33242ad commit a50dfa0

File tree

228 files changed

+14366
-686
lines changed

Some content is hidden

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

228 files changed

+14366
-686
lines changed

app/models/custom_value.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,10 @@ def strategy
6161
end
6262
end
6363

64+
def default?
65+
value == custom_field.default_value
66+
end
67+
6468
protected
6569

6670
def validate_presence_of_required_value

app/models/group.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,6 @@ def uniqueness_of_name
7272
end
7373

7474
def fail_add
75-
fail "Do not add users through association, use `group.add_members!` instead."
75+
fail "Do not add users through association, use `Groups::AddUsersService` instead."
7676
end
7777
end

app/models/work_package/pdf_export/view.rb

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -55,27 +55,25 @@ def document
5555
end
5656

5757
def fallback_fonts
58-
[]
58+
[noto_font_base_path.join('NotoSansSymbols2-Regular.ttf')]
5959
end
6060

6161
def register_fonts!(document)
62-
font_path = Rails.public_path.join('fonts')
63-
6462
document.font_families['NotoSans'] = {
6563
normal: {
66-
file: font_path.join('noto/NotoSans-Regular.ttf'),
64+
file: noto_font_base_path.join('NotoSans-Regular.ttf'),
6765
font: 'NotoSans-Regular'
6866
},
6967
italic: {
70-
file: font_path.join('noto/NotoSans-Italic.ttf'),
68+
file: noto_font_base_path.join('NotoSans-Italic.ttf'),
7169
font: 'NotoSans-Italic'
7270
},
7371
bold: {
74-
file: font_path.join('noto/NotoSans-Bold.ttf'),
72+
file: noto_font_base_path.join('NotoSans-Bold.ttf'),
7573
font: 'NotoSans-Bold'
7674
},
7775
bold_italic: {
78-
file: font_path.join('noto/NotoSans-BoldItalic.ttf'),
76+
file: noto_font_base_path.join('NotoSans-BoldItalic.ttf'),
7977
font: 'NotoSans-BoldItalic'
8078
}
8179
}
@@ -99,4 +97,10 @@ def font(name: nil, style: nil, size: nil)
9997

10098
document.font
10199
end
100+
101+
private
102+
103+
def noto_font_base_path
104+
Rails.public_path.join('fonts/noto')
105+
end
102106
end

app/services/groups/add_users_service.rb

Lines changed: 23 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@
2929
module Groups
3030
class AddUsersService < ::BaseServices::BaseContracted
3131
using CoreExtensions::SquishSql
32-
include Groups::Concerns::MembershipManipulation
3332

3433
def initialize(group, current_user:, contract_class: AdminOnlyContract)
3534
self.model = group
@@ -40,95 +39,37 @@ def initialize(group, current_user:, contract_class: AdminOnlyContract)
4039

4140
private
4241

43-
def modify_members_and_roles(params)
42+
def persist(call)
4443
sql_query = ::OpenProject::SqlSanitization
45-
.sanitize add_to_user_and_projects_cte,
46-
group_id: model.id,
47-
user_ids: params[:ids]
48-
44+
.sanitize add_to_group,
45+
group_id: model.id,
46+
user_ids: params[:ids]
4947
execute_query(sql_query)
48+
49+
call
5050
end
5151

52-
def add_to_user_and_projects_cte
53-
<<~SQL.squish
54-
-- select existing users from given IDs
55-
WITH found_users AS (
56-
SELECT id as user_id FROM #{User.table_name} WHERE id IN (:user_ids)
57-
),
58-
timestamp AS (
59-
SELECT CURRENT_TIMESTAMP as time
60-
),
61-
-- select existing memberships of the group
62-
group_memberships AS (
63-
SELECT project_id, user_id FROM #{Member.table_name} WHERE user_id = :group_id
64-
),
65-
-- select existing member_roles of the group
66-
group_roles AS (
67-
SELECT members.project_id AS project_id,
68-
members.user_id AS user_id,
69-
members.id AS member_id,
70-
member_roles.role_id AS role_id,
71-
member_roles.id AS member_role_id
72-
FROM #{MemberRole.table_name} member_roles
73-
JOIN #{Member.table_name} members
74-
ON members.id = member_roles.member_id AND members.user_id = :group_id
75-
),
76-
-- insert into group_users association
77-
new_group_users AS (
78-
INSERT INTO group_users (group_id, user_id)
79-
SELECT :group_id as group_id, user_id FROM found_users
80-
ON CONFLICT DO NOTHING
81-
),
82-
-- find members that already exist
83-
existing_members AS (
84-
SELECT members.id, found_users.user_id, members.project_id
85-
FROM members, found_users, group_memberships
86-
WHERE members.user_id = found_users.user_id
87-
AND members.project_id IS NOT DISTINCT FROM group_memberships.project_id
88-
AND members.id IS NOT NULL
89-
),
90-
-- insert the group user into members
91-
new_members AS (
92-
INSERT INTO #{Member.table_name} (project_id, user_id, updated_at, created_at)
93-
SELECT group_memberships.project_id, found_users.user_id, (SELECT time from timestamp), (SELECT time from timestamp)
94-
FROM found_users, group_memberships
95-
WHERE NOT EXISTS (SELECT 1 FROM existing_members WHERE existing_members.user_id = found_users.user_id AND existing_members.project_id IS NOT DISTINCT FROM group_memberships.project_id)
96-
ON CONFLICT(project_id, user_id) DO NOTHING
97-
RETURNING id, user_id, project_id
98-
),
99-
-- copy the member roles of the group
100-
add_roles AS (
101-
INSERT INTO #{MemberRole.table_name} (member_id, role_id, inherited_from)
102-
SELECT members.id, group_roles.role_id, group_roles.member_role_id
103-
FROM group_roles
104-
JOIN
105-
(SELECT * FROM new_members UNION SELECT * from existing_members) members ON group_roles.project_id IS NOT DISTINCT FROM members.project_id
106-
-- Ignore if the role was already inserted by us
107-
ON CONFLICT DO NOTHING
108-
RETURNING id, member_id, role_id
109-
),
110-
-- get the ids of members where roles have been added the member did not have before
111-
members_with_added_roles AS (
112-
SELECT DISTINCT add_roles.member_id
113-
FROM add_roles
114-
WHERE NOT EXISTS
115-
(SELECT 1 FROM #{MemberRole.table_name}
116-
WHERE #{MemberRole.table_name}.member_id = add_roles.member_id
117-
AND #{MemberRole.table_name}.role_id = add_roles.role_id
118-
AND #{MemberRole.table_name}.id != add_roles.id)
119-
),
120-
touch_existing_members AS (
121-
UPDATE members SET updated_AT = CURRENT_TIMESTAMP
122-
WHERE id IN (SELECT id from existing_members)
123-
AND id IN (SELECT member_id from members_with_added_roles)
124-
)
52+
def after_perform(call)
53+
Groups::CreateInheritedRolesService
54+
.new(model, current_user: user, contract_class:)
55+
.call(user_ids: params[:ids], message: params[:message])
12556

126-
SELECT member_id from members_with_added_roles
57+
call
58+
end
59+
60+
def add_to_group
61+
<<~SQL.squish
62+
INSERT INTO group_users (group_id, user_id)
63+
SELECT :group_id as group_id, user_id FROM
64+
(SELECT id as user_id FROM #{User.table_name} WHERE id IN (:user_ids)) users
65+
ON CONFLICT DO NOTHING
12766
SQL
12867
end
12968

130-
def touch_updated(member_ids)
131-
# do nothing in this case as we already touch while updating
69+
def execute_query(query)
70+
::Group
71+
.connection
72+
.exec_query(query)
13273
end
13374
end
13475
end
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
#-- copyright
2+
# OpenProject is an open source project management software.
3+
# Copyright (C) 2012-2022 the OpenProject GmbH
4+
#
5+
# This program is free software; you can redistribute it and/or
6+
# modify it under the terms of the GNU General Public License version 3.
7+
#
8+
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
9+
# Copyright (C) 2006-2013 Jean-Philippe Lang
10+
# Copyright (C) 2010-2013 the ChiliProject Team
11+
#
12+
# This program is free software; you can redistribute it and/or
13+
# modify it under the terms of the GNU General Public License
14+
# as published by the Free Software Foundation; either version 2
15+
# of the License, or (at your option) any later version.
16+
#
17+
# This program is distributed in the hope that it will be useful,
18+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
19+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20+
# GNU General Public License for more details.
21+
#
22+
# You should have received a copy of the GNU General Public License
23+
# along with this program; if not, write to the Free Software
24+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
25+
#
26+
# See COPYRIGHT and LICENSE files for more details.
27+
#++
28+
29+
module Groups
30+
# Adds inherited roles to the users provided to mirror the roles the group has.
31+
# This can be scoped to only a certain project which results in considerably better performance.
32+
class CreateInheritedRolesService < ::BaseServices::BaseContracted
33+
using CoreExtensions::SquishSql
34+
include Groups::Concerns::MembershipManipulation
35+
36+
def initialize(group, current_user:, contract_class: AdminOnlyContract)
37+
self.model = group
38+
39+
super user: current_user,
40+
contract_class:
41+
end
42+
43+
private
44+
45+
def modify_members_and_roles(params)
46+
sql_query = ::OpenProject::SqlSanitization
47+
.sanitize add_to_user_and_projects_cte(project_ids: params[:project_ids]),
48+
group_id: model.id,
49+
user_ids: params[:user_ids],
50+
project_ids: params[:project_ids]
51+
52+
execute_query(sql_query)
53+
end
54+
55+
def add_to_user_and_projects_cte(project_ids: nil)
56+
project_limit = if project_ids
57+
"project_id IN (:project_ids)"
58+
else
59+
"1=1"
60+
end
61+
62+
<<~SQL.squish
63+
-- select existing users from given IDs
64+
WITH found_users AS (
65+
SELECT id as user_id FROM #{User.table_name} WHERE id IN (:user_ids)
66+
),
67+
timestamp AS (
68+
SELECT CURRENT_TIMESTAMP as time
69+
),
70+
-- select existing memberships of the group
71+
group_memberships AS (
72+
SELECT project_id, user_id FROM #{Member.table_name} WHERE user_id = :group_id AND #{project_limit}
73+
),
74+
-- select existing member_roles of the group
75+
group_roles AS (
76+
SELECT members.project_id AS project_id,
77+
members.user_id AS user_id,
78+
members.id AS member_id,
79+
member_roles.role_id AS role_id,
80+
member_roles.id AS member_role_id
81+
FROM #{MemberRole.table_name} member_roles
82+
JOIN #{Member.table_name} members
83+
ON members.id = member_roles.member_id AND members.user_id = :group_id
84+
),
85+
-- find members that already exist
86+
existing_members AS (
87+
SELECT members.id, found_users.user_id, members.project_id
88+
FROM members, found_users, group_memberships
89+
WHERE members.user_id = found_users.user_id
90+
AND members.project_id IS NOT DISTINCT FROM group_memberships.project_id
91+
AND members.id IS NOT NULL
92+
),
93+
-- insert the group user into members
94+
new_members AS (
95+
INSERT INTO #{Member.table_name} (project_id, user_id, updated_at, created_at)
96+
SELECT group_memberships.project_id, found_users.user_id, (SELECT time from timestamp), (SELECT time from timestamp)
97+
FROM found_users, group_memberships
98+
WHERE NOT EXISTS (SELECT 1 FROM existing_members WHERE existing_members.user_id = found_users.user_id AND existing_members.project_id IS NOT DISTINCT FROM group_memberships.project_id)
99+
ON CONFLICT(project_id, user_id) DO NOTHING
100+
RETURNING id, user_id, project_id
101+
),
102+
-- copy the member roles of the group
103+
add_roles AS (
104+
INSERT INTO #{MemberRole.table_name} (member_id, role_id, inherited_from)
105+
SELECT members.id, group_roles.role_id, group_roles.member_role_id
106+
FROM group_roles
107+
JOIN
108+
(SELECT * FROM new_members UNION SELECT * from existing_members) members ON group_roles.project_id IS NOT DISTINCT FROM members.project_id
109+
-- Ignore if the role was already inserted by us
110+
ON CONFLICT DO NOTHING
111+
RETURNING id, member_id, role_id
112+
),
113+
-- get the ids of members where roles have been added the member did not have before
114+
members_with_added_roles AS (
115+
SELECT DISTINCT add_roles.member_id
116+
FROM add_roles
117+
WHERE NOT EXISTS
118+
(SELECT 1 FROM #{MemberRole.table_name}
119+
WHERE #{MemberRole.table_name}.member_id = add_roles.member_id
120+
AND #{MemberRole.table_name}.role_id = add_roles.role_id
121+
AND #{MemberRole.table_name}.id != add_roles.id)
122+
),
123+
touch_existing_members AS (
124+
UPDATE members SET updated_at = CURRENT_TIMESTAMP
125+
WHERE id IN (SELECT id from existing_members)
126+
AND id IN (SELECT member_id from members_with_added_roles)
127+
)
128+
129+
SELECT member_id from members_with_added_roles
130+
SQL
131+
end
132+
133+
def touch_updated(member_ids)
134+
# do nothing in this case as we already touch while updating
135+
end
136+
end
137+
end

app/services/members/create_service.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,9 @@ def post_process
4747
def add_group_memberships(member)
4848
return unless member.principal.is_a?(Group)
4949

50-
Groups::AddUsersService
50+
Groups::CreateInheritedRolesService
5151
.new(member.principal, current_user: user, contract_class: EmptyContract)
52-
.call(ids: member.principal.user_ids, send_notifications: false)
52+
.call(user_ids: member.principal.user_ids, send_notifications: false, project_ids: [member.project_id])
5353
end
5454

5555
def event_type

app/services/oauth_clients/connection_manager.rb

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ def request_with_token_refresh(oauth_client_token)
151151
# `yield` needs to returns a ServiceResult:
152152
# success: result= any object with data
153153
# failure: result= :error or :not_authorized
154-
yield_service_result = yield
154+
yield_service_result = yield(oauth_client_token)
155155

156156
if yield_service_result.failure? && yield_service_result.result == :not_authorized
157157
refresh_service_result = refresh_token
@@ -162,19 +162,12 @@ def request_with_token_refresh(oauth_client_token)
162162
end
163163

164164
oauth_client_token.reload
165-
yield_service_result = yield # Should contain result=<data> in case of success
165+
yield_service_result = yield(oauth_client_token) # Should contain result=<data> in case of success
166166
end
167167

168168
yield_service_result
169169
end
170170

171-
def with_refreshed_token(&)
172-
token = get_existing_token
173-
return ServiceResult.failure(result: :not_authorized) if token.blank?
174-
175-
request_with_token_refresh(token, &)
176-
end
177-
178171
private
179172

180173
# Check if a OAuthClientToken already exists and return nil otherwise.

app/workers/application_job.rb

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ class ApplicationJob < ::ActiveJob::Base
3636
# to avoid leaking sensitive information to logs
3737
self.log_arguments = false
3838

39-
around_perform :clean_context
39+
around_perform :prepare_job_context
4040

4141
##
4242
# Return a priority number on the given payload
@@ -94,8 +94,9 @@ def reload_mailer_settings!
9494

9595
private
9696

97-
def clean_context
97+
def prepare_job_context
9898
with_clean_request_store do
99+
::OpenProject::Appsignal.tag_request
99100
reload_mailer_settings!
100101

101102
yield

0 commit comments

Comments
 (0)