Skip to content

Commit 49b802c

Browse files
committed
Merge branch 'release/17.5' into dev
2 parents 13f6a6b + 4c05592 commit 49b802c

36 files changed

Lines changed: 1539 additions & 187 deletions

File tree

-9.98 KB
Binary file not shown.
21.1 KB
Loading
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# frozen_string_literal: true
2+
3+
#-- copyright
4+
# OpenProject is an open source project management software.
5+
# Copyright (C) the OpenProject GmbH
6+
#
7+
# This program is free software; you can redistribute it and/or
8+
# modify it under the terms of the GNU General Public License version 3.
9+
#
10+
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
11+
# Copyright (C) 2006-2013 Jean-Philippe Lang
12+
# Copyright (C) 2010-2013 the ChiliProject Team
13+
#
14+
# This program is free software; you can redistribute it and/or
15+
# modify it under the terms of the GNU General Public License
16+
# as published by the Free Software Foundation; either version 2
17+
# of the License, or (at your option) any later version.
18+
#
19+
# This program is distributed in the hope that it will be useful,
20+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
21+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22+
# GNU General Public License for more details.
23+
#
24+
# You should have received a copy of the GNU General Public License
25+
# along with this program; if not, write to the Free Software
26+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
27+
#
28+
# See COPYRIGHT and LICENSE files for more details.
29+
#++
30+
31+
# Pin the external rendering channel so mailer templates never have to
32+
# remember the `render_mode:` / `only_path:` / `static_html:` combination.
33+
# Matching the `.html.erb` / `.text.erb` extension to the helper name keeps
34+
# caller intent visible.
35+
module MailFormattingHelper
36+
def format_mail_html(*, **)
37+
format_text(*, render_mode: :external_html, **)
38+
end
39+
40+
def format_mail_text(*, **)
41+
format_text(*, render_mode: :external_text, **)
42+
end
43+
end

app/mailers/application_mailer.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ class ApplicationMailer < ActionMailer::Base
3434
helper :application, # for format_text
3535
:work_packages, # for css classes
3636
:custom_fields, # for show_value
37+
:mail_formatting, # for format_mail_html / format_mail_text
3738
:mail_layout # for layouting
3839

3940
include OpenProject::LocaleHelper

app/models/work_package/semantic_identifier.rb

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,13 @@ def self.numeric_id?(value)
121121
end
122122
end
123123

124+
# Returns formatted value for inline UI display.
125+
# * Semantic mode: "PROJ-42" (no prefix — self-describing)
126+
# * Classic mode: "#42" (hash-prefixed)
127+
def self.format_display_id(display_id)
128+
display_id.is_a?(String) && display_id.match?(/[A-Za-z]/) ? display_id : "##{display_id}"
129+
end
130+
124131
# Returns the user-facing identifier for this work package.
125132
# In semantic mode: the project-based identifier (e.g. "PROJ-42")
126133
# In classic mode: the numeric database ID
@@ -134,8 +141,7 @@ def display_id
134141
# Semantic mode: "PROJ-42" (no prefix — self-describing)
135142
# Classic mode: "#42" (hash-prefixed)
136143
def formatted_id
137-
did = display_id
138-
did.is_a?(String) && did.match?(/[A-Za-z]/) ? did : "##{did}"
144+
WorkPackage::SemanticIdentifier.format_display_id(display_id)
139145
end
140146

141147
# Override ActiveRecord's default `to_param` so Rails URL helpers

app/seeders/env_data/ldap_seeder.rb

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,20 @@
2929
# See COPYRIGHT and LICENSE files for more details.
3030
module EnvData
3131
class LdapSeeder < Seeder
32+
KNOWN_CONNECTION_KEYS = %w[
33+
host port security tls_verify tls_certificate sync_users
34+
filter basedn binduser bindpassword
35+
login_mapping firstname_mapping lastname_mapping mail_mapping admin_mapping
36+
groupfilter
37+
].freeze
38+
39+
KNOWN_FILTER_KEYS = %w[base filter sync_users group_attribute].freeze
40+
3241
def seed_data!
3342
print_status " ↳ Creating LDAP connection" do
3443
Setting.seed_ldap.each do |name, options|
44+
validate_options!(name, options)
45+
3546
ldap = LdapAuthSource.find_or_initialize_by(name:)
3647

3748
print_ldap_status(ldap)
@@ -51,6 +62,34 @@ def applicable?
5162

5263
private
5364

65+
def validate_options!(name, options)
66+
check_unknown_keys!(options, KNOWN_CONNECTION_KEYS,
67+
scope: "LDAP connection '#{name}'")
68+
69+
filters = options["groupfilter"]
70+
return if filters.blank?
71+
72+
filters.each do |filter_name, filter_options|
73+
check_unknown_keys!(filter_options, KNOWN_FILTER_KEYS,
74+
scope: "LDAP group filter '#{filter_name}' (connection '#{name}')")
75+
end
76+
end
77+
78+
def check_unknown_keys!(options, known_keys, scope:)
79+
unknown = options.keys - known_keys
80+
return if unknown.empty?
81+
82+
raise <<~MSG.strip
83+
#{scope}: unknown configuration key(s): #{unknown.map { |k| env_form(k) }.join(', ')}.
84+
Accepted keys: #{known_keys.map { |k| env_form(k) }.join(', ')}.
85+
Note: in environment variable names, single underscores split path segments and double underscores encode a literal underscore (e.g. LOGIN__MAPPING, not LOGIN_MAPPING).
86+
MSG
87+
end
88+
89+
def env_form(key)
90+
key.gsub("_", "__").upcase
91+
end
92+
5493
# rubocop:disable Metrics/AbcSize
5594
def upsert_settings(ldap, options)
5695
ldap.host = options["host"]

app/services/work_packages/update_ancestors_service.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,8 @@ def derive_attributes(work_package, loader, attributes)
123123

124124
def set_journal_note(work_packages)
125125
work_packages.each do |wp|
126-
wp.journal_notes = I18n.t("work_package.updated_automatically_by_child_changes", child: "##{initiator_work_package.id}")
126+
wp.journal_notes = I18n.t("work_package.updated_automatically_by_child_changes",
127+
child: "##{initiator_work_package.id}")
127128
end
128129
end
129130

app/views/work_package_mailer/_work_package_details.html.erb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,4 +47,4 @@ See COPYRIGHT and LICENSE files for more details.
4747
<% end %>
4848
</ul>
4949

50-
<%= format_text(work_package, :description, only_path: false) %>
50+
<%= format_mail_html(work_package, :description) %>

app/views/work_package_mailer/mentioned.html.erb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
<table <%= placeholder_table_styles(width: "100%", style: "width:100%;") %>>
2727
<tr>
2828
<td style="<%= placeholder_text_styles %>">
29-
<%= format_text @journal.notes, only_path: false %>
29+
<%= format_mail_html @journal.notes %>
3030
</td>
3131
</tr>
3232
</table>

app/views/work_package_mailer/mentioned.text.erb

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@
88
<%= "=" * ((@work_package.formatted_id + " " + @work_package.subject).length + 4) %>
99

1010
<%= I18n.t(:label_comment_added) %>:
11-
<%= strip_tags @journal.notes %>
11+
<%= format_mail_text(
12+
@journal.notes,
13+
object: @work_package,
14+
project: @work_package.project
15+
) %>
1216

1317
<%= "-" * 100 %>

0 commit comments

Comments
 (0)