mirror of
https://github.com/opf/openproject.git
synced 2026-06-13 19:20:00 +00:00
Replace raw and explicit html_safe calls
This commit is contained in:
@@ -34,7 +34,10 @@ See COPYRIGHT and LICENSE files for more details.
|
||||
<% else %>
|
||||
<%= @event.event_title %>
|
||||
<% end %>
|
||||
<%= project_suffix %>
|
||||
<% suffix = project_suffix %>
|
||||
<% if suffix.present? %>
|
||||
(<%= suffix %>)
|
||||
<% end %>
|
||||
</div>
|
||||
<%=
|
||||
render(
|
||||
|
||||
@@ -45,8 +45,7 @@ class Activities::ItemComponent < ViewComponent::Base
|
||||
return if activity_is_from_current_project?
|
||||
|
||||
kind = activity_is_from_subproject? ? "subproject" : "project"
|
||||
suffix = I18n.t("events.title.#{kind}", name: link_to(@event.project.name, @event.project))
|
||||
"(#{suffix})".html_safe # rubocop:disable Rails/OutputSafety
|
||||
helpers.t("events.title.#{kind}_html", name: link_to(@event.project.name, @event.project))
|
||||
end
|
||||
|
||||
def display_user?
|
||||
|
||||
@@ -29,10 +29,11 @@ See COPYRIGHT and LICENSE files for more details.
|
||||
|
||||
<div class="op-activity-list--item-subtitle">
|
||||
<%=
|
||||
# OG: html_safe usage has been double-checked and would otherwise require a lot of i18n key change
|
||||
I18n.t(
|
||||
i18n_key,
|
||||
user: user_html,
|
||||
datetime: datetime_html
|
||||
).html_safe # OG: html_safe usage has been double-checked and would otherwise require a lot of i18n key change
|
||||
user: h(user_html),
|
||||
datetime: h(datetime_html)
|
||||
).html_safe # rubocop:disable Rails/OutputSafety
|
||||
%>
|
||||
</div>
|
||||
|
||||
@@ -48,7 +48,7 @@ module EnterpriseEdition
|
||||
def description
|
||||
@description || begin
|
||||
if I18n.exists?(:description_html, scope: i18n_scope)
|
||||
I18n.t(:description_html, scope: i18n_scope).html_safe
|
||||
helpers.t(:description_html, scope: i18n_scope)
|
||||
else
|
||||
I18n.t(:description, scope: i18n_scope)
|
||||
end
|
||||
@@ -85,7 +85,7 @@ module EnterpriseEdition
|
||||
I18n.t("ee.upsell.plan_name", plan: plan.capitalize)
|
||||
end
|
||||
|
||||
I18n.t("ee.upsell.plan_text_html", plan_name:).html_safe
|
||||
helpers.t("ee.upsell.plan_text_html", plan_name:)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -65,7 +65,7 @@ module EnterpriseEdition
|
||||
end
|
||||
|
||||
def description
|
||||
I18n.t("ee.teaser.description", trial_plan: plan_name).html_safe
|
||||
helpers.t("ee.teaser.description_html", trial_plan: plan_name)
|
||||
end
|
||||
|
||||
def plan_name
|
||||
|
||||
@@ -28,7 +28,11 @@ See COPYRIGHT and LICENSE files for more details.
|
||||
++#%>
|
||||
<%=
|
||||
render(Primer::OpenProject::PageHeader.new) do |header|
|
||||
header.with_title { "#{avatar @placeholder_user} #{h(@placeholder_user.name)}".html_safe }
|
||||
header.with_title do
|
||||
concat avatar(@placeholder_user)
|
||||
concat @placeholder_user.name
|
||||
end
|
||||
|
||||
header.with_breadcrumbs(breadcrumb_items)
|
||||
|
||||
if @current_user.allowed_globally?(:manage_placeholder_user)
|
||||
|
||||
@@ -27,9 +27,10 @@ See COPYRIGHT and LICENSE files for more details.
|
||||
|
||||
++#%>
|
||||
|
||||
<tr
|
||||
<%= "id=\"#{row_css_id}\"".html_safe if row_css_id %>
|
||||
<%= "class=\"#{row_css_class}\"".html_safe if row_css_class %>>
|
||||
<%= content_tag(:tr,
|
||||
id: row_css_id,
|
||||
class: row_css_class
|
||||
) do %>
|
||||
<% columns.each do |column| %>
|
||||
<td class="<%= column_css_class(column) %>">
|
||||
<%= column_value(column) %>
|
||||
@@ -40,4 +41,4 @@ See COPYRIGHT and LICENSE files for more details.
|
||||
<%= link %>
|
||||
<% end %>
|
||||
</td>
|
||||
</tr>
|
||||
<% end %>
|
||||
|
||||
@@ -211,17 +211,15 @@ module Projects
|
||||
def project_status
|
||||
return nil unless user_can_view_project_attributes?
|
||||
|
||||
content = "".html_safe
|
||||
|
||||
status_code = project.status_code
|
||||
|
||||
if status_code
|
||||
classes = helpers.project_status_css_class(status_code)
|
||||
content << content_tag(:span, "", class: "project-status--bulb -inline #{classes}")
|
||||
content << content_tag(:span, helpers.project_status_name(status_code), class: "project-status--name #{classes}")
|
||||
end
|
||||
|
||||
content
|
||||
capture do
|
||||
concat content_tag(:span, "", class: "project-status--bulb -inline #{classes}")
|
||||
concat content_tag(:span, helpers.project_status_name(status_code), class: "project-status--name #{classes}")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def status_explanation
|
||||
@@ -250,7 +248,7 @@ module Projects
|
||||
|
||||
def row_css_class
|
||||
classes = %w[basics context-menu--reveal op-project-row-component]
|
||||
classes << project_css_classes
|
||||
classes += project_css_classes
|
||||
classes << row_css_level_classes
|
||||
|
||||
classes.join(" ")
|
||||
@@ -269,13 +267,13 @@ module Projects
|
||||
end
|
||||
|
||||
def project_css_classes
|
||||
s = " project ".html_safe
|
||||
output = ["project"]
|
||||
|
||||
s << " root" if project.root?
|
||||
s << " child" if project.child?
|
||||
s << (project.leaf? ? " leaf" : " parent")
|
||||
output << "root" if project.root?
|
||||
output << "child" if project.child?
|
||||
output << (project.leaf? ? "leaf" : "parent")
|
||||
|
||||
s
|
||||
output
|
||||
end
|
||||
|
||||
def column_css_class(column)
|
||||
|
||||
@@ -31,7 +31,10 @@ See COPYRIGHT and LICENSE files for more details.
|
||||
<div class="generic-table--flex-container">
|
||||
<div class="generic-table--container <%= container_class %>">
|
||||
<div class="generic-table--results-container">
|
||||
<table class="generic-table" data-controller="table-highlighting" <%= table_id ? "id=\"#{table_id}\"".html_safe : "" %>>
|
||||
<%= content_tag :table,
|
||||
id: table_id,
|
||||
class: "generic-table",
|
||||
data: { controller: "table-highlighting" } do %>
|
||||
<colgroup>
|
||||
<% columns.each do |column| %>
|
||||
<col <%= "opHighlightCol" unless column.attribute == :hierarchy %>>
|
||||
@@ -84,7 +87,7 @@ See COPYRIGHT and LICENSE files for more details.
|
||||
<% end %>
|
||||
<%= render_collection rows %>
|
||||
</tbody>
|
||||
</table>
|
||||
<% end %>
|
||||
<% if inline_create_link && show_inline_create %>
|
||||
<div class="wp-inline-create-button">
|
||||
<%= inline_create_link %>
|
||||
|
||||
@@ -27,9 +27,10 @@ See COPYRIGHT and LICENSE files for more details.
|
||||
|
||||
++#%>
|
||||
|
||||
<tr
|
||||
<%= "id=\"#{row_css_id}\"".html_safe if row_css_id %>
|
||||
<%= "class=\"#{row_css_class}\"".html_safe if row_css_class %>>
|
||||
<%= content_tag(:tr,
|
||||
id: row_css_id,
|
||||
class: row_css_class
|
||||
) do %>
|
||||
<% columns.each do |column| %>
|
||||
<td class="<%= column_css_class(column) %>">
|
||||
<%= column_value(column) %>
|
||||
@@ -40,4 +41,4 @@ See COPYRIGHT and LICENSE files for more details.
|
||||
<%= link %>
|
||||
<% end %>
|
||||
</td>
|
||||
</tr>
|
||||
<% end %>
|
||||
|
||||
@@ -104,7 +104,7 @@ module Settings
|
||||
|
||||
private
|
||||
|
||||
def internal_comments_translation = t("ee.features.internal_comments").html_safe
|
||||
def internal_comments_translation = t("ee.features.internal_comments")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -39,11 +39,11 @@
|
||||
|
||||
user_limit_row.with_column do
|
||||
render(Primer::Beta::Text.new(color: :danger)) do
|
||||
I18n.t(
|
||||
helpers.link_translate(
|
||||
"sharing.warning_user_limit_reached#{'_admin' if User.current.admin?}",
|
||||
upgrade_url: OpenProject::Enterprise.upgrade_url,
|
||||
entity: entity.model_name.human
|
||||
).html_safe
|
||||
i18n_args: { entity: entity.model_name.human },
|
||||
links: { upgrade_url: OpenProject::Enterprise.upgrade_url }
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -90,9 +90,9 @@ class Users::HoverCardComponent < ApplicationComponent
|
||||
remaining_count_link = link_to(t("users.groups.more", count: remaining_count), user_path(@user))
|
||||
|
||||
if remaining_count > 0
|
||||
t("users.groups.summary_with_more", names: summary_links, count_link: remaining_count_link).html_safe
|
||||
t("users.groups.summary_with_more_html", names: summary_links, count_link: remaining_count_link)
|
||||
else
|
||||
t("users.groups.summary", names: summary_links).html_safe
|
||||
t("users.groups.summary_html", names: summary_links)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -29,7 +29,8 @@ See COPYRIGHT and LICENSE files for more details.
|
||||
<%=
|
||||
render(Primer::OpenProject::PageHeader.new) do |header|
|
||||
header.with_title do
|
||||
"#{avatar(@user, hover_card: { active: false })} #{h(@user.name)}".html_safe
|
||||
concat avatar(@user, hover_card: { active: false })
|
||||
concat @user.name
|
||||
end
|
||||
header.with_breadcrumbs(breadcrumb_items)
|
||||
|
||||
|
||||
@@ -42,13 +42,13 @@
|
||||
target: "_blank"
|
||||
)
|
||||
) do
|
||||
I18n.t("js.label_committed_link", revision_identifier: short_revision)
|
||||
I18n.t(:label_committed_link, revision_identifier: short_revision)
|
||||
end
|
||||
I18n.t(
|
||||
"js.label_committed_at",
|
||||
committed_revision_link: committed_text.html_safe,
|
||||
:label_committed_at_html,
|
||||
committed_revision_link: committed_text,
|
||||
date: format_time(changeset.committed_on)
|
||||
).html_safe
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -69,13 +69,13 @@
|
||||
target: "_blank"
|
||||
)
|
||||
) do
|
||||
I18n.t("js.label_committed_link", revision_identifier: short_revision)
|
||||
I18n.t(:label_committed_link, revision_identifier: short_revision)
|
||||
end
|
||||
I18n.t(
|
||||
"js.label_committed_at",
|
||||
committed_revision_link: committed_text.html_safe,
|
||||
t(
|
||||
:label_committed_at_html,
|
||||
committed_revision_link: committed_text,
|
||||
date: format_time(changeset.committed_on)
|
||||
).html_safe
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -96,15 +96,17 @@ module WorkPackages
|
||||
end
|
||||
|
||||
def mobile_description
|
||||
text = if @manually_scheduled
|
||||
I18n.t("work_packages.datepicker_modal.banner.description.manual_mobile")
|
||||
else
|
||||
I18n.t("work_packages.datepicker_modal.banner.description.automatic_mobile")
|
||||
end
|
||||
text =
|
||||
if @manually_scheduled
|
||||
I18n.t("work_packages.datepicker_modal.banner.description.manual_mobile")
|
||||
else
|
||||
I18n.t("work_packages.datepicker_modal.banner.description.automatic_mobile")
|
||||
end
|
||||
|
||||
"#{text} #{render(Primer::Beta::Link.new(tag: :a, href: link, target: '_blank', underline: true)) do
|
||||
I18n.t('work_packages.datepicker_modal.show_relations')
|
||||
end}".html_safe
|
||||
capture do
|
||||
concat text
|
||||
concat render(Primer::Beta::Link.new(tag: :a, href: link, target: "_blank", underline: true)) { I18n.t("work_packages.datepicker_modal.show_relations") }
|
||||
end
|
||||
end
|
||||
|
||||
def overlapping_predecessor?
|
||||
@@ -122,7 +124,7 @@ module WorkPackages
|
||||
|
||||
predecessor_work_packages.filter_map(&:due_date)
|
||||
.max
|
||||
&.before?(@work_package.start_date - 2)
|
||||
&.before?(@work_package.start_date - 2)
|
||||
end
|
||||
|
||||
def predecessor_relations
|
||||
@@ -131,8 +133,8 @@ module WorkPackages
|
||||
|
||||
def predecessor_work_packages
|
||||
@predecessor_work_packages ||= predecessor_relations
|
||||
.includes(:to)
|
||||
.map(&:to)
|
||||
.includes(:to)
|
||||
.map(&:to)
|
||||
end
|
||||
|
||||
def children
|
||||
|
||||
@@ -49,8 +49,13 @@ module WorkPackages
|
||||
end
|
||||
|
||||
def gantt_chart_label
|
||||
label = I18n.t("export.dialog.pdf.export_type.options.gantt.label")
|
||||
gantt_chart_allowed? ? label : (label + enterprise_icon).html_safe # rubocop:disable Rails/OutputSafety
|
||||
capture do
|
||||
concat I18n.t("export.dialog.pdf.export_type.options.gantt.label")
|
||||
|
||||
unless gantt_chart_allowed?
|
||||
concat enterprise_icon
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def pdf_export_types
|
||||
|
||||
@@ -283,7 +283,7 @@ class AccountController < ApplicationController
|
||||
ldap_auth_source_id: user.ldap_auth_source_id
|
||||
}
|
||||
|
||||
flash[:notice] = I18n.t("account.auth_source_login", login: user.login).html_safe
|
||||
flash[:notice] = helpers.t("account.auth_source_login_html", login: user.login)
|
||||
|
||||
redirect_to signin_path(username: user.login)
|
||||
end
|
||||
|
||||
@@ -91,18 +91,16 @@ module Accounts::UserLimits
|
||||
end
|
||||
|
||||
def user_limit_warning
|
||||
warning = if current_user.admin?
|
||||
I18n.t(
|
||||
:warning_user_limit_reached_admin,
|
||||
upgrade_url: OpenProject::Enterprise.upgrade_url
|
||||
)
|
||||
else
|
||||
I18n.t(
|
||||
:warning_user_limit_reached
|
||||
)
|
||||
end
|
||||
|
||||
warning.html_safe
|
||||
if current_user.admin?
|
||||
link_translate(
|
||||
:warning_user_limit_reached_admin,
|
||||
links: { upgrade_url: OpenProject::Enterprise.upgrade_url }
|
||||
)
|
||||
else
|
||||
I18n.t(
|
||||
:warning_user_limit_reached
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
def show_imminent_user_limit_warning!(flash_now: false)
|
||||
@@ -115,12 +113,10 @@ module Accounts::UserLimits
|
||||
# A warning for when the user limit has technically not been reached yet
|
||||
# but if all invited users were to activate their accounts it would be reached.
|
||||
def imminent_user_limit_warning
|
||||
warning = I18n.t(
|
||||
link_translate(
|
||||
:warning_imminent_user_limit,
|
||||
upgrade_url: OpenProject::Enterprise.upgrade_url
|
||||
links: { upgrade_url: OpenProject::Enterprise.upgrade_url }
|
||||
)
|
||||
|
||||
warning.html_safe
|
||||
end
|
||||
|
||||
def user_limit_reached?
|
||||
|
||||
@@ -53,8 +53,8 @@ class WorkPackages::RemindersController < ApplicationController
|
||||
.call(reminder_params)
|
||||
|
||||
if service_result.success?
|
||||
message = I18n.t("work_package.reminders.create_success_message",
|
||||
reminder_time: reminder_chosen_time(service_result.result)).html_safe
|
||||
message = helpers.t("work_package.reminders.create_success_message_html",
|
||||
reminder_time: reminder_chosen_time(service_result.result))
|
||||
respond_with_success_flash_message(message:)
|
||||
else
|
||||
respond_with_error_modal_component(service_result)
|
||||
|
||||
+3
-3
@@ -9,9 +9,9 @@
|
||||
<% end %>
|
||||
<%= render(Primer::Beta::Text.new(tag: :p, mb: 0)) do %>
|
||||
<%=
|
||||
I18n.t(
|
||||
helpers.link_translate(
|
||||
:setting_apiv3_write_readonly_attributes_additional,
|
||||
api_documentation_link: static_link_to(:api_docs)
|
||||
).html_safe
|
||||
links: { api_documentation_link: %i[api_docs] }
|
||||
)
|
||||
%>
|
||||
<% end %>
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
<% end %>
|
||||
|
||||
<%= render(Primer::Beta::Text.new(tag: :p)) do %>
|
||||
<%= I18n.t(
|
||||
<%= helpers.link_translate(
|
||||
:setting_apiv3_cors_origins_text_html,
|
||||
origin_link: ::OpenProject::Static::Links.url_for(:origin_mdn_documentation)
|
||||
).html_safe %>
|
||||
links: { docs_url: %i[origin_mdn_documentation] }
|
||||
) %>
|
||||
<% end %>
|
||||
|
||||
+2
-2
@@ -1,10 +1,10 @@
|
||||
<%=
|
||||
I18n.t(
|
||||
helpers.t(
|
||||
:setting_allowed_link_protocols_text_html,
|
||||
tel_code: content_tag(:code, "tel"),
|
||||
element_code: content_tag(:code, "element"),
|
||||
http_code: content_tag(:code, "http"),
|
||||
https_code: content_tag(:code, "https"),
|
||||
mailto_code: content_tag(:code, "mailto")
|
||||
).html_safe
|
||||
)
|
||||
%>
|
||||
|
||||
+2
-2
@@ -1,8 +1,8 @@
|
||||
<%=
|
||||
I18n.t(
|
||||
helpers.t(
|
||||
:text_notice_security_badge_displayed_html,
|
||||
information_panel_label: I18n.t(:label_information),
|
||||
more_info_url: ::OpenProject::Static::Links.url_for(:security_badge_documentation),
|
||||
information_panel_path: url_helpers.info_admin_index_path
|
||||
).html_safe
|
||||
)
|
||||
%>
|
||||
|
||||
@@ -37,6 +37,8 @@ class ApplicationForm < Primer::Forms::Base
|
||||
end
|
||||
end
|
||||
|
||||
delegate :helpers, to: :ApplicationController
|
||||
|
||||
def url_helpers
|
||||
Rails.application.routes.url_helpers
|
||||
end
|
||||
|
||||
@@ -67,13 +67,20 @@ module EnterpriseTrials
|
||||
|
||||
f.check_box(
|
||||
required: true,
|
||||
label: I18n.t("ee.trial.consent_html").html_safe,
|
||||
label: helpers.link_translate("ee.trial.consent",
|
||||
links: {
|
||||
tos_url: %i[terms_of_service],
|
||||
privacy_url: %i[data_privacy]
|
||||
}),
|
||||
name: :general_consent
|
||||
)
|
||||
|
||||
f.check_box(
|
||||
required: false,
|
||||
label: I18n.t("ee.trial.receive_newsletter_html").html_safe,
|
||||
label: helpers.link_translate("ee.trial.receive_newsletter",
|
||||
links: {
|
||||
newsletter_url: %i[newsletter]
|
||||
}),
|
||||
name: :newsletter_consent
|
||||
)
|
||||
end
|
||||
|
||||
@@ -84,7 +84,7 @@ class My::LookAndFeelForm < ApplicationForm
|
||||
private
|
||||
|
||||
def disable_keyboard_shortcuts_caption
|
||||
attribute_name(:disable_keyboard_shortcuts_caption_html,
|
||||
href: OpenProject::Static::Links.url_for(:shortcuts)).html_safe # rubocop:disable Rails/OutputSafety
|
||||
helpers.link_translate(:"user_preferences.disable_keyboard_shortcuts_caption",
|
||||
links: { docs_url: %i[shortcuts] })
|
||||
end
|
||||
end
|
||||
|
||||
@@ -77,7 +77,7 @@ module Projects
|
||||
f.autocompleter(
|
||||
name: :project_creation_wizard_assignee_custom_field_id,
|
||||
label: I18n.t("settings.project_initiation_request.submission.assignee"),
|
||||
caption: I18n.t("settings.project_initiation_request.submission.assignee_caption_html").html_safe,
|
||||
caption: helpers.t("settings.project_initiation_request.submission.assignee_caption_html"),
|
||||
required: false,
|
||||
input_width: :large,
|
||||
autocomplete_options: {
|
||||
|
||||
@@ -51,16 +51,8 @@ module Projects::Settings::WorkPackages::Activities
|
||||
private
|
||||
|
||||
def caption_text
|
||||
link = render(
|
||||
Primer::Beta::Link.new(
|
||||
href: OpenProject::Static::Links.url_for(:enterprise_features, :internal_comments),
|
||||
underline: true
|
||||
)
|
||||
) do
|
||||
I18n.t("label_learn_more")
|
||||
end
|
||||
|
||||
I18n.t("settings.work_packages.activities.helper_text", link:).html_safe
|
||||
helpers.link_translate("settings.work_packages.activities.helper_text",
|
||||
links: { docs_url: %i[enterprise_features internal_comments] })
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -59,7 +59,7 @@ module ScimClients
|
||||
client_form.select_list(
|
||||
name: :authentication_method,
|
||||
label: ScimClient.human_attribute_name(:authentication_method),
|
||||
caption: I18n.t("admin.scim_clients.form.authentication_method_description_html").html_safe,
|
||||
caption: helpers.t("admin.scim_clients.form.authentication_method_description_html"),
|
||||
input_width: :large,
|
||||
include_blank: false,
|
||||
disabled: model.persisted?,
|
||||
|
||||
@@ -91,13 +91,13 @@ module Settings
|
||||
|
||||
f.text_field(
|
||||
name: :after_first_login_redirect_url,
|
||||
caption: I18n.t(:setting_after_first_login_redirect_url_text_html).html_safe,
|
||||
caption: helpers.t(:setting_after_first_login_redirect_url_text_html),
|
||||
input_width: :large
|
||||
)
|
||||
|
||||
f.text_field(
|
||||
name: :after_login_default_redirect_url,
|
||||
caption: I18n.t(:setting_after_login_default_redirect_url_text_html).html_safe,
|
||||
caption: helpers.t(:setting_after_login_default_redirect_url_text_html),
|
||||
input_width: :large
|
||||
)
|
||||
|
||||
|
||||
@@ -56,8 +56,8 @@ module Settings
|
||||
# @param names [Array<Symbol>] The name(s) of the setting
|
||||
# @return [String] The translated HTML-safe caption
|
||||
def setting_caption(*names)
|
||||
I18n.t("setting_#{names.join('_')}_caption_html", default: nil)&.html_safe \
|
||||
|| I18n.t("setting_#{names.join('_')}_caption", default: nil)
|
||||
ApplicationController.helpers.t("setting_#{names.join('_')}_caption_html", default: nil) ||
|
||||
I18n.t("setting_#{names.join('_')}_caption", default: nil)
|
||||
end
|
||||
|
||||
# Retrieves the current value of a setting
|
||||
|
||||
@@ -79,7 +79,7 @@ module Statuses
|
||||
label: attribute_name(:is_readonly),
|
||||
name: :is_readonly,
|
||||
disabled: readonly_disabled?,
|
||||
caption: I18n.t("statuses.edit.status_readonly_html").html_safe,
|
||||
caption: helpers.t("statuses.edit.status_readonly_html"),
|
||||
data: {
|
||||
"admin--statuses-target": "isReadonlyCheckbox",
|
||||
restricted: readonly_work_packages_restricted?
|
||||
@@ -115,8 +115,8 @@ module Statuses
|
||||
end
|
||||
|
||||
def percent_complete_field_caption
|
||||
I18n.t("statuses.edit.status_percent_complete_text",
|
||||
href: url_helpers.admin_settings_progress_tracking_path).html_safe
|
||||
helpers.link_translate("statuses.edit.status_percent_complete_text",
|
||||
links: { setting_url: url_helpers.admin_settings_progress_tracking_path })
|
||||
end
|
||||
|
||||
def already_default_status?
|
||||
|
||||
@@ -30,17 +30,16 @@
|
||||
|
||||
module AccessibilityHelper
|
||||
def you_are_here_info(condition = true, disabled = nil)
|
||||
if condition && !disabled
|
||||
"<span style = 'display: block' class = 'position-label sr-only'>#{I18n.t(:description_current_position)}</span>".html_safe
|
||||
elsif condition && disabled
|
||||
"<span style = 'display: none' class = 'position-label sr-only'>#{I18n.t(:description_current_position)}</span>".html_safe
|
||||
else
|
||||
""
|
||||
end
|
||||
return "" unless condition
|
||||
|
||||
content_tag(:span,
|
||||
I18n.t(:description_current_position),
|
||||
style: disabled ? "display: none" : "display: block",
|
||||
class: "position-label sr-only")
|
||||
end
|
||||
|
||||
def empty_element_tag
|
||||
@empty_element_tag ||= ApplicationController.new.render_to_string(partial: "accessibility/empty_element_tag").html_safe
|
||||
@empty_element_tag ||= ApplicationController.new.render_to_string(partial: "accessibility/empty_element_tag")
|
||||
end
|
||||
|
||||
# Returns the locale :en for the given menu item if the user locale is
|
||||
|
||||
@@ -130,8 +130,8 @@ module ApplicationHelper
|
||||
class: :user_status_class)
|
||||
end
|
||||
|
||||
def labeled_check_box_tags(name, collection, options = {})
|
||||
collection.sort.map do |object|
|
||||
def labeled_check_box_tags(name, collection, options = {}) # rubocop:disable Metrics/AbcSize
|
||||
fields = collection.sort.map do |object|
|
||||
id = name.gsub(/[\[\]]+/, "_") + object.id.to_s
|
||||
|
||||
object_options = options.inject({}) do |h, (k, v)|
|
||||
@@ -146,18 +146,30 @@ module ApplicationHelper
|
||||
styled_check_box_tag(name, object.id, false, id:) + object.to_s
|
||||
end
|
||||
end
|
||||
end.join.html_safe
|
||||
end
|
||||
|
||||
safe_join(fields)
|
||||
end
|
||||
|
||||
def authoring(created, author, options = {})
|
||||
label = options[:label] || :label_added_time_by
|
||||
I18n.t(label, author: link_to_user(author), age: time_tag(created)).html_safe
|
||||
# Ensure we pass inputs here to html_escape
|
||||
# which will respect html_safe?
|
||||
author = ERB::Util.html_escape link_to_user(author)
|
||||
age = ERB::Util.html_escape time_tag(created)
|
||||
|
||||
# OG: html_safe is used here with explicitly escaped inputs except for the translation file
|
||||
I18n.t(label, author:, age:).html_safe
|
||||
end
|
||||
|
||||
def authoring_at(creation_date, author)
|
||||
return if author.nil?
|
||||
|
||||
I18n.t(:label_added_by_on, author: link_to_user(author), date: creation_date).html_safe
|
||||
author = ERB::Util.html_escape link_to_user(author)
|
||||
date = ERB::Util.html_escape creation_date
|
||||
|
||||
# OG: html_safe is used here to avoid having to change this reusable key
|
||||
I18n.t(:label_added_by_on, author:, date:).html_safe
|
||||
end
|
||||
|
||||
def time_tag(time)
|
||||
@@ -196,7 +208,8 @@ module ApplicationHelper
|
||||
formats = capture(Redmine::Views::OtherFormatsBuilder.new(self), &)
|
||||
unless formats.nil? || formats.strip.empty?
|
||||
content_tag "p", class: "other-formats" do
|
||||
(I18n.t(:label_export_to) + formats).html_safe
|
||||
concat I18n.t(:label_export_to)
|
||||
concat formats
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -251,6 +264,12 @@ module ApplicationHelper
|
||||
.sort_by(&:first)
|
||||
end
|
||||
|
||||
def blank_select_option
|
||||
content_tag(:option,
|
||||
"--- #{t(:actionview_instancetag_blank_option)} ---",
|
||||
disabled: true)
|
||||
end
|
||||
|
||||
def theme_options_for_select
|
||||
[
|
||||
[I18n.t("themes.light"), "light"],
|
||||
@@ -424,7 +443,7 @@ module ApplicationHelper
|
||||
# @param [optional, String] content the content of the ROBOTS tag.
|
||||
# defaults to no index, follow, and no archive
|
||||
def robot_exclusion_tag(content = "NOINDEX,FOLLOW,NOARCHIVE")
|
||||
"<meta name='ROBOTS' content='#{h(content)}' />".html_safe
|
||||
content_tag(:meta, name: "ROBOTS", content:)
|
||||
end
|
||||
|
||||
def permitted_params
|
||||
|
||||
@@ -30,10 +30,9 @@
|
||||
|
||||
module BreadcrumbHelper
|
||||
def nested_breadcrumb_element(section_header, title)
|
||||
output = "".html_safe
|
||||
output << "#{section_header}: "
|
||||
output << content_tag(:b, title)
|
||||
|
||||
output
|
||||
capture do
|
||||
concat "#{section_header}: "
|
||||
concat content_tag(:b, title)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -63,7 +63,7 @@ module ErrorMessageHelper
|
||||
list_of_messages(base_error_messages),
|
||||
text_header_invalid_fields(base_error_messages, fields_error_messages),
|
||||
list_of_messages(fields_error_messages)
|
||||
].compact, "<br/>".html_safe)
|
||||
].compact, tag(:br))
|
||||
end
|
||||
|
||||
def text_header_invalid_fields(base_error_messages, fields_error_messages)
|
||||
@@ -77,6 +77,6 @@ module ErrorMessageHelper
|
||||
def list_of_messages(messages)
|
||||
return if messages.blank?
|
||||
|
||||
safe_join(messages, "<br/>".html_safe)
|
||||
safe_join(messages, tag(:br))
|
||||
end
|
||||
end
|
||||
|
||||
@@ -32,17 +32,15 @@
|
||||
module FlashMessagesOutputSafetyHelper
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
# For .safe_join in join_flash_messages
|
||||
include ActionView::Helpers::OutputSafetyHelper
|
||||
end
|
||||
|
||||
###
|
||||
# Joins individual flash messages with a Line Break Element.
|
||||
#
|
||||
# @param [String|Array<String>] messages the flash messages to join.
|
||||
# @return [String] the joined messages as an HTML-safe string.
|
||||
def join_flash_messages(messages)
|
||||
safe_join(Array(messages), "<br />".html_safe)
|
||||
ApplicationController.helpers.safe_join(
|
||||
Array(messages),
|
||||
ApplicationController.helpers.tag(:br)
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -57,7 +57,10 @@ module HookHelper
|
||||
default_context = { project: @project, hook_caller: self }
|
||||
default_context[:controller] = controller if respond_to?(:controller)
|
||||
default_context[:request] = request if respond_to?(:request)
|
||||
OpenProject::Hook.call_hook(hook, default_context.merge(context)).join(" ").html_safe
|
||||
ApplicationController.helpers.safe_join(
|
||||
OpenProject::Hook.call_hook(hook, default_context.merge(context)),
|
||||
" "
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -30,12 +30,11 @@
|
||||
|
||||
module IconsHelper
|
||||
##
|
||||
# Create an <i> tag with the given icon class names
|
||||
# Create a <i> tag with the given icon class names
|
||||
# and make it aria-hidden since screenreaders otherwise
|
||||
# output the css `content` of the icon.
|
||||
def op_icon(classnames, title: nil)
|
||||
title = "title=\"#{h(title)}\"" unless title.nil?
|
||||
%(<i class="#{classnames}" #{title} aria-hidden="true"></i>).html_safe
|
||||
content_tag(:i, nil, class: classnames, title:, "aria-hidden": true)
|
||||
end
|
||||
|
||||
##
|
||||
|
||||
@@ -37,7 +37,7 @@ module OAuthHelper
|
||||
if strings.empty?
|
||||
I18n.t("oauth.scopes.api_v3")
|
||||
else
|
||||
safe_join(strings.map { |scope| I18n.t("oauth.scopes.#{scope}", default: scope) }, "</br>".html_safe)
|
||||
safe_join(strings.map { |scope| I18n.t("oauth.scopes.#{scope}", default: scope) }, tag(:br))
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -121,7 +121,7 @@ module PaginationHelper
|
||||
|
||||
def pagination_options_section(paginator, params:, allowed_params:)
|
||||
per_page_options = Setting.per_page_options_array
|
||||
return "".html_safe if per_page_options.empty?
|
||||
return "" if per_page_options.empty?
|
||||
|
||||
allowed_params ||= %w[filters sortBy]
|
||||
content_tag(:div, class: "op-pagination--options") do
|
||||
|
||||
@@ -69,10 +69,13 @@ module PasswordHelper
|
||||
def render_password_complexity_hint
|
||||
rules = password_rules_description
|
||||
|
||||
s = OpenProject::Passwords::Evaluator.min_length_description
|
||||
s += "<br> #{rules}" if rules.present?
|
||||
|
||||
s.html_safe
|
||||
capture do
|
||||
concat OpenProject::Passwords::Evaluator.min_length_description
|
||||
if rules.present?
|
||||
concat tag(:br)
|
||||
concat rules
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
@@ -47,16 +47,6 @@ module RepositoriesHelper
|
||||
text&.gsub(%r{^(.{#{length}}[^\n]*)\n.+$}m, '\\1...')
|
||||
end
|
||||
|
||||
def render_properties(properties)
|
||||
unless properties.nil? || properties.empty?
|
||||
content = +""
|
||||
properties.keys.sort.each do |property|
|
||||
content << content_tag("li", raw("<b>#{h property}</b>: <span>#{h properties[property]}</span>"))
|
||||
end
|
||||
content_tag("ul", content.html_safe, class: "properties")
|
||||
end
|
||||
end
|
||||
|
||||
def render_changeset_changes
|
||||
changes = @changeset.file_changes.limit(1000).order(Arel.sql("path")).filter_map do |change|
|
||||
case change.action
|
||||
@@ -113,58 +103,59 @@ module RepositoriesHelper
|
||||
seen.size == 1 ? seen.first : :open
|
||||
end
|
||||
|
||||
def render_changes_tree(tree)
|
||||
return "".html_safe if tree.nil?
|
||||
# rubocop:disable Rails/HelperInstanceVariable
|
||||
def render_changes_tree(tree) # rubocop:disable Metrics/AbcSize,Metrics/PerceivedComplexity
|
||||
return "" if tree.nil?
|
||||
|
||||
items = tree.keys.sort.flat_map do |file|
|
||||
style = "change"
|
||||
items = tree.keys.sort.filter_map do |file|
|
||||
text = File.basename(file)
|
||||
|
||||
if (s = tree[file][:s])
|
||||
style += " folder"
|
||||
path_param = without_leading_slash(to_path_param(@repository.relative_path(file)))
|
||||
text = link_to(h(text),
|
||||
link = link_to(text,
|
||||
show_revisions_path_project_repository_path(project_id: @project,
|
||||
repo_path: path_param,
|
||||
rev: @changeset.identifier),
|
||||
title: I18n.t(:label_folder))
|
||||
|
||||
folder_li = content_tag(:li, text,
|
||||
class: "#{style} icon icon-folder-#{calculate_folder_action(s)}")
|
||||
[folder_li, render_changes_tree(s)]
|
||||
content_tag(:li, safe_join([link, render_changes_tree(s)]),
|
||||
class: "change folder icon icon-folder-#{calculate_folder_action(s)}")
|
||||
elsif (c = tree[file][:c])
|
||||
style += " change-#{c.action}"
|
||||
path_param = without_leading_slash(to_path_param(@repository.relative_path(c.path)))
|
||||
parts = []
|
||||
|
||||
text_parts = []
|
||||
parts <<
|
||||
if c.action == "D"
|
||||
text
|
||||
else
|
||||
link_to(text,
|
||||
entry_revision_project_repository_path(project_id: @project,
|
||||
repo_path: path_param,
|
||||
rev: @changeset.identifier),
|
||||
title: changes_tree_change_title(c.action))
|
||||
end
|
||||
|
||||
unless c.action == "D"
|
||||
text = link_to(h(text),
|
||||
entry_revision_project_repository_path(project_id: @project,
|
||||
repo_path: path_param,
|
||||
rev: @changeset.identifier),
|
||||
title: changes_tree_change_title(c.action))
|
||||
end
|
||||
|
||||
text_parts << text
|
||||
text_parts << " - " << h(c.revision) if c.revision.present?
|
||||
parts << safe_join([" - ", c.revision]) if c.revision.present?
|
||||
|
||||
if c.action == "M"
|
||||
text_parts << " (" << link_to(I18n.t(:label_diff),
|
||||
diff_revision_project_repository_path(project_id: @project,
|
||||
repo_path: path_param,
|
||||
rev: @changeset.identifier)) << ") "
|
||||
diff_link = link_to(I18n.t(:label_diff),
|
||||
diff_revision_project_repository_path(project_id: @project,
|
||||
repo_path: path_param,
|
||||
rev: @changeset.identifier))
|
||||
parts << safe_join([" (", diff_link, ") "])
|
||||
end
|
||||
|
||||
text_parts << " " << content_tag(:span, c.from_path, class: "copied-from") if c.from_path.present?
|
||||
parts << safe_join([" ", content_tag(:span, c.from_path, class: "copied-from")]) if c.from_path.present?
|
||||
|
||||
[changes_tree_li_element(c.action, safe_join(text_parts), style)]
|
||||
changes_tree_li_element(c.action, safe_join(parts), "change change-#{c.action}")
|
||||
end
|
||||
end.compact
|
||||
end
|
||||
|
||||
content_tag(:ul, safe_join(items))
|
||||
end
|
||||
|
||||
# rubocop:enable Rails/HelperInstanceVariable
|
||||
|
||||
def to_utf8_for_repositories(str)
|
||||
return str if str.nil?
|
||||
|
||||
@@ -203,14 +194,16 @@ module RepositoriesHelper
|
||||
|
||||
if str.respond_to?(:force_encoding)
|
||||
str.force_encoding("UTF-8")
|
||||
if !str.valid_encoding?
|
||||
str = str.encode("US-ASCII", invalid: :replace,
|
||||
undef: :replace, replace: "?").encode("UTF-8")
|
||||
unless str.valid_encoding?
|
||||
str = str.encode("US-ASCII",
|
||||
invalid: :replace,
|
||||
undef: :replace, replace: "?")
|
||||
.encode("UTF-8")
|
||||
end
|
||||
else
|
||||
# removes invalid UTF8 sequences
|
||||
begin
|
||||
(str + " ").encode("UTF-8", invalid: :replace, undef: :replace, replace: "?")[0..-3]
|
||||
"#{str} ".encode("UTF-8", invalid: :replace, undef: :replace, replace: "?")[0..-3]
|
||||
rescue Encoding::InvalidByteSequenceError, Encoding::UndefinedConversionError
|
||||
end
|
||||
end
|
||||
@@ -297,17 +290,15 @@ module RepositoriesHelper
|
||||
end
|
||||
|
||||
def changes_tree_li_element(action, text, style)
|
||||
icon_name = case action
|
||||
when "A" then "icon-add"
|
||||
when "D" then "icon-delete"
|
||||
when "C" then "icon-copy"
|
||||
when "R" then "icon-rename"
|
||||
else
|
||||
"icon-arrow-left-right"
|
||||
end
|
||||
icon_name =
|
||||
case action
|
||||
when "A" then "icon-add"
|
||||
when "D" then "icon-delete"
|
||||
when "C" then "icon-copy"
|
||||
when "R" then "icon-rename"
|
||||
else "icon-arrow-left-right"
|
||||
end
|
||||
|
||||
content_tag(:li, text,
|
||||
class: "#{style} icon #{icon_name}",
|
||||
title: changes_tree_change_title(action))
|
||||
content_tag(:li, text, class: "#{style} icon #{icon_name}", title: changes_tree_change_title(action))
|
||||
end
|
||||
end
|
||||
|
||||
@@ -34,21 +34,27 @@ module SearchHelper
|
||||
|
||||
return nil unless split_text.length > 1 || text_on_not_found
|
||||
|
||||
result = +""
|
||||
parts = []
|
||||
total_length = 0
|
||||
|
||||
split_text.each_with_index do |words, i|
|
||||
if result.length > 1200
|
||||
if total_length > 1200
|
||||
# maximum length of the preview reached
|
||||
result << "..."
|
||||
parts << "..."
|
||||
break
|
||||
end
|
||||
|
||||
result << if i.even?
|
||||
abbreviated_text(words)
|
||||
else
|
||||
token_span(tokens, words)
|
||||
end
|
||||
part = if i.even?
|
||||
abbreviated_text(words)
|
||||
else
|
||||
token_span(tokens, words)
|
||||
end
|
||||
|
||||
parts << part
|
||||
total_length += part.length
|
||||
end
|
||||
result.html_safe
|
||||
|
||||
safe_join(parts)
|
||||
end
|
||||
|
||||
def highlight_tokens_in_event(event, tokens)
|
||||
@@ -66,12 +72,11 @@ module SearchHelper
|
||||
def highlight_and_abbreviate_html(event_description, tokens)
|
||||
html = OpenProject::TextFormatting::Renderer.format_text(event_description)
|
||||
highlighted_html = highlight_tokens_in_html(html, tokens)
|
||||
# rubocop:disable Rails/OutputSafety
|
||||
abbreviated_html(highlighted_html).html_safe
|
||||
# rubocop:enable Rails/OutputSafety
|
||||
# This html_safe call is fine, as coming from our html pipeline
|
||||
abbreviated_html(highlighted_html).html_safe # rubocop:disable Rails/OutputSafety
|
||||
end
|
||||
|
||||
def highlight_tokens_in_html(html, tokens)
|
||||
def highlight_tokens_in_html(html, tokens) # rubocop:disable Metrics/AbcSize
|
||||
doc = Nokogiri::HTML::DocumentFragment.parse(html)
|
||||
|
||||
tokens.each do |token|
|
||||
@@ -84,7 +89,7 @@ module SearchHelper
|
||||
|
||||
t = (tokens.index(node.content.downcase) || 0) % 4
|
||||
highlighted_text = escaped_text.gsub(/(#{escaped_token})/i) do
|
||||
%{<span class="search-highlight token-#{t}">#{$1}</span>}
|
||||
content_tag(:span, $1, class: "search-highlight token-#{t}")
|
||||
end
|
||||
|
||||
node.replace(Nokogiri::HTML::DocumentFragment.parse(highlighted_text))
|
||||
|
||||
@@ -163,7 +163,7 @@ module SettingsHelper
|
||||
|
||||
def setting_label(setting, options = {})
|
||||
label = options[:label]
|
||||
return "".html_safe if label == false
|
||||
return "" if label == false
|
||||
|
||||
styled_label_tag(
|
||||
"settings_#{setting}", options[:not_translated_label] || I18n.t(label || "setting_#{setting}"),
|
||||
@@ -192,18 +192,23 @@ module SettingsHelper
|
||||
def build_settings_matrix_head(settings, options = {})
|
||||
content_tag(:tr, class: "form--matrix-header-row") do
|
||||
content_tag(:th, I18n.t(options[:label_choices] || :label_choices),
|
||||
class: "form--matrix-header-cell") +
|
||||
settings.map do |setting|
|
||||
content_tag(:th, class: "form--matrix-header-cell") do
|
||||
hidden_field_tag("settings[#{setting}][]", "") +
|
||||
I18n.t("setting_#{setting}")
|
||||
end
|
||||
end.join.html_safe
|
||||
class: "form--matrix-header-cell") + build_settings_matrix_head_values(settings)
|
||||
end
|
||||
end
|
||||
|
||||
def build_settings_matrix_head_values(settings)
|
||||
values = settings.map do |setting|
|
||||
content_tag(:th, class: "form--matrix-header-cell") do
|
||||
hidden_field_tag("settings[#{setting}][]", "") +
|
||||
I18n.t("setting_#{setting}")
|
||||
end
|
||||
end
|
||||
|
||||
safe_join(values)
|
||||
end
|
||||
|
||||
def build_settings_matrix_body(settings, choices)
|
||||
choices.map do |choice|
|
||||
body = choices.map do |choice|
|
||||
value = choice[:value]
|
||||
caption = choice[:caption] || value.to_s
|
||||
exceptions = Array(choice[:except]).compact
|
||||
@@ -211,11 +216,13 @@ module SettingsHelper
|
||||
content_tag(:td, caption, class: "form--matrix-cell") +
|
||||
settings_matrix_tds(settings, exceptions, value)
|
||||
end
|
||||
end.join.html_safe # rubocop:disable Rails/OutputSafety
|
||||
end
|
||||
|
||||
safe_join(body)
|
||||
end
|
||||
|
||||
def settings_matrix_tds(settings, exceptions, value)
|
||||
settings.map do |setting|
|
||||
tds = settings.map do |setting|
|
||||
content_tag(:td, class: "form--matrix-checkbox-cell") do
|
||||
unless exceptions.include?(setting)
|
||||
styled_check_box_tag("settings[#{setting}][]", value,
|
||||
@@ -223,14 +230,16 @@ module SettingsHelper
|
||||
disabled_setting_option(setting).merge(id: "#{setting}_#{value}"))
|
||||
end
|
||||
end
|
||||
end.join.html_safe # rubocop:disable Rails/OutputSafety
|
||||
end
|
||||
|
||||
safe_join(tds)
|
||||
end
|
||||
|
||||
def setting_multiselect_choice(setting, choice, options)
|
||||
def setting_multiselect_choice(setting, choice, options) # rubocop:disable Metrics/AbcSize
|
||||
text, value, choice_options = (choice.is_a?(Array) ? choice : [choice, choice])
|
||||
choice_options = disabled_setting_option(setting)
|
||||
.merge(choice_options || {})
|
||||
.merge(options.except(:id))
|
||||
.merge(choice_options || {})
|
||||
.merge(options.except(:id))
|
||||
choice_options[:id] = "#{setting}_#{value}"
|
||||
|
||||
content_tag(:label, class: "form--label-with-check-box") do
|
||||
@@ -255,7 +264,7 @@ module SettingsHelper
|
||||
if writable_setting?(setting)
|
||||
yield
|
||||
else
|
||||
"".html_safe
|
||||
ActiveSupport::SafeBuffer.new
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -80,7 +80,6 @@ module TextFormattingHelper
|
||||
end
|
||||
|
||||
def truncate_formatted_text(text, length: 120, replace_newlines: true)
|
||||
# rubocop:disable Rails/OutputSafety
|
||||
stripped_text = strip_tags(format_text(text.to_s))
|
||||
|
||||
stripped_text = if length
|
||||
@@ -91,13 +90,13 @@ module TextFormattingHelper
|
||||
.strip
|
||||
|
||||
if replace_newlines
|
||||
stripped_text
|
||||
.gsub(/[\r\n]+/, "<br />")
|
||||
stripped_text.gsub!(/[\r\n]+/, "<br/>")
|
||||
else
|
||||
stripped_text
|
||||
end
|
||||
.html_safe
|
||||
# rubocop:enable Rails/OutputSafety
|
||||
|
||||
# Sanitized through strip_tags
|
||||
stripped_text.html_safe # rubocop:disable Rails/OutputSafety
|
||||
end
|
||||
|
||||
def truncate_multiline(string, length)
|
||||
|
||||
@@ -89,11 +89,12 @@ module UsersHelper
|
||||
|
||||
# Create buttons to lock/unlock a user and reset failed logins
|
||||
def build_change_user_status_action(user)
|
||||
result = "".html_safe
|
||||
iterate_user_statusses(user) do |title, name|
|
||||
result << ((yield title, name) + " ") # rubocop:disable Style/StringConcatenation
|
||||
capture do
|
||||
iterate_user_statusses(user) do |title, name|
|
||||
concat yield(title, name)
|
||||
concat " "
|
||||
end
|
||||
end
|
||||
result
|
||||
end
|
||||
|
||||
def iterate_user_statusses(user)
|
||||
|
||||
@@ -45,7 +45,7 @@ module VersionsHelper
|
||||
|
||||
html_options = html_options.merge(id: link_to_version_id(version))
|
||||
|
||||
link_name = options[:before_text].to_s.html_safe + format_version_name(version, options[:project] || @project)
|
||||
link_name = format_version_name(version, options[:project] || @project) # rubocop:disable Rails/HelperInstanceVariable
|
||||
link_to_if version.visible?,
|
||||
link_name,
|
||||
{ controller: "/versions", action: "show", id: version },
|
||||
@@ -57,7 +57,7 @@ module VersionsHelper
|
||||
%i[start_date due_date]
|
||||
.filter { |attr| version.send(attr) }
|
||||
.map { |attr| "#{Version.human_attribute_name(attr)} #{format_date(version.send(attr))}" }
|
||||
safe_join(formatted_dates, "<br>".html_safe)
|
||||
safe_join(formatted_dates, tag(:br))
|
||||
end
|
||||
|
||||
def link_to_version_id(version)
|
||||
|
||||
@@ -39,7 +39,7 @@ module WorkPackagesHelper
|
||||
# link_to_work_package(package, link_subject: true) # => Defect #6: This is the subject (everything within the link)
|
||||
# link_to_work_package(package, display_project: true) # => Foo - Defect #6: This is the subject
|
||||
def link_to_work_package(work_package, display_project: false, link_subject: false) # rubocop:disable Metrics/AbcSize
|
||||
output = "".html_safe
|
||||
output = ActiveSupport::SafeBuffer.new
|
||||
output << "#{work_package.project} - " if display_project && work_package.project_id
|
||||
|
||||
link = link_to(work_package_path(work_package),
|
||||
@@ -101,22 +101,6 @@ module WorkPackagesHelper
|
||||
end
|
||||
end
|
||||
|
||||
def work_package_associations_to_address(associated)
|
||||
ret = "".html_safe
|
||||
|
||||
ret += content_tag(:p, I18n.t(:text_destroy_with_associated), class: "bold")
|
||||
|
||||
ret += content_tag(:ul) do
|
||||
associated.inject("".html_safe) do |list, associated_class|
|
||||
list += content_tag(:li, associated_class.model_name.human, class: "decorated")
|
||||
|
||||
list
|
||||
end
|
||||
end
|
||||
|
||||
ret
|
||||
end
|
||||
|
||||
def back_url_is_wp_show?
|
||||
route = Rails.application.routes.recognize_path(params[:back_url] || request.env["HTTP_REFERER"])
|
||||
route[:controller] == "work_packages" && route[:action] == "index" && route[:state]&.match?(/^\d+/)
|
||||
|
||||
@@ -51,7 +51,7 @@ class Activities::ProjectActivityProvider < Activities::BaseActivityProvider
|
||||
end
|
||||
|
||||
def event_title(event)
|
||||
I18n.t("events.title.project", name: event["project_name"])
|
||||
I18n.t("events.title.project_html", name: event["project_name"])
|
||||
end
|
||||
|
||||
def event_path(event)
|
||||
|
||||
@@ -33,14 +33,10 @@ See COPYRIGHT and LICENSE files for more details.
|
||||
<% html_title t(:label_login) %>
|
||||
<%= call_hook :view_account_login_top %>
|
||||
|
||||
<%
|
||||
signin_link = link_to I18n.t("label_here"), signin_path
|
||||
instruction_text = I18n.t "instructions_#{instructions}", signin: signin_link
|
||||
%>
|
||||
|
||||
<div id="login-form" class="form -bordered">
|
||||
<h1><%= I18n.t(:label_login) %></h1>
|
||||
<hr class="form--separator">
|
||||
<p><%= instruction_text.html_safe %></p>
|
||||
<p><%= link_translate "instructions_#{instructions}",
|
||||
links: { signin_url: signin_path } %></p>
|
||||
</div>
|
||||
<%= call_hook :view_account_login_bottom %>
|
||||
|
||||
@@ -31,7 +31,13 @@ See COPYRIGHT and LICENSE files for more details.
|
||||
|
||||
<%=
|
||||
render Primer::OpenProject::PageHeader.new do |header|
|
||||
header.with_title { (@author.nil? ? t(:label_activity) : t(:label_user_activity, value: link_to_user(@author))).html_safe }
|
||||
header.with_title do
|
||||
if @author.nil?
|
||||
t(:label_activity)
|
||||
else
|
||||
t(:label_user_activity_html, value: link_to_user(@author))
|
||||
end
|
||||
end
|
||||
header.with_description { t(:label_date_from_to, start: format_date(@date_to - @days), end: format_date(@date_to - 1)) }
|
||||
header.with_breadcrumbs(@project ? [{ href: project_overview_path(@project.id), text: @project.name }, t(:label_activity)] : nil)
|
||||
end
|
||||
|
||||
@@ -62,10 +62,12 @@ See COPYRIGHT and LICENSE files for more details.
|
||||
|
||||
<p>
|
||||
<%= t(
|
||||
"backup.reset_token.verification",
|
||||
word: "<em class=\"danger-zone--expected-value\">#{t("backup.reset_token.verification_word_#{action}")}</em>",
|
||||
"backup.reset_token.verification_html",
|
||||
word: content_tag(:em,
|
||||
t("backup.reset_token.verification_word_#{action}"),
|
||||
class: "danger-zone--expected-value"),
|
||||
action: t("backup.reset_token.verification_word_#{action}")
|
||||
).html_safe %>
|
||||
) %>
|
||||
</p>
|
||||
<div class="danger-zone--verification">
|
||||
<input type="text" name="login_verification">
|
||||
|
||||
@@ -58,14 +58,12 @@ See COPYRIGHT and LICENSE files for more details.
|
||||
scheme: :warning,
|
||||
icon: :alert
|
||||
)) do
|
||||
<<~STR.html_safe
|
||||
Starting with OpenProject 16.0, PostgreSQL 16 is required to use OpenProject.
|
||||
Your installation will remain functional with your current database, but anticipate incompatability
|
||||
in future releases.
|
||||
<br/>
|
||||
We have prepared #{static_link_to(:postgres_17_upgrade, label: 'upgrade guides for all installation methods')}.
|
||||
You can perform the upgrade ahead of the next release at any time by following the guides.
|
||||
STR
|
||||
link_translate(
|
||||
:"admin.info.database_deprecation_html",
|
||||
links: {
|
||||
upgrade_guide: %i[postgres_17_upgrade]
|
||||
}
|
||||
)
|
||||
end
|
||||
component.with_attribute(key: "Deprecation warning", value:)
|
||||
end
|
||||
@@ -103,7 +101,7 @@ end %>
|
||||
|
||||
component.with_attribute(
|
||||
key: t(:label_storage_for),
|
||||
value: safe_join(entries[:labels], "<br/>".html_safe)
|
||||
value: safe_join(entries[:labels], tag(:br))
|
||||
)
|
||||
|
||||
component.with_attribute(
|
||||
|
||||
@@ -29,9 +29,9 @@ See COPYRIGHT and LICENSE files for more details.
|
||||
|
||||
<%=
|
||||
settings_primer_form_with(scope: :settings,
|
||||
url: admin_settings_authentication_path(tab: params[:tab]),
|
||||
data: { turbo_method: :patch },
|
||||
method: :patch) do |f|
|
||||
url: admin_settings_authentication_path(tab: params[:tab]),
|
||||
data: { turbo_method: :patch },
|
||||
method: :patch) do |f|
|
||||
render_inline_settings_form(f) do |form|
|
||||
disabled = OpenProject::Configuration.disable_password_login?
|
||||
|
||||
@@ -42,10 +42,8 @@ See COPYRIGHT and LICENSE files for more details.
|
||||
icon: :info,
|
||||
my: 2
|
||||
) do
|
||||
I18n.t(
|
||||
:note_password_login_disabled,
|
||||
configuration: static_link_to(:disable_password_login, label: I18n.t('label_configuration'))
|
||||
).html_safe
|
||||
link_translate(:note_password_login_disabled,
|
||||
links: { configuration_url: %i[disable_password_login] })
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -64,7 +62,7 @@ See COPYRIGHT and LICENSE files for more details.
|
||||
group.check_box(value:,
|
||||
label: I18n.t("label_password_rule_#{value}"),
|
||||
checked: OpenProject::Passwords::Evaluator.active_rule?(value),
|
||||
)
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -37,10 +37,10 @@ See COPYRIGHT and LICENSE files for more details.
|
||||
name: :omniauth_direct_login_provider,
|
||||
input_width: :medium,
|
||||
label: I18n.t(:setting_omniauth_direct_login_provider),
|
||||
caption: I18n.t(
|
||||
caption: t(
|
||||
"settings.authentication.omniauth_direct_login_hint_html",
|
||||
internal_path: internal_signin_url
|
||||
).html_safe,
|
||||
),
|
||||
include_blank: I18n.t(:label_none_parentheses)
|
||||
) do |select|
|
||||
AuthProvider
|
||||
|
||||
@@ -102,7 +102,11 @@ See COPYRIGHT and LICENSE files for more details.
|
||||
<div class="op-toast -warning icon-warning d-none"
|
||||
data-user-limit-target="limitWarning">
|
||||
<div class="op-toast--content">
|
||||
<p><%= I18n.t("warning_user_limit_reached#{'_admin' if current_user.admin?}", upgrade_url: OpenProject::Enterprise.upgrade_url).html_safe %></p>
|
||||
<p><%=
|
||||
link_translate(
|
||||
"warning_user_limit_reached#{'_admin' if current_user.admin?}",
|
||||
links: { upgrade_url: OpenProject::Enterprise.upgrade_url }
|
||||
) %>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
@@ -29,7 +29,11 @@ See COPYRIGHT and LICENSE files for more details.
|
||||
|
||||
<%=
|
||||
render Primer::OpenProject::PageHeader.new do |header|
|
||||
header.with_title { "#{avatar(@news.author)} #{h @news.title}".html_safe }
|
||||
header.with_title do
|
||||
concat avatar(@news.author)
|
||||
concat @news.title
|
||||
end
|
||||
|
||||
header.with_breadcrumbs(
|
||||
[
|
||||
{ href: project_overview_path(@project.id), text: @project.name },
|
||||
|
||||
@@ -27,7 +27,7 @@ See COPYRIGHT and LICENSE files for more details.
|
||||
|
||||
++#%>
|
||||
<% name = @placeholder_user.name %>
|
||||
<% html_title(t(:label_administration), t("placeholder_users.deletion_info.heading", name: name).to_s) -%>
|
||||
<% html_title(t(:label_administration), t("placeholder_users.deletion_info.heading_html", name:).to_s) -%>
|
||||
|
||||
<%= labelled_tabular_form_for(
|
||||
:placeholder_user,
|
||||
@@ -40,11 +40,7 @@ See COPYRIGHT and LICENSE files for more details.
|
||||
<div class='wiki'>
|
||||
<section class="form--section">
|
||||
<h3 class="form--section-title">
|
||||
<%= t(
|
||||
"placeholder_users.deletion_info.heading",
|
||||
name: content_tag(:em, name)
|
||||
)
|
||||
.html_safe %>
|
||||
<%= t("placeholder_users.deletion_info.heading_html", name: content_tag(:em, name)) %>
|
||||
</h3>
|
||||
|
||||
<p>
|
||||
@@ -56,9 +52,9 @@ See COPYRIGHT and LICENSE files for more details.
|
||||
</p>
|
||||
<p>
|
||||
<%= t(
|
||||
"placeholder_users.deletion_info.confirmation",
|
||||
"placeholder_users.deletion_info.confirmation_html",
|
||||
name: content_tag(:em, name, class: "danger-zone--expected-value")
|
||||
).html_safe %>
|
||||
) %>
|
||||
</p>
|
||||
<div class="danger-zone--verification">
|
||||
<input type="text" name="name_verification">
|
||||
|
||||
@@ -31,4 +31,4 @@ See COPYRIGHT and LICENSE files for more details.
|
||||
|
||||
<p><%= t("copy_project.text.failed", source_project_name: @source_project.name, target_project_name: @target_project_name) %></p>
|
||||
|
||||
<%= @errors.join("<br/>".html_safe) %>
|
||||
<%= safe_join @errors, tag(:br) %>
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
<%#-- copyright
|
||||
OpenProject is an open source project management software.
|
||||
Copyright (C) the OpenProject GmbH
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License version 3.
|
||||
|
||||
OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
|
||||
Copyright (C) 2006-2013 Jean-Philippe Lang
|
||||
Copyright (C) 2010-2013 the ChiliProject Team
|
||||
|
||||
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.
|
||||
|
||||
See COPYRIGHT and LICENSE files for more details.
|
||||
|
||||
++#%>
|
||||
|
||||
<% if @properties.present? %>
|
||||
<ul class="properties">
|
||||
<% @properties.keys.sort.each do |property| %>
|
||||
<li>
|
||||
<b><%= property %></b>:
|
||||
<span><%= @properties[property] %></span>
|
||||
</li>
|
||||
<% end %>
|
||||
</ul>
|
||||
<% end %>
|
||||
@@ -32,7 +32,7 @@ See COPYRIGHT and LICENSE files for more details.
|
||||
|
||||
<p><%= render partial: "link_to_functions" %></p>
|
||||
|
||||
<%= render_properties(@properties) %>
|
||||
<%= render partial: "properties" %>
|
||||
|
||||
<%= unless @changesets.empty?
|
||||
render(
|
||||
|
||||
@@ -42,12 +42,15 @@ See COPYRIGHT and LICENSE files for more details.
|
||||
<%= styled_form_tag(project_repository_path(@project), method: :delete, class: "danger-zone") do %>
|
||||
<section class="form--section">
|
||||
<h3 class="form--section-title">
|
||||
<%= t("repositories.destroy.title", repository_type: "<em>#{h(@repository.repository_type)} - #{t(:project_module_repository)}</em>").html_safe %>
|
||||
<%= t("repositories.destroy.title_html", repository_type: content_tag(:em, "#{@repository.repository_type} - #{t(:project_module_repository)}")) %>
|
||||
</h3>
|
||||
<p>
|
||||
<%= t("repositories.destroy.subtitle", repository_type: "#{h(@repository.repository_type)} - #{t(:project_module_repository)}", project_name: h(@project.identifier)).html_safe %> <br>
|
||||
<%= t("repositories.destroy.subtitle",
|
||||
repository_type: "#{@repository.repository_type} - #{t(:project_module_repository)}",
|
||||
project_name: @project.identifier) %>
|
||||
<br/>
|
||||
<%= t("repositories.destroy.confirmation") %>
|
||||
<br>
|
||||
<br/>
|
||||
<%= t("repositories.destroy.managed_path_note", path: @repository.root_url) %>
|
||||
</p>
|
||||
<p class="danger-zone--warning">
|
||||
@@ -55,7 +58,8 @@ See COPYRIGHT and LICENSE files for more details.
|
||||
<span><%= t("repositories.destroy.info") %></span>
|
||||
</p>
|
||||
<p>
|
||||
<%= t("repositories.destroy.repository_verification", identifier: "<em class=\"danger-zone--expected-value\">#{h(@project.identifier)}</em>").html_safe %>
|
||||
<%= t("repositories.destroy.repository_verification_html",
|
||||
identifier: content_tag(:em, @project.identifier, class: "danger-zone--expected-value")) %>
|
||||
</p>
|
||||
<div class="danger-zone--verification">
|
||||
<input type="text">
|
||||
@@ -66,13 +70,13 @@ See COPYRIGHT and LICENSE files for more details.
|
||||
title: t(:button_delete),
|
||||
disabled: true,
|
||||
class: "-primary" do %>
|
||||
<%= op_icon("button--icon icon-delete") %>
|
||||
<span class="button--text"><%= t(:button_delete) %></span>
|
||||
<%= op_icon("button--icon icon-delete") %>
|
||||
<span class="button--text"><%= t(:button_delete) %></span>
|
||||
<% end %>
|
||||
<%= link_to project_settings_repository_path(@project),
|
||||
title: t(:button_cancel),
|
||||
class: "button -with-icon icon-cancel" do %>
|
||||
<%= t(:button_cancel) %>
|
||||
<%= t(:button_cancel) %>
|
||||
<% end %>
|
||||
</div>
|
||||
</section>
|
||||
@@ -80,14 +84,20 @@ See COPYRIGHT and LICENSE files for more details.
|
||||
<% else %>
|
||||
<div class="op-toast -warning">
|
||||
<div class="op-toast--content">
|
||||
<p><strong><%= t("repositories.destroy.title_not_managed", repository_type: "<em>#{h(@repository.repository_type)} - #{t(:project_module_repository)}</em>").html_safe %></strong><br></p>
|
||||
<p>
|
||||
<strong>
|
||||
<%= t("repositories.destroy.title_not_managed",
|
||||
repository_type: content_tag(:em, "#{@repository.repository_type} - #{t(:project_module_repository)}")) %>
|
||||
</strong>
|
||||
<br/>
|
||||
</p>
|
||||
<p>
|
||||
<%= t(
|
||||
"repositories.destroy.subtitle_not_managed",
|
||||
repository_type: "#{h(@repository.repository_type)} - #{t(:project_module_repository)}",
|
||||
project_name: h(@project.identifier),
|
||||
url: h(@repository.url)
|
||||
).html_safe %> <br>
|
||||
"repositories.destroy.subtitle_not_managed_html",
|
||||
repository_type: "#{@repository.repository_type} - #{t(:project_module_repository)}",
|
||||
project_name: @project.identifier,
|
||||
url: @repository.url
|
||||
) %> <br>
|
||||
</p>
|
||||
<p>
|
||||
<span><%= t("repositories.destroy.info_not_managed") %></span>
|
||||
@@ -106,7 +116,7 @@ See COPYRIGHT and LICENSE files for more details.
|
||||
<%= link_to project_settings_repository_path(@project),
|
||||
title: t(:button_cancel),
|
||||
class: "button -with-icon icon-cancel" do %>
|
||||
<%= t(:button_cancel) %>
|
||||
<%= t(:button_cancel) %>
|
||||
<% end %>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -65,14 +65,11 @@ See COPYRIGHT and LICENSE files for more details.
|
||||
<%= render partial: 'common/diff', locals: { diff: @diff, diff_type: @diff_type } %>
|
||||
<% end -%>
|
||||
<%= other_formats_links do |f| %>
|
||||
<% unidiff_link = f.link_to 'Diff', url: permitted_params.repository_diff.to_h, caption: 'Unified diff' %>
|
||||
<% if !@path.blank? %>
|
||||
<% unidiff_link.gsub!('?', '&') %>
|
||||
<% end %>
|
||||
<% wrong_url = CGI::escapeHTML(CGI.escape(with_leading_slash(@path))).concat('.diff') %>
|
||||
<% good_url = '.diff'.concat('?repo_path=', CGI.escape(without_leading_slash(@path)).gsub('&', '%26')) %>
|
||||
<% unidiff_link.gsub!(wrong_url, good_url) %>
|
||||
<%= unidiff_link.html_safe %>
|
||||
<%
|
||||
unidiff_params = permitted_params.repository_diff.to_h.merge(format: "diff")
|
||||
unidiff_params[:repo_path] = without_leading_slash(@path) if @path.present?
|
||||
%>
|
||||
<%= f.link_to "Diff", url: unidiff_params, caption: "Unified diff" %>
|
||||
<% end %>
|
||||
|
||||
<% html_title(h(with_leading_slash(@path)), 'Diff') -%>
|
||||
|
||||
@@ -34,7 +34,7 @@ See COPYRIGHT and LICENSE files for more details.
|
||||
<%= render partial: "dir_list" %>
|
||||
<% end %>
|
||||
|
||||
<%= render_properties(@properties) %>
|
||||
<%= render partial: "properties" %>
|
||||
|
||||
<div class="repository--revision-toolbar">
|
||||
<%# rev => nil prevents overwriting the rev parameter queried for in the form with the parameter from the request %>
|
||||
|
||||
@@ -36,12 +36,12 @@
|
||||
<tr>
|
||||
<td style="<%= placeholder_text_styles %>">
|
||||
<%
|
||||
formatted_actions = @allowed_work_package_actions.map do |action|
|
||||
"<span style=\"font-weight:bold;\">#{action.downcase}</span>"
|
||||
allowed_actions = @allowed_work_package_actions.map do |action|
|
||||
content_tag(:span, action.downcase, class: "-bold")
|
||||
end.to_sentence
|
||||
%>
|
||||
|
||||
<%= t("mail.sharing.work_packages.allowed_actions", allowed_actions: formatted_actions).html_safe %>
|
||||
<%= t("mail.sharing.work_packages.allowed_actions_html", allowed_actions:) %>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
<%= "=" * (("# " + @work_package.id.to_s + @work_package.subject).length + 4) %>
|
||||
|
||||
<%= I18n.t("mail.work_packages.reason.shared") %>:
|
||||
<%= t("mail.sharing.work_packages.allowed_actions", allowed_actions: @allowed_work_package_actions.to_sentence).html_safe %>
|
||||
<%= t("mail.sharing.work_packages.allowed_actions_html", allowed_actions: @allowed_work_package_actions.to_sentence) %>
|
||||
|
||||
<%= t("mail.sharing.work_packages.open_work_package") %>
|
||||
<%= @url %>
|
||||
|
||||
@@ -30,7 +30,7 @@ See COPYRIGHT and LICENSE files for more details.
|
||||
<p>
|
||||
<% mail_link = content_tag(:a, @email, href: "mailto:#{@email}") %>
|
||||
<% host_link = content_tag(:a, Setting.app_title, href: Rails.application.root_url) %>
|
||||
<%= t("mail_user_activation_limit_reached.message", email: mail_link, host: host_link).html_safe %>
|
||||
<%= t("mail_user_activation_limit_reached.message_html", email: mail_link, host: host_link) %>
|
||||
</p>
|
||||
|
||||
<span><%= t("mail_user_activation_limit_reached.steps.label") %></span>
|
||||
|
||||
@@ -27,7 +27,7 @@ See COPYRIGHT and LICENSE files for more details.
|
||||
|
||||
++#%>
|
||||
|
||||
<%= t("mail_user_activation_limit_reached.message", email: @email, host: Rails.application.root_url) %>
|
||||
<%= t("mail_user_activation_limit_reached.message_html", email: @email, host: Rails.application.root_url) %>
|
||||
|
||||
<%= t("mail_user_activation_limit_reached.steps.label") %>
|
||||
- <%= t("mail_user_activation_limit_reached.steps.a").sub(link_regex, OpenProject::Enterprise.upgrade_url) %>
|
||||
|
||||
@@ -28,11 +28,11 @@ See COPYRIGHT and LICENSE files for more details.
|
||||
++#%>
|
||||
<%= fields_for :pref, @user.pref, builder: TabularFormBuilder, lang: current_language do |pref_fields| %>
|
||||
<%= render Settings::TimeZoneSettingComponent.new(
|
||||
"time_zone",
|
||||
form: pref_fields,
|
||||
include_blank: false,
|
||||
container_class: defined?(input_size) ? "-#{input_size}" : "-wide"
|
||||
) %>
|
||||
"time_zone",
|
||||
form: pref_fields,
|
||||
include_blank: false,
|
||||
container_class: defined?(input_size) ? "-#{input_size}" : "-wide"
|
||||
) %>
|
||||
<div class="form--field">
|
||||
<%= pref_fields.select :theme, theme_options_for_select, container_class: "-middle" %> <!-- TODO -->
|
||||
<div class="form--field-instructions">
|
||||
@@ -44,10 +44,8 @@ See COPYRIGHT and LICENSE files for more details.
|
||||
<%= pref_fields.check_box :disable_keyboard_shortcuts,
|
||||
label: I18n.t("activerecord.attributes.user_preference.disable_keyboard_shortcuts") %>
|
||||
<span class="form--field-instructions">
|
||||
<%= I18n.t(
|
||||
"activerecord.attributes.user_preference.disable_keyboard_shortcuts_caption_html",
|
||||
href: OpenProject::Static::Links.url_for(:shortcuts)
|
||||
).html_safe %>
|
||||
<%= link_translate(:"user_preferences.disable_keyboard_shortcuts_caption",
|
||||
links: { docs_url: %i[shortcuts] }) %>
|
||||
</span>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
@@ -48,8 +48,12 @@ See COPYRIGHT and LICENSE files for more details.
|
||||
<%= t("work_package.destroy.title") %>
|
||||
</h3>
|
||||
|
||||
<%= work_package_associations_to_address(associated) %>
|
||||
|
||||
<p class="bold"><%= t(:text_destroy_with_associated) %></p>
|
||||
<ul>
|
||||
<% associated.each do |associated_class| %>
|
||||
<li class="decorated"><%= associated_class.model_name.human %></li>
|
||||
<% end %>
|
||||
</ul>
|
||||
<p>
|
||||
<strong><%= t(:text_destroy_what_to_do) %></strong>
|
||||
</p>
|
||||
@@ -108,9 +112,9 @@ See COPYRIGHT and LICENSE files for more details.
|
||||
<% end %>
|
||||
<div class="danger-zone--verification">
|
||||
<%= styled_button_tag title: t(:button_delete), class: "-primary" do
|
||||
concat content_tag :i, "", class: "button--icon icon-delete"
|
||||
concat content_tag :span, t(:button_delete), class: "button--text"
|
||||
end %>
|
||||
concat content_tag :i, "", class: "button--icon icon-delete"
|
||||
concat content_tag :span, t(:button_delete), class: "button--text"
|
||||
end %>
|
||||
<%= link_to_function t(:button_cancel),
|
||||
"history.back()",
|
||||
title: t(:button_cancel),
|
||||
|
||||
@@ -39,18 +39,19 @@ See COPYRIGHT and LICENSE files for more details.
|
||||
<label class="form--label" for="source_type_id"><%= Type.model_name.human %></label>
|
||||
<%= select_tag(
|
||||
"source_type_id",
|
||||
"<option value=\"\">--- #{t(:actionview_instancetag_blank_option)} ---</option>".html_safe +
|
||||
"<option value=\"any\">--- #{t(:label_copy_same_as_target)} ---</option>".html_safe +
|
||||
options_from_collection_for_select(@types, "id", "name", @source_type && @source_type.id), class: "form--select"
|
||||
(blank_select_option +
|
||||
content_tag(:option, "--- #{t(:label_copy_same_as_target)} ---") +
|
||||
options_from_collection_for_select(@types, "id", "name", @source_type && @source_type.id)),
|
||||
class: "form--select"
|
||||
) %>
|
||||
</div>
|
||||
<div class="form--select-container -middle">
|
||||
<label class="form--label" for='source_role_id'><%= Role.model_name.human %></label>
|
||||
<%= select_tag(
|
||||
"source_role_id",
|
||||
"<option value=\"\">--- #{t(:actionview_instancetag_blank_option)} ---</option>".html_safe +
|
||||
"<option value=\"any\">--- #{t(:label_copy_same_as_target)} ---</option>".html_safe +
|
||||
options_from_collection_for_select(@roles, "id", "name", @source_role && @source_role.id), class: "form--select"
|
||||
(blank_select_option +
|
||||
content_tag(:option, "--- #{t(:label_copy_same_as_target)} ---") +
|
||||
options_from_collection_for_select(@roles, "id", "name", @source_type && @source_type.id)),
|
||||
) %>
|
||||
</div>
|
||||
</div>
|
||||
@@ -62,16 +63,14 @@ See COPYRIGHT and LICENSE files for more details.
|
||||
<div class="form--select-container -middle">
|
||||
<label class="form--label" for="target_type_ids"><%= Type.model_name.human %></label>
|
||||
<%= select_tag "target_type_ids",
|
||||
"<option value=\"\" disabled=\"disabled\">--- #{t(:actionview_instancetag_blank_option)} ---</option>".html_safe +
|
||||
options_from_collection_for_select(@types, "id", "name", @target_types && @target_types.map(&:id)),
|
||||
blank_select_option + options_from_collection_for_select(@types, "id", "name", @target_types && @target_types.map(&:id)),
|
||||
multiple: true,
|
||||
size: 20, class: "form--select" %>
|
||||
</div>
|
||||
<div class="form--select-container -middle">
|
||||
<label class="form--label" for='target_role_ids'><%= Role.model_name.human %></label>
|
||||
<%= select_tag "target_role_ids",
|
||||
"<option value=\"\" disabled=\"disabled\">--- #{t(:actionview_instancetag_blank_option)} ---</option>".html_safe +
|
||||
options_from_collection_for_select(@roles, "id", "name", @target_roles && @target_roles.map(&:id)),
|
||||
blank_select_option + options_from_collection_for_select(@roles, "id", "name", @target_roles && @target_roles.map(&:id)),
|
||||
multiple: true,
|
||||
size: 20,
|
||||
class: "form--select" %>
|
||||
|
||||
+52
-36
@@ -436,6 +436,15 @@ en:
|
||||
default_transitions: "Default transitions"
|
||||
user_author: "User is author"
|
||||
user_assignee: "User is assignee"
|
||||
info:
|
||||
database_deprecation_html: >
|
||||
Starting with OpenProject 16.0, PostgreSQL 16 is required to use OpenProject.
|
||||
Your installation will remain functional with your current database, but anticipate incompatability
|
||||
in future releases.
|
||||
<br/>
|
||||
We have prepared [upgrade guides for all installation methods](upgrade_guide).
|
||||
You can perform the upgrade ahead of the next release at any time by following the guides.
|
||||
|
||||
|
||||
authentication:
|
||||
login_and_registration: "Login and registration"
|
||||
@@ -1101,9 +1110,9 @@ en:
|
||||
groups:
|
||||
member_in_these_groups: "This user is currently a member of the following groups:"
|
||||
no_results_title_text: This user is currently not a member in any group.
|
||||
summary_with_more: Member of %{names} and %{count_link}.
|
||||
summary_with_more_html: Member of %{names} and %{count_link}.
|
||||
more: "%{count} more"
|
||||
summary: Member of %{names}.
|
||||
summary_html: Member of %{names}.
|
||||
memberships:
|
||||
no_results_title_text: This user is currently not a member of a project.
|
||||
open_profile: "Open profile"
|
||||
@@ -1219,6 +1228,9 @@ en:
|
||||
work_days_count:
|
||||
one: "1 working day"
|
||||
other: "%{count} working days"
|
||||
user_preferences:
|
||||
disable_keyboard_shortcuts_caption: >
|
||||
You can choose to disable default [keyboard shortcuts](docs_url) if you use a screen reader or want to avoid accidentally triggering an action with a shortcut.
|
||||
page:
|
||||
text: "Text"
|
||||
placeholder_users:
|
||||
@@ -1227,7 +1239,7 @@ en:
|
||||
You do not have the right to manage members for all projects that the placeholder user is a member of.
|
||||
delete_tooltip: "Delete placeholder user"
|
||||
deletion_info:
|
||||
heading: "Delete placeholder user %{name}"
|
||||
heading_html: "Delete placeholder user %{name}"
|
||||
data_consequences: >
|
||||
All occurrences of the placeholder user (e.g., as assignee, responsible or other user values)
|
||||
will be reassigned to an account called "Deleted user".
|
||||
@@ -1236,7 +1248,7 @@ en:
|
||||
it will not be possible to distinguish the data the user created from
|
||||
the data of another deleted account.
|
||||
irreversible: "This action is irreversible"
|
||||
confirmation: "Enter the placeholder user name %{name} to confirm the deletion."
|
||||
confirmation_html: "Enter the placeholder user name %{name} to confirm the deletion."
|
||||
priorities:
|
||||
edit:
|
||||
priority_color_text: |
|
||||
@@ -1272,7 +1284,7 @@ en:
|
||||
Check this option to exclude work packages with this status from totals of Work,
|
||||
Remaining work, and % Complete in a hierarchy.
|
||||
status_percent_complete_text: |-
|
||||
In <a href="%{href}">status-based progress calculation mode</a>, the % Complete of a work
|
||||
In [status-based progress calculation mode](setting_url), the % Complete of a work
|
||||
package is automatically set to this value when this status is selected.
|
||||
Ignored in work-based mode.
|
||||
status_readonly_html: |
|
||||
@@ -1560,7 +1572,7 @@ en:
|
||||
account for you or change the self registration limit for this provider.
|
||||
login_with_auth_provider: "or sign in with your existing account"
|
||||
signup_with_auth_provider: "or sign up using"
|
||||
auth_source_login: Please login as <em>%{login}</em> to activate your account.
|
||||
auth_source_login_html: Please login as <em>%{login}</em> to activate your account.
|
||||
omniauth_login: Please login to activate your account.
|
||||
|
||||
actionview_instancetag_blank_option: "Please select"
|
||||
@@ -1853,8 +1865,6 @@ en:
|
||||
button_update_user_information: "Update profile"
|
||||
comments_sorting: "Display work package activity sorted by"
|
||||
disable_keyboard_shortcuts: "Disable keyboard shortcuts"
|
||||
disable_keyboard_shortcuts_caption_html: |-
|
||||
You can choose to disable default <a href="%{href}">keyboard shortcuts</a> if you use a screen reader or want to avoid accidentally triggering an action with a shortcut.
|
||||
dismissed_enterprise_banners: "Hidden enterprise banners"
|
||||
impaired: "Accessibility mode"
|
||||
auto_hide_popups: "Automatically hide success banners"
|
||||
@@ -2618,7 +2628,7 @@ en:
|
||||
You will need to generate a backup token to be able to create a backup.
|
||||
Each time you want to request a backup you will have to provide this token.
|
||||
You can delete the backup token to disable backups for this user.
|
||||
verification: >
|
||||
verification_html: >
|
||||
Enter %{word} to confirm you want to %{action} the backup token.
|
||||
verification_word_reset: reset
|
||||
verification_word_create: create
|
||||
@@ -3077,7 +3087,7 @@ en:
|
||||
title:
|
||||
one: "One day left of %{trial_plan} trial token"
|
||||
other: "%{count} days left of %{trial_plan} trial token"
|
||||
description: "You have access to all %{trial_plan} features."
|
||||
description_html: "You have access to all %{trial_plan} features."
|
||||
trial:
|
||||
not_found: "You have requested a trial token, but that request is no longer available. Please try again."
|
||||
wait_for_confirmation: "We sent you an email to confirm your address in order to retrieve a trial token."
|
||||
@@ -3096,11 +3106,8 @@ en:
|
||||
confirmation_subline: >
|
||||
Please, check your inbox and follow the steps to start your 14-day free trial.
|
||||
domain_caption: The token will be valid for your currently configured host name.
|
||||
receive_newsletter_html: >
|
||||
I want to receive the OpenProject <a target="_blank" href="https://www.openproject.org/newsletter/">newsletter</a>.
|
||||
consent_html: >
|
||||
I agree with the <a target="_blank" href="https://www.openproject.org/terms-of-service/">terms of service</a>
|
||||
and the <a target="_blank" href="https://www.openproject.org/data-privacy-and-security/">privacy policy</a>.
|
||||
receive_newsletter: "I want to receive the OpenProject [newsletter](newsletter_url)."
|
||||
consent: "I agree with the [terms of service](tos_url) and the [privacy policy](privacy_url)."
|
||||
|
||||
email_calendar_updates:
|
||||
state:
|
||||
@@ -3195,8 +3202,8 @@ en:
|
||||
work_package_edit: "Work Package edited"
|
||||
work_package_note: "Work Package note added"
|
||||
title:
|
||||
project: "Project: %{name}"
|
||||
subproject: "Subproject: %{name}"
|
||||
project_html: "Project: %{name}"
|
||||
subproject_html: "Subproject: %{name}"
|
||||
|
||||
export:
|
||||
dialog:
|
||||
@@ -3466,9 +3473,9 @@ en:
|
||||
configuration_guide: "Configuration guide"
|
||||
get_in_touch: "You have questions? Get in touch with us."
|
||||
|
||||
instructions_after_registration: "You can sign in as soon as your account has been activated by clicking %{signin}."
|
||||
instructions_after_logout: "You can sign in again by clicking %{signin}."
|
||||
instructions_after_error: "You can try to sign in again by clicking %{signin}. If the error persists, ask your admin for help."
|
||||
instructions_after_registration: "You can sign in as soon as your account has been activated by clicking [here](signin_url)."
|
||||
instructions_after_logout: "You can sign in again by clicking [here](signin_url)."
|
||||
instructions_after_error: "You can try to sign in again by clicking [here](signin_url). If the error persists, ask your admin for help."
|
||||
|
||||
menus:
|
||||
admin:
|
||||
@@ -3664,6 +3671,8 @@ en:
|
||||
label_calendar_show: "Show Calendar"
|
||||
label_category: "Category"
|
||||
label_completed: Completed
|
||||
label_committed_at_html: "%{committed_revision_link} at %{date}"
|
||||
label_committed_link: "committed revision %{revision_identifier}"
|
||||
label_consent_settings: "User Consent"
|
||||
label_wiki_menu_item: Wiki menu item
|
||||
label_select_main_menu_item: Select new main menu item
|
||||
@@ -4189,7 +4198,7 @@ en:
|
||||
label_user: "User"
|
||||
label_user_and_permission: "Users and permissions"
|
||||
label_user_named: "User %{name}"
|
||||
label_user_activity: "%{value}'s activity"
|
||||
label_user_activity_html: "%{value}'s activity"
|
||||
label_user_anonymous: "Anonymous"
|
||||
label_user_menu: "User menu"
|
||||
label_user_new: "New user"
|
||||
@@ -4373,7 +4382,7 @@ en:
|
||||
note: "Note: “%{note}”"
|
||||
sharing:
|
||||
work_packages:
|
||||
allowed_actions: "You may %{allowed_actions} this work package. This can change depending on your project role and permissions."
|
||||
allowed_actions_html: "You may %{allowed_actions} this work package. This can change depending on your project role and permissions."
|
||||
create_account: "To access this work package, you will need to create and activate an account on %{instance}."
|
||||
open_work_package: "Open work package"
|
||||
subject: "Work package #%{id} was shared with you"
|
||||
@@ -4476,7 +4485,7 @@ en:
|
||||
|
||||
mail_user_activation_limit_reached:
|
||||
subject: User activation limit reached
|
||||
message: |
|
||||
message_html: |
|
||||
A new user (%{email}) tried to create an account on an OpenProject environment that you manage (%{host}).
|
||||
The user cannot activate their account since the user limit has been reached.
|
||||
steps:
|
||||
@@ -4832,10 +4841,10 @@ en:
|
||||
info: "Deleting the repository is an irreversible action."
|
||||
info_not_managed: "Note: This will NOT delete the contents of this repository, as it is not managed by OpenProject."
|
||||
managed_path_note: "The following directory will be erased: %{path}"
|
||||
repository_verification: "Enter the project's identifier %{identifier} to verify the deletion of its repository."
|
||||
repository_verification_html: "Enter the project's identifier %{identifier} to verify the deletion of its repository."
|
||||
subtitle: "Do you really want to delete the %{repository_type} of the project %{project_name}?"
|
||||
subtitle_not_managed: "Do you really want to remove the linked %{repository_type} %{url} from the project %{project_name}?"
|
||||
title: "Delete the %{repository_type}"
|
||||
subtitle_not_managed_html: "Do you really want to remove the linked %{repository_type} %{url} from the project %{project_name}?"
|
||||
title_html: "Delete the %{repository_type}"
|
||||
title_not_managed: "Remove the linked %{repository_type}?"
|
||||
errors:
|
||||
build_failed: "Unable to create the repository with the selected configuration. %{reason}"
|
||||
@@ -4946,7 +4955,7 @@ en:
|
||||
setting_apiv3_cors_origins_text_html: >
|
||||
If CORS is enabled, these are the origins that are allowed to access OpenProject API.
|
||||
<br/>
|
||||
Please check the <a href="%{origin_link}" target="_blank">Documentation on the Origin header</a> on how to specify the expected values.
|
||||
Please check the [Documentation on the Origin header](docs_url) on how to specify the expected values.
|
||||
setting_apiv3_write_readonly_attributes: "Write access to read-only attributes"
|
||||
setting_apiv3_write_readonly_attributes_instructions: >
|
||||
If enabled, the API will allow administrators to write static read-only attributes during creation,
|
||||
@@ -4956,7 +4965,7 @@ en:
|
||||
administrators to impersonate the creation of items as other users. All creation requests are being
|
||||
logged however with the true author.
|
||||
setting_apiv3_write_readonly_attributes_additional: >
|
||||
For more information on attributes and supported resources, please see the %{api_documentation_link}.
|
||||
For more information on attributes and supported resources, please see the [API documentation](api_documentation_link).
|
||||
setting_apiv3_max_page_size: "Maximum API page size"
|
||||
setting_apiv3_max_page_size_instructions: >
|
||||
Set the maximum page size the API will respond with.
|
||||
@@ -5366,7 +5375,10 @@ en:
|
||||
not_allowed_text: "You do not have the necessary permissions to view this page."
|
||||
activities:
|
||||
enable_internal_comments: "Enable internal comments"
|
||||
helper_text: "Internal comments allow an internal team to communicate amongst themselves privately. These are only visible to selected roles that have the necessary permissions and will not be visible publicly. %{link}"
|
||||
helper_text: >
|
||||
Internal comments allow an internal team to communicate amongst themselves privately.
|
||||
These are only visible to selected roles that have the necessary permissions and will not be visible publicly.
|
||||
[Click here to learn more](docs_url)
|
||||
|
||||
text_formatting:
|
||||
markdown: "Markdown"
|
||||
@@ -5647,21 +5659,23 @@ en:
|
||||
version_status_open: "open"
|
||||
|
||||
note: Note
|
||||
note_password_login_disabled: "Password login has been disabled by %{configuration}."
|
||||
note_password_login_disabled: "Password login has been disabled through a [configuration setting](configuration_url)."
|
||||
warning: Warning
|
||||
warning_attachments_not_saved: "%{count} file(s) could not be saved."
|
||||
warning_imminent_user_limit: >
|
||||
You invited more users than are supported by your current plan.
|
||||
Invited users may not be able to join your OpenProject environment.
|
||||
Please <a href="%{upgrade_url}">upgrade your plan</a> or block existing
|
||||
Please [upgrade your plan](upgrade_url) or block existing
|
||||
users in order to allow invited and registered users to join.
|
||||
warning_registration_token_expired: |
|
||||
The activation email has expired. We sent you a new one to %{email}.
|
||||
Please click the link inside of it to activate your account.
|
||||
warning_user_limit_reached: >
|
||||
Adding additional users will exceed the current limit. Please contact an administrator to increase the user limit to ensure external users are able to access this instance.
|
||||
Adding additional users will exceed the current limit.
|
||||
Please contact an administrator to increase the user limit to ensure external users are able to access this instance.
|
||||
warning_user_limit_reached_admin: >
|
||||
Adding additional users will exceed the current limit. Please <a href="%{upgrade_url}">upgrade your plan</a> to be able to ensure external users are able to access this instance.
|
||||
Adding additional users will exceed the current limit.
|
||||
Please [upgrade your plan](upgrade_url) to be able to ensure external users are able to access this instance.
|
||||
warning_user_limit_reached_instructions: >
|
||||
You reached your user limit (%{current}/%{max} active users).
|
||||
Please contact sales@openproject.com to upgrade your Enterprise edition plan and add additional users.
|
||||
@@ -5730,7 +5744,7 @@ en:
|
||||
reminders:
|
||||
label_remind_at: "Date"
|
||||
note_placeholder: "Why are you setting this reminder?"
|
||||
create_success_message: "Reminder set successfully. You will receive a notification for this work package %{reminder_time}."
|
||||
create_success_message_html: "Reminder set successfully. You will receive a notification for this work package %{reminder_time}."
|
||||
success_update_message: "Reminder updated successfully."
|
||||
success_deletion_message: "Reminder deleted successfully."
|
||||
sharing:
|
||||
@@ -5760,9 +5774,11 @@ en:
|
||||
text_user_limit_reached: "Adding additional users will exceed the current limit. Please contact an administrator to increase the user limit to ensure external users are able to access this %{entity}."
|
||||
text_user_limit_reached_admins: 'Adding additional users will exceed the current limit. Please <a href="%{upgrade_url}">upgrade your plan</a> to be able to add more users.'
|
||||
warning_user_limit_reached: >
|
||||
Adding additional users will exceed the current limit. Please contact an administrator to increase the user limit to ensure external users are able to access this %{entity}.
|
||||
Adding additional users will exceed the current limit.
|
||||
Please contact an administrator to increase the user limit to ensure external users are able to access this %{entity}.
|
||||
warning_user_limit_reached_admin: >
|
||||
Adding additional users will exceed the current limit. Please <a href="%{upgrade_url}">upgrade your plan</a> to be able to ensure external users are able to access this %{entity}.
|
||||
Adding additional users will exceed the current limit.
|
||||
Please [upgrade your plan](upgrade_url) to be able to ensure external users are able to access this %{entity}.
|
||||
warning_no_selected_user: "Please select users to share this %{entity} with"
|
||||
warning_locked_user: "The user %{user} is locked and cannot be shared with"
|
||||
user_details:
|
||||
|
||||
@@ -389,8 +389,6 @@ en:
|
||||
label_collapse_all: "Collapse all"
|
||||
label_collapse_text: "Collapse text"
|
||||
label_comment: "Comment"
|
||||
label_committed_at: "%{committed_revision_link} at %{date}"
|
||||
label_committed_link: "committed revision %{revision_identifier}"
|
||||
label_contains: "contains"
|
||||
label_created_on: "created on"
|
||||
label_edit_comment: "Edit this comment"
|
||||
|
||||
@@ -201,6 +201,8 @@ sysadmin_docs:
|
||||
href: https://www.openproject.org/docs/system-admin-guide/authentication/scim/#step-3-choose-an-authentication-method
|
||||
scim_jwt_authetication_method:
|
||||
href: https://www.openproject.org/docs/system-admin-guide/authentication/scim/#c-jwt-from-identity-provider
|
||||
terms_of_service:
|
||||
href: https://www.openproject.org/terms-of-service/
|
||||
text_formatting:
|
||||
href: https://www.openproject.org/docs/user-guide/wysiwyg/
|
||||
label: :setting_text_formatting
|
||||
|
||||
@@ -142,15 +142,14 @@ class CustomFieldFormBuilder < TabularFormBuilder
|
||||
for: custom_field_field_id,
|
||||
class: classes,
|
||||
title: custom_field.name do
|
||||
output = "".html_safe
|
||||
output += custom_field.name
|
||||
capture do
|
||||
concat custom_field.name
|
||||
|
||||
# Render a help text icon
|
||||
if options[:help_text]
|
||||
output += content_tag("attribute-help-text", "", data: options[:help_text])
|
||||
# Render a help text icon
|
||||
if options[:help_text]
|
||||
concat content_tag("attribute-help-text", "", data: options[:help_text])
|
||||
end
|
||||
end
|
||||
|
||||
output
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -167,7 +167,11 @@ module OpenProject
|
||||
|
||||
def project_link_name(project, show_icon)
|
||||
if show_icon && User.current.member_of?(project)
|
||||
icon_wrapper("icon-context icon-star", I18n.t(:description_my_project).html_safe + " ".html_safe) + project.name
|
||||
label = ActiveSupport::SafeBuffer.new
|
||||
label << I18n.t(:description_my_project)
|
||||
label << " ".html_safe
|
||||
|
||||
icon_wrapper("icon-context icon-star", label) + project.name
|
||||
else
|
||||
project.name
|
||||
end
|
||||
|
||||
@@ -39,7 +39,7 @@ module OpenProject
|
||||
concat render_page_hierarchy(pages, page.id, options) if is_parent
|
||||
end
|
||||
end
|
||||
chunks.join.html_safe
|
||||
safe_join(chunks)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -44,7 +44,11 @@ module ActionView
|
||||
def wrap_with_error_span(html_tag, object, method)
|
||||
object_identifier = erroneous_object_identifier(object.object_id.to_s, method)
|
||||
|
||||
"<span id='#{object_identifier}' class=\"errorSpan\"><a name=\"#{object_identifier}\"></a>#{html_tag}</span>".html_safe
|
||||
helpers = ActionController::Base.helpers
|
||||
|
||||
helpers.content_tag(:span, id: object_identifier, class: "errorSpan") do
|
||||
helpers.content_tag(:a, "", name: object_identifier) + html_tag
|
||||
end
|
||||
end
|
||||
|
||||
def erroneous_object_identifier(id, method)
|
||||
|
||||
@@ -180,10 +180,10 @@ module Redmine::MenuManager::MenuHelper
|
||||
caption, url, selected = extract_node_details(item, project)
|
||||
shown_in_main_menu = menu_class == "op-menu"
|
||||
|
||||
link_text = "".html_safe
|
||||
link_text = ActiveSupport::SafeBuffer.new
|
||||
|
||||
if item.icon(project).present?
|
||||
link_text += render(Primer::Beta::Octicon.new(
|
||||
link_text << render(Primer::Beta::Octicon.new(
|
||||
icon: item.icon,
|
||||
mr: shown_in_main_menu ? 3 : 0,
|
||||
size: shown_in_main_menu ? :small : :medium
|
||||
@@ -192,7 +192,7 @@ module Redmine::MenuManager::MenuHelper
|
||||
|
||||
badge_class = item.badge(project:).present? ? " #{menu_class}--item-title_has-badge" : ""
|
||||
|
||||
link_text += content_tag(:span,
|
||||
link_text << content_tag(:span,
|
||||
class: "#{menu_class}--item-title#{badge_class}",
|
||||
lang: menu_item_locale(item)) do
|
||||
title_text = content_tag(:span, caption, class: "ellipsis") + badge_for(item)
|
||||
@@ -205,7 +205,7 @@ module Redmine::MenuManager::MenuHelper
|
||||
end
|
||||
|
||||
if item.icon_after.present?
|
||||
link_text += render(Primer::Beta::Octicon.new(icon: item.icon_after, classes: "trailing-icon"))
|
||||
link_text << render(Primer::Beta::Octicon.new(icon: item.icon_after, classes: "trailing-icon"))
|
||||
end
|
||||
|
||||
html_options = item.html_options(selected:)
|
||||
@@ -424,8 +424,6 @@ module Redmine::MenuManager::MenuHelper
|
||||
key = item.badge(project: @project)
|
||||
if key.present?
|
||||
content_tag("span", I18n.t(key), class: "main-item--badge")
|
||||
else
|
||||
"".html_safe
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -207,7 +207,7 @@ class TabularFormBuilder < ActionView::Helpers::FormBuilder
|
||||
|
||||
if prefix
|
||||
ret.prepend content_tag(:span,
|
||||
prefix.html_safe,
|
||||
prefix,
|
||||
class: "form--field-affix",
|
||||
id: options[:prefix_id],
|
||||
"aria-hidden": true)
|
||||
@@ -215,7 +215,7 @@ class TabularFormBuilder < ActionView::Helpers::FormBuilder
|
||||
|
||||
if suffix
|
||||
ret.concat content_tag(:span,
|
||||
suffix.html_safe,
|
||||
suffix,
|
||||
class: "form--field-affix",
|
||||
id: options[:suffix_id],
|
||||
"aria-hidden": true)
|
||||
|
||||
@@ -290,7 +290,7 @@ module Redmine
|
||||
href:,
|
||||
target:,
|
||||
underline:,
|
||||
data: { allow_external_link: true },
|
||||
data: { allow_external_link: true }
|
||||
)
|
||||
component.with_trailing_visual_icon(icon: :"link-external") if external
|
||||
component.with_content(text)
|
||||
|
||||
@@ -439,8 +439,7 @@ class Admin::Settings::GeneralSettingsForm < ApplicationForm
|
||||
name: :per_page_options,
|
||||
label: I18n.t("setting_per_page_options"),
|
||||
value: Setting[:per_page_options],
|
||||
caption: "#{I18n.t(:text_comma_separated)}<br/>" \
|
||||
"#{I18n.t(:text_notice_too_many_values_are_inperformant)}".html_safe)
|
||||
caption: safe_join [I18n.t(:text_comma_separated), helpers.tag(:br), I18n.t(:text_notice_too_many_values_are_inperformant)],
|
||||
disabled: !Setting.per_page_options_writable?
|
||||
)
|
||||
general_form.text_field(
|
||||
@@ -492,8 +491,9 @@ class Admin::Settings::GeneralSettingsForm < ApplicationForm
|
||||
settings_form do |general_form|
|
||||
general_form.text_field(name: :app_title)
|
||||
general_form.text_field(name: :per_page_options,
|
||||
caption: "#{I18n.t(:text_comma_separated)}<br/>" \
|
||||
"#{I18n.t(:text_notice_too_many_values_are_inperformant)}".html_safe)
|
||||
caption: safe_join [I18n.t(:text_comma_separated),
|
||||
helpers.tag(:br),
|
||||
I18n.t(:text_notice_too_many_values_are_inperformant)])
|
||||
general_form.text_field(name: :activity_days_default,
|
||||
type: :number)
|
||||
general_form.text_field(name: :host_name,
|
||||
|
||||
@@ -36,7 +36,7 @@ See COPYRIGHT and LICENSE files for more details.
|
||||
<%= t("saml.delete_title") %>
|
||||
</h3>
|
||||
<p>
|
||||
<%= t("provider.delete_warning.provider", name: content_tag(:strong, @provider.display_name)).html_safe %>
|
||||
<%= t("provider.delete_warning.provider_html", name: content_tag(:strong, @provider.display_name)) %>
|
||||
</p>
|
||||
<ul class="mb-3">
|
||||
<li><%= t("provider.delete_warning.delete_result_1") %></li>
|
||||
@@ -50,7 +50,8 @@ See COPYRIGHT and LICENSE files for more details.
|
||||
<span><%= t("provider.delete_warning.irreversible_notice") %></span>
|
||||
</p>
|
||||
<p>
|
||||
<%= t("provider.delete_warning.input_delete_confirmation", name: "<em class=\"danger-zone--expected-value\">#{h(@provider.display_name)}</em>").html_safe %>
|
||||
<%= t("provider.delete_warning.input_delete_confirmation_html",
|
||||
name: content_tag(:em, @provider.display_name, class: "danger-zone--expected-value")) %>
|
||||
</p>
|
||||
<div class="danger-zone--verification">
|
||||
<%= text_field_tag :delete_confirmation %>
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<% if @user == current_user %>
|
||||
<p>
|
||||
<strong><%= t "note" %></strong>:
|
||||
<%= t("avatars.text_change_gravatar_html", gravatar_url: link_to("gravatar.com", "https://www.gravatar.com")).html_safe %>
|
||||
<%= t("avatars.text_change_gravatar_html", gravatar_url: link_to("gravatar.com", "https://www.gravatar.com")) %>
|
||||
</p>
|
||||
<% end %>
|
||||
</fieldset>
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
download: mime_type_pdf? ? nil : "download",
|
||||
data: {
|
||||
"job-status-polling-target": "download"
|
||||
})
|
||||
}
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
+3
-3
@@ -52,9 +52,9 @@
|
||||
<% end %>
|
||||
<p>
|
||||
<%= t(
|
||||
"ldap_groups.synchronized_filters.destroy.verification",
|
||||
name: "<em class=\"danger-zone--expected-value\">#{h(@filter.name)}</em>"
|
||||
).html_safe %>
|
||||
"ldap_groups.synchronized_filters.destroy.verification_html",
|
||||
name: content_tag(:em, @filter.name, class: "danger-zone--expected-value")
|
||||
) %>
|
||||
</p>
|
||||
|
||||
<div class="danger-zone--verification">
|
||||
|
||||
@@ -20,8 +20,8 @@
|
||||
<br>
|
||||
<%= t(
|
||||
"ldap_groups.synchronized_groups.destroy.verification",
|
||||
name: "<em class=\"danger-zone--expected-value\">#{h(@group.dn)}</em>"
|
||||
).html_safe %>
|
||||
name: content_tag(:em, @group.dn, class: "danger-zone--expected-value")
|
||||
) %>
|
||||
</p>
|
||||
<div class="danger-zone--verification">
|
||||
<input type="text">
|
||||
|
||||
@@ -48,7 +48,7 @@ en:
|
||||
title: 'Remove synchronized filter %{name}'
|
||||
confirmation: "If you continue, the synchronized filter %{name} and all groups %{groups_count} created through it will be removed."
|
||||
removed_groups: "Warning: This will remove the following groups from OpenProject and remove it from all projects!"
|
||||
verification: "Enter the filter name %{name} to verify the deletion."
|
||||
verification_html: "Enter the filter name %{name} to verify the deletion."
|
||||
form:
|
||||
group_name_attribute_text: 'Enter the attribute of the LDAP group used for setting the OpenProject group name.'
|
||||
filter_string_text: 'Enter the RFC4515 LDAP filter that returns groups in your LDAP to synchronize with OpenProject.'
|
||||
@@ -63,7 +63,7 @@ en:
|
||||
title: 'Remove synchronized group %{name}'
|
||||
confirmation: "If you continue, the synchronized group %{name} and all %{users_count} users synchronized through it will be removed."
|
||||
info: "Note: The OpenProject group itself and members added outside this LDAP synchronization will not be removed."
|
||||
verification: "Enter the group's name %{name} to verify the deletion."
|
||||
verification_html: "Enter the group's name %{name} to verify the deletion."
|
||||
help_text_html: |
|
||||
This module allows you to set up a synchronization between LDAP and OpenProject groups.
|
||||
It depends on LDAP groups need to use the <em>groupOfNames / memberOf</em> attribute set to be working with OpenProject.
|
||||
|
||||
@@ -52,6 +52,6 @@ class BaseErrorsComponent < ApplicationComponent
|
||||
|
||||
def joined_messages
|
||||
messages = @keys.map { |key| @errors.full_messages_for(key) }.flatten
|
||||
helpers.safe_join(messages, "<br />".html_safe)
|
||||
helpers.safe_join(messages, helpers.tag(:br))
|
||||
end
|
||||
end
|
||||
|
||||
@@ -119,13 +119,16 @@ class MeetingsController < ApplicationController
|
||||
@meeting = call.result
|
||||
|
||||
if call.success?
|
||||
text = I18n.t(:notice_successful_create)
|
||||
text = ActiveSupport::SafeBuffer.new
|
||||
text << I18n.t(:notice_successful_create)
|
||||
unless User.current.pref.time_zone?
|
||||
link = I18n.t(:notice_timezone_missing, zone: formatted_time_zone_offset)
|
||||
text += " #{view_context.link_to(link, { controller: '/my', action: :locale, anchor: 'pref_time_zone' },
|
||||
class: 'link_to_profile')}"
|
||||
text << " "
|
||||
text << view_context.link_to(link,
|
||||
{ controller: "/my", action: :locale, anchor: "pref_time_zone" },
|
||||
class: "link_to_profile")
|
||||
end
|
||||
flash[:notice] = text.html_safe # rubocop:disable Rails/OutputSafety
|
||||
flash[:notice] = text
|
||||
|
||||
redirect_to status: :see_other, action: "show", id: @meeting
|
||||
else
|
||||
@@ -317,7 +320,7 @@ class MeetingsController < ApplicationController
|
||||
.call
|
||||
.on_failure { |call| render_500(message: call.message) }
|
||||
.on_success do |call|
|
||||
send_data call.result, filename: filename_for_content_disposition("#{@meeting.title}.ics")
|
||||
send_data call.result, filename: filename_for_content_disposition("#{@meeting.title}.ics")
|
||||
end
|
||||
end
|
||||
|
||||
@@ -379,8 +382,8 @@ class MeetingsController < ApplicationController
|
||||
|
||||
def exit_draft_mode
|
||||
call = ::Meetings::UpdateService
|
||||
.new(user: current_user, model: @meeting)
|
||||
.call({ state: "open", notify: meeting_params[:notify] == "1" })
|
||||
.new(user: current_user, model: @meeting)
|
||||
.call({ state: "open", notify: meeting_params[:notify] == "1" })
|
||||
|
||||
if call.success?
|
||||
deliver_invitation_mails
|
||||
@@ -419,11 +422,11 @@ class MeetingsController < ApplicationController
|
||||
.participants
|
||||
.invited
|
||||
.find_each do |participant|
|
||||
MeetingMailer.invited(
|
||||
@meeting,
|
||||
participant.user,
|
||||
User.current
|
||||
).deliver_later
|
||||
MeetingMailer.invited(
|
||||
@meeting,
|
||||
participant.user,
|
||||
User.current
|
||||
).deliver_later
|
||||
end
|
||||
end
|
||||
|
||||
@@ -646,12 +649,13 @@ class MeetingsController < ApplicationController
|
||||
service = MeetingNotificationService.new(@meeting)
|
||||
result = service.call(:invited)
|
||||
|
||||
message = if result.success?
|
||||
I18n.t(:notice_successful_notification)
|
||||
else
|
||||
I18n.t(:error_notification_with_errors,
|
||||
recipients: result.errors.map(&:name).join("; "))
|
||||
end
|
||||
message =
|
||||
if result.success?
|
||||
I18n.t(:notice_successful_notification)
|
||||
else
|
||||
I18n.t(:error_notification_with_errors,
|
||||
recipients: result.errors.map(&:name).join("; "))
|
||||
end
|
||||
|
||||
if type == :notify
|
||||
flash[result.success? ? :notice : :error] = message
|
||||
@@ -669,11 +673,11 @@ class MeetingsController < ApplicationController
|
||||
.participants
|
||||
.invited
|
||||
.find_each do |participant|
|
||||
MeetingSeriesMailer.invited(
|
||||
recurring_meeting,
|
||||
participant.user,
|
||||
User.current
|
||||
).deliver_later
|
||||
MeetingSeriesMailer.invited(
|
||||
recurring_meeting,
|
||||
participant.user,
|
||||
User.current
|
||||
).deliver_later
|
||||
end
|
||||
|
||||
render_success_flash_message_via_turbo_stream(message: I18n.t(:notice_successful_notification))
|
||||
|
||||
@@ -86,7 +86,7 @@ class RecurringMeetingsController < ApplicationController
|
||||
@recurring_meeting = call.result
|
||||
|
||||
if call.success?
|
||||
flash[:notice] = I18n.t(:notice_successful_create).html_safe
|
||||
flash[:notice] = I18n.t(:notice_successful_create)
|
||||
redirect_to project_meeting_path(@recurring_meeting.project, @recurring_meeting.template),
|
||||
status: :see_other
|
||||
else
|
||||
|
||||
@@ -36,7 +36,8 @@ See COPYRIGHT and LICENSE files for more details.
|
||||
<%= t("openid_connect.delete_title") %>
|
||||
</h3>
|
||||
<p>
|
||||
<%= t("provider.delete_warning.provider", name: content_tag(:strong, @provider.display_name)).html_safe %>
|
||||
<%= t("provider.delete_warning.provider_html",
|
||||
name: content_tag(:strong, @provider.display_name)) %>
|
||||
</p>
|
||||
<ul class="mb-3">
|
||||
<li><%= t("provider.delete_warning.delete_result_1") %></li>
|
||||
@@ -50,7 +51,8 @@ See COPYRIGHT and LICENSE files for more details.
|
||||
<span><%= t("provider.delete_warning.irreversible_notice") %></span>
|
||||
</p>
|
||||
<p>
|
||||
<%= t("provider.delete_warning.input_delete_confirmation", name: "<em class=\"danger-zone--expected-value\">#{h(@provider.display_name)}</em>").html_safe %>
|
||||
<%= t("provider.delete_warning.input_delete_confirmation_html",
|
||||
name: content_tag(:em, @provider.display_name, class: "danger-zone--expected-value")) %>
|
||||
</p>
|
||||
<div class="danger-zone--verification">
|
||||
<%= text_field_tag :delete_confirmation %>
|
||||
|
||||
@@ -51,9 +51,9 @@ en:
|
||||
|
||||
provider:
|
||||
delete_warning:
|
||||
input_delete_confirmation: Enter the provider name %{name} to confirm deletion.
|
||||
input_delete_confirmation_html: Enter the provider name %{name} to confirm deletion.
|
||||
irreversible_notice: Deleting an SSO provider is an irreversible action.
|
||||
provider: 'Are you sure you want to delete the SSO provider %{name}? To confirm this action please enter the name of the provider in the field below, this will:'
|
||||
provider_html: 'Are you sure you want to delete the SSO provider %{name}? To confirm this action please enter the name of the provider in the field below, this will:'
|
||||
delete_result_1: Remove the provider from the list of available providers.
|
||||
delete_result_user_count:
|
||||
zero: No users are currently using this provider. No further action is required.
|
||||
|
||||
@@ -27,12 +27,12 @@
|
||||
container_class: "-middle" %>
|
||||
</div>
|
||||
<div class="form--field-instructions">
|
||||
<%= I18n.t(
|
||||
<%= t(
|
||||
"recaptcha.settings.captcha_description_html",
|
||||
hcaptcha_link: link_to("https://docs.hcaptcha.com/switch/", "https://docs.hcaptcha.com/switch/", target: "_blank"),
|
||||
recaptcha_link: link_to("https://www.google.com/recaptcha", "https://www.google.com/recaptcha", target: "_blank"),
|
||||
turnstile_link: link_to("https://developers.cloudflare.com/turnstile/", "https://developers.cloudflare.com/turnstile/", target: "_blank")
|
||||
).html_safe %>
|
||||
) %>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form--field">
|
||||
|
||||
+5
-1
@@ -4,7 +4,11 @@
|
||||
flex_layout(flex_items: :center) do |credentials_row|
|
||||
credentials_row.with_row(mb: 3, test_selector: "storage-openproject_oauth_application_warning") do
|
||||
render(Primer::Alpha::Banner.new(icon: :alert, scheme: :warning)) do
|
||||
I18n.t("storages.instructions.oauth_application_details", oauth_application_details_link: oauth_application_details_link).html_safe
|
||||
helpers.link_translate(
|
||||
"storages.instructions.oauth_application_details",
|
||||
links: { oauth_application_details_link: ::Storages::UrlBuilder.url(storage.uri, "settings/admin/openproject") },
|
||||
external: true
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-10
@@ -40,16 +40,6 @@ module Storages::Admin
|
||||
|
||||
def self.wrapper_key = :storage_openproject_oauth_section
|
||||
|
||||
def oauth_application_details_link
|
||||
render(
|
||||
Primer::Beta::Link.new(
|
||||
href: ::Storages::UrlBuilder.url(storage.uri, "settings/admin/openproject"),
|
||||
data: { allow_external_link: true },
|
||||
target: "_blank"
|
||||
)
|
||||
) { I18n.t("storages.instructions.oauth_application_details_link_text") }
|
||||
end
|
||||
|
||||
def submit_button_options
|
||||
{
|
||||
scheme: :primary,
|
||||
|
||||
+2
-1
@@ -43,7 +43,8 @@ See COPYRIGHT and LICENSE files for more details.
|
||||
<% dialog.with_additional_details do %>
|
||||
<% render(Primer::OpenProject::FlexLayout.new) do |flex| %>
|
||||
<% flex.with_row do %>
|
||||
<%= t("storages.delete_warning.storage", file_storage: "<strong>#{h(@storage.name)}</strong>").html_safe %>
|
||||
<%= t("storages.delete_warning.storage_html",
|
||||
file_storage: content_tag(:strong, @storage.name)) %>
|
||||
<% end %>
|
||||
<% flex.with_row do %>
|
||||
<ul>
|
||||
|
||||
+2
-1
@@ -44,7 +44,8 @@ See COPYRIGHT and LICENSE files for more details.
|
||||
<% dialog.with_additional_details do %>
|
||||
<% render(Primer::OpenProject::FlexLayout.new) do |flex| %>
|
||||
<% flex.with_row do %>
|
||||
<%= t("storages.delete_warning.project_storage", file_storage: "<strong>#{h(@project_storage.storage.name)}</strong>").html_safe %>
|
||||
<%= t("storages.delete_warning.project_storage_html",
|
||||
file_storage: content_tag(:strong, @project_storage.storage.name)) %>
|
||||
<% end %>
|
||||
<% flex.with_row do %>
|
||||
<ul>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user