2025-02-24 11:10:37 +01:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
|
2011-05-29 13:11:52 -07:00
|
|
|
#-- copyright
|
2020-01-15 11:31:26 +01:00
|
|
|
# OpenProject is an open source project management software.
|
2024-07-30 13:42:36 +02:00
|
|
|
# Copyright (C) the OpenProject GmbH
|
2011-05-30 20:52:25 +02:00
|
|
|
#
|
2011-05-29 13:11:52 -07:00
|
|
|
# This program is free software; you can redistribute it and/or
|
2013-06-05 16:27:56 +02:00
|
|
|
# modify it under the terms of the GNU General Public License version 3.
|
2011-05-30 20:52:25 +02:00
|
|
|
#
|
2013-09-16 17:59:31 +02:00
|
|
|
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
|
2021-01-13 17:47:45 +01:00
|
|
|
# Copyright (C) 2006-2013 Jean-Philippe Lang
|
2013-09-16 17:59:31 +02:00
|
|
|
# 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.
|
|
|
|
|
#
|
2021-09-02 21:49:06 +02:00
|
|
|
# See COPYRIGHT and LICENSE files for more details.
|
2011-05-29 13:11:52 -07:00
|
|
|
#++
|
|
|
|
|
|
2007-12-30 10:48:11 +00:00
|
|
|
require "action_view/helpers/form_helper"
|
2016-06-30 20:00:00 +02:00
|
|
|
require "securerandom"
|
2007-12-30 10:48:11 +00:00
|
|
|
|
|
|
|
|
class TabularFormBuilder < ActionView::Helpers::FormBuilder
|
2009-02-21 11:04:50 +00:00
|
|
|
include Redmine::I18n
|
2012-03-27 14:50:47 +02:00
|
|
|
include ActionView::Helpers::AssetTagHelper
|
2015-06-23 16:30:47 +02:00
|
|
|
include ERB::Util
|
2018-07-11 23:18:33 +02:00
|
|
|
include TextFormattingHelper
|
2023-01-24 16:48:26 +01:00
|
|
|
include AngularHelper
|
2011-05-30 20:52:25 +02:00
|
|
|
|
2018-07-09 13:12:45 +02:00
|
|
|
def self.tag_with_label_method(selector, &)
|
2018-07-11 23:18:33 +02:00
|
|
|
->(field, options = {}, *args) do
|
2017-03-30 11:08:40 +02:00
|
|
|
options[:class] = Array(options[:class]) + [field_css_class(selector)]
|
|
|
|
|
merge_required_attributes(options[:required], options)
|
2012-04-03 11:34:20 +02:00
|
|
|
|
2017-03-30 11:08:40 +02:00
|
|
|
input_options, label_options = extract_from options
|
2012-03-27 14:50:47 +02:00
|
|
|
|
2018-08-02 09:27:11 +02:00
|
|
|
if field_has_errors?(field)
|
2025-02-24 11:10:37 +01:00
|
|
|
add_error_class(input_options)
|
2018-08-02 09:27:11 +02:00
|
|
|
end
|
|
|
|
|
|
2017-03-30 11:08:40 +02:00
|
|
|
label = label_for_field(field, label_options)
|
|
|
|
|
input = super(field, input_options, *args)
|
2012-03-27 14:50:47 +02:00
|
|
|
|
2018-07-09 13:12:45 +02:00
|
|
|
input = instance_exec(input, options, &) if block_given?
|
2018-05-02 09:25:31 +02:00
|
|
|
|
2017-03-30 11:08:40 +02:00
|
|
|
(label + container_wrap_field(input, selector, options))
|
2007-12-30 10:48:11 +00:00
|
|
|
end
|
|
|
|
|
end
|
2018-07-11 23:18:33 +02:00
|
|
|
private_class_method :tag_with_label_method
|
2014-01-07 17:21:56 +01:00
|
|
|
|
2018-07-09 13:12:45 +02:00
|
|
|
def self.with_text_formatting
|
|
|
|
|
->(input, options) {
|
|
|
|
|
if options[:with_text_formatting]
|
|
|
|
|
# use either the provided id or fetch the one created by rails
|
2026-03-19 14:39:20 +01:00
|
|
|
id = options[:id] || input.match(/<textarea[^>]* id="(\w+)"[^>]*>/)[1]
|
2018-07-20 09:29:00 +02:00
|
|
|
options[:preview_context] ||= preview_context(object)
|
2018-07-17 08:24:03 +02:00
|
|
|
input.concat text_formatting_wrapper id, options
|
2018-07-09 13:12:45 +02:00
|
|
|
end
|
|
|
|
|
|
|
|
|
|
input
|
|
|
|
|
}
|
|
|
|
|
end
|
2018-07-11 23:18:33 +02:00
|
|
|
private_class_method :with_text_formatting
|
2018-07-09 13:12:45 +02:00
|
|
|
|
2025-03-11 10:22:56 +01:00
|
|
|
(field_helpers - %i(radio_button hidden_field fields_for label text_area) + %i(date_select check_box)).each do |selector|
|
2018-07-09 13:12:45 +02:00
|
|
|
define_method selector, &tag_with_label_method(selector)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
define_method(:text_area, &tag_with_label_method(:text_area, &with_text_formatting))
|
|
|
|
|
|
2015-02-26 17:38:09 +01:00
|
|
|
def label(method, text = nil, options = {}, &)
|
2015-02-27 10:51:12 +01:00
|
|
|
options[:class] = Array(options[:class]) + %w(form--label)
|
2015-03-11 15:12:48 +01:00
|
|
|
options[:title] = options[:title] || title_from_context(method)
|
2015-02-26 17:38:09 +01:00
|
|
|
super
|
|
|
|
|
end
|
2012-03-27 14:50:47 +02:00
|
|
|
|
2024-08-15 09:26:29 +02:00
|
|
|
def date_picker(field, options = {}) # rubocop:disable Metrics/AbcSize
|
2023-01-25 11:47:56 +01:00
|
|
|
options[:class] = Array(options[:class])
|
2023-02-22 17:07:14 +01:00
|
|
|
options[:container_class] ||= "-xslim"
|
2023-01-24 16:48:26 +01:00
|
|
|
merge_required_attributes(options[:required], options)
|
2023-01-26 16:53:35 +02:00
|
|
|
options[:visible_overflow] = true
|
2023-01-24 16:48:26 +01:00
|
|
|
|
|
|
|
|
input_options, label_options = extract_from options
|
|
|
|
|
|
|
|
|
|
if field_has_errors?(field)
|
2025-02-24 11:10:37 +01:00
|
|
|
add_error_class(input_options)
|
2023-01-24 16:48:26 +01:00
|
|
|
end
|
|
|
|
|
|
2023-01-31 17:47:42 +01:00
|
|
|
@object_name.to_s.sub!(/\[\]$/, "") || @object_name.to_s.sub!(/\[\]\]$/, "]")
|
2023-01-31 17:34:24 +02:00
|
|
|
|
|
|
|
|
inputs = {
|
|
|
|
|
value: @object.public_send(field),
|
2025-09-11 18:34:06 +01:00
|
|
|
id: field_id(field, index: options[:index]),
|
2024-07-15 14:56:40 +02:00
|
|
|
name: options[:name] || field_name(field, index: options[:index])
|
2023-01-31 17:34:24 +02:00
|
|
|
}
|
2023-01-26 18:24:17 +02:00
|
|
|
|
2023-01-26 18:31:37 +02:00
|
|
|
if options.dig(:data, :"remote-field-key")
|
|
|
|
|
inputs["remote-field-key"] = options.dig(:data, :"remote-field-key")
|
2023-02-22 17:07:14 +01:00
|
|
|
inputs[:inputClassNames] = "remote-field--input"
|
2023-01-26 18:24:17 +02:00
|
|
|
end
|
|
|
|
|
|
2023-02-14 17:28:20 +02:00
|
|
|
if !options[:show_ignore_non_working_days].nil?
|
|
|
|
|
inputs["show-ignore-non-working-days"] = options[:show_ignore_non_working_days]
|
|
|
|
|
end
|
|
|
|
|
|
2023-08-02 14:35:32 +03:00
|
|
|
if options[:required]
|
|
|
|
|
inputs[:required] = options[:required]
|
|
|
|
|
end
|
|
|
|
|
|
2023-01-24 16:48:26 +01:00
|
|
|
label = label_for_field(field, label_options)
|
2024-08-13 19:19:09 +02:00
|
|
|
input = angular_component_tag("opce-basic-single-date-picker",
|
2023-01-26 16:53:35 +02:00
|
|
|
class: options[:class],
|
2023-01-26 18:24:17 +02:00
|
|
|
inputs:)
|
2023-01-24 16:48:26 +01:00
|
|
|
(label + container_wrap_field(input, :date_picker, options))
|
2023-01-24 14:21:30 +01:00
|
|
|
end
|
|
|
|
|
|
2015-03-02 17:19:14 +01:00
|
|
|
def radio_button(field, value, options = {}, *)
|
2015-03-03 16:10:16 +01:00
|
|
|
options[:class] = Array(options[:class]) + %w(form--radio-button)
|
2012-03-27 14:50:47 +02:00
|
|
|
|
2015-03-03 16:08:09 +01:00
|
|
|
input_options, label_options = extract_from options
|
2015-03-03 16:40:26 +01:00
|
|
|
label_options[:for] = "#{sanitized_object_name}_#{field}_#{value.downcase}"
|
2015-03-03 16:08:09 +01:00
|
|
|
|
2018-08-02 09:27:11 +02:00
|
|
|
if field_has_errors?(field)
|
2025-02-24 11:10:37 +01:00
|
|
|
add_error_class(input_options)
|
2018-08-02 09:27:11 +02:00
|
|
|
end
|
|
|
|
|
|
2015-10-28 10:34:33 +01:00
|
|
|
label = label_for_field(field, label_options)
|
2015-03-02 17:19:14 +01:00
|
|
|
input = super(field, value, input_options, *)
|
|
|
|
|
|
2015-03-03 16:08:09 +01:00
|
|
|
(label + container_wrap_field(input, "radio-button", options))
|
2007-12-30 10:48:11 +00:00
|
|
|
end
|
2011-05-30 20:52:25 +02:00
|
|
|
|
|
|
|
|
def select(field, choices, options = {}, html_options = {})
|
2015-02-06 16:40:36 +01:00
|
|
|
html_options[:class] = Array(html_options[:class]) + %w(form--select)
|
2017-07-18 09:29:03 +02:00
|
|
|
if html_options[:container_class].present?
|
|
|
|
|
options[:container_class] = html_options[:container_class]
|
|
|
|
|
end
|
2018-08-02 09:27:11 +02:00
|
|
|
|
|
|
|
|
if field_has_errors?(field)
|
2025-02-24 11:10:37 +01:00
|
|
|
add_error_class(html_options)
|
2018-08-02 09:27:11 +02:00
|
|
|
end
|
|
|
|
|
|
2016-01-22 13:35:18 +01:00
|
|
|
merge_required_attributes(options[:required], html_options)
|
2015-10-28 10:34:33 +01:00
|
|
|
label_for_field(field, options) + container_wrap_field(super, "select", options)
|
2009-01-07 20:03:33 +00:00
|
|
|
end
|
2011-05-30 20:52:25 +02:00
|
|
|
|
2014-01-13 18:32:10 +01:00
|
|
|
def collection_select(field, collection, value_method, text_method, options = {}, html_options = {})
|
2015-02-06 16:40:36 +01:00
|
|
|
html_options[:class] = Array(html_options[:class]) + %w(form--select)
|
2015-02-04 17:17:02 +01:00
|
|
|
|
2015-10-28 10:34:33 +01:00
|
|
|
label_for_field(field, options) + container_wrap_field(super, "select", options)
|
2014-01-13 18:32:10 +01:00
|
|
|
end
|
|
|
|
|
|
2015-02-06 18:12:18 +01:00
|
|
|
def collection_check_box(field,
|
2015-08-07 14:53:08 +02:00
|
|
|
checked_value,
|
2015-02-06 18:12:18 +01:00
|
|
|
checked,
|
2015-08-07 14:53:08 +02:00
|
|
|
text = field.to_s + "_#{checked_value}",
|
2015-02-06 18:12:18 +01:00
|
|
|
options = {})
|
2024-01-04 17:01:17 +01:00
|
|
|
label_for = :"#{sanitized_object_name}_#{field}_#{checked_value}"
|
2015-08-07 14:53:08 +02:00
|
|
|
unchecked_value = options.delete(:unchecked_value) { "" }
|
2015-02-06 18:12:18 +01:00
|
|
|
|
2015-02-11 08:26:42 +01:00
|
|
|
input_options = options.reverse_merge(multiple: true,
|
|
|
|
|
checked:,
|
|
|
|
|
for: label_for,
|
|
|
|
|
label: text)
|
2015-02-06 18:12:18 +01:00
|
|
|
|
2017-06-29 14:33:04 +02:00
|
|
|
if options.delete(:no_label)
|
|
|
|
|
input_options.delete :for
|
|
|
|
|
input_options.delete :label
|
|
|
|
|
end
|
|
|
|
|
|
2015-08-07 14:53:08 +02:00
|
|
|
check_box(field, input_options, checked_value, unchecked_value)
|
2015-02-06 18:12:18 +01:00
|
|
|
end
|
|
|
|
|
|
2015-02-09 09:46:16 +01:00
|
|
|
def fields_for_custom_fields(record_name, record_object = nil, options = {}, &)
|
|
|
|
|
options_with_defaults = options.merge(builder: CustomFieldFormBuilder)
|
2015-02-08 21:25:10 +01:00
|
|
|
|
2015-02-09 09:46:16 +01:00
|
|
|
fields_for(record_name, record_object, options_with_defaults, &)
|
2014-01-13 18:32:10 +01:00
|
|
|
end
|
|
|
|
|
|
2012-04-03 11:34:20 +02:00
|
|
|
private
|
|
|
|
|
|
2015-02-08 21:25:10 +01:00
|
|
|
attr_reader :template
|
|
|
|
|
|
2015-05-01 12:44:13 +02:00
|
|
|
TEXT_LIKE_FIELDS = %i(
|
|
|
|
|
number_field password_field url_field telephone_field email_field
|
|
|
|
|
).freeze
|
2015-02-04 17:17:02 +01:00
|
|
|
|
2015-02-05 19:41:35 +01:00
|
|
|
def container_wrap_field(field_html, selector, options = {})
|
2023-01-31 01:17:18 +02:00
|
|
|
ret = if options.delete(:no_field_container)
|
|
|
|
|
field_html
|
|
|
|
|
else
|
|
|
|
|
content_tag(:span, field_html, class: field_container_css_class(selector, options))
|
|
|
|
|
end
|
2015-02-05 19:41:35 +01:00
|
|
|
|
2015-02-28 16:52:49 +01:00
|
|
|
prefix, suffix = options.values_at(:prefix, :suffix)
|
|
|
|
|
|
2016-06-30 20:00:00 +02:00
|
|
|
if prefix
|
|
|
|
|
ret.prepend content_tag(:span,
|
2026-02-18 07:47:11 +01:00
|
|
|
prefix,
|
2016-06-30 20:00:00 +02:00
|
|
|
class: "form--field-affix",
|
|
|
|
|
id: options[:prefix_id],
|
2018-07-09 13:12:45 +02:00
|
|
|
"aria-hidden": true)
|
2016-06-30 20:00:00 +02:00
|
|
|
end
|
|
|
|
|
|
|
|
|
|
if suffix
|
|
|
|
|
ret.concat content_tag(:span,
|
2026-02-18 07:47:11 +01:00
|
|
|
suffix,
|
2016-06-30 20:00:00 +02:00
|
|
|
class: "form--field-affix",
|
|
|
|
|
id: options[:suffix_id],
|
2018-07-09 13:12:45 +02:00
|
|
|
"aria-hidden": true)
|
2016-06-30 20:00:00 +02:00
|
|
|
end
|
2015-02-28 16:52:49 +01:00
|
|
|
|
2015-02-11 08:26:42 +01:00
|
|
|
field_container_wrap_field(ret, options)
|
|
|
|
|
end
|
|
|
|
|
|
2016-06-30 20:00:00 +02:00
|
|
|
def merge_required_attributes(required, options = nil)
|
2016-01-22 13:35:18 +01:00
|
|
|
if required
|
2018-07-09 13:12:45 +02:00
|
|
|
options.merge!(required: true, "aria-required": "true")
|
2016-01-22 13:35:18 +01:00
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2015-02-11 08:26:42 +01:00
|
|
|
def field_container_wrap_field(field_html, options = {})
|
|
|
|
|
if options[:no_label]
|
|
|
|
|
field_html
|
|
|
|
|
else
|
2023-01-24 16:48:26 +01:00
|
|
|
classes = options[:visible_overflow] ? "-visible-overflow" : ""
|
2023-01-26 16:53:35 +02:00
|
|
|
content_tag(:span, field_html, class: options[:no_class] ? classes : "#{classes} form--field-container")
|
2015-02-11 08:26:42 +01:00
|
|
|
end
|
2015-02-05 17:59:07 +01:00
|
|
|
end
|
|
|
|
|
|
2015-03-22 08:22:51 +01:00
|
|
|
def field_container_css_class(selector, options)
|
|
|
|
|
classes = if TEXT_LIKE_FIELDS.include?(selector)
|
2025-02-24 11:10:37 +01:00
|
|
|
["form--text-field-container"]
|
2015-03-22 08:22:51 +01:00
|
|
|
else
|
2025-02-24 11:10:37 +01:00
|
|
|
["form--#{selector.to_s.tr('_', '-')}-container"]
|
2015-03-22 08:22:51 +01:00
|
|
|
end
|
|
|
|
|
|
2025-02-24 11:10:37 +01:00
|
|
|
classes << options[:container_class]
|
2015-03-22 08:22:51 +01:00
|
|
|
|
2025-02-24 11:10:37 +01:00
|
|
|
classes.compact
|
2015-02-05 17:59:07 +01:00
|
|
|
end
|
|
|
|
|
|
2018-05-02 09:25:31 +02:00
|
|
|
##
|
|
|
|
|
# Create a wrapper for the text formatting toolbar for this field
|
2018-07-17 08:24:03 +02:00
|
|
|
def text_formatting_wrapper(target_id, options)
|
2023-01-26 16:53:35 +02:00
|
|
|
return "".html_safe if target_id.blank?
|
2018-05-02 09:25:31 +02:00
|
|
|
|
2020-12-03 17:30:36 +01:00
|
|
|
::OpenProject::TextFormatting::Formats
|
|
|
|
|
.rich_helper
|
|
|
|
|
.new(@template)
|
|
|
|
|
.wikitoolbar_for target_id, **options
|
2018-05-02 09:25:31 +02:00
|
|
|
end
|
|
|
|
|
|
2015-02-04 17:17:02 +01:00
|
|
|
def field_css_class(selector)
|
|
|
|
|
if TEXT_LIKE_FIELDS.include?(selector)
|
2015-05-01 12:44:13 +02:00
|
|
|
"form--text-field -#{selector.to_s.gsub(/_field$/, '')}"
|
2015-02-04 17:17:02 +01:00
|
|
|
else
|
2015-05-01 12:44:13 +02:00
|
|
|
"form--#{selector.to_s.tr('_', '-')}"
|
2015-02-04 17:17:02 +01:00
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2009-01-07 20:03:33 +00:00
|
|
|
# Returns a label tag for the given field
|
2017-03-30 11:08:40 +02:00
|
|
|
def label_for_field(field, options = {})
|
2016-06-30 20:00:00 +02:00
|
|
|
return "".html_safe if options[:no_label]
|
2015-03-25 16:19:32 +01:00
|
|
|
|
2020-12-03 17:30:36 +01:00
|
|
|
label_options = {
|
|
|
|
|
class: label_for_field_class(options[:class]),
|
|
|
|
|
title: get_localized_field(field, options[:label])
|
|
|
|
|
}
|
2017-03-28 09:54:20 +02:00
|
|
|
|
2020-12-03 17:30:36 +01:00
|
|
|
content = h(label_options[:title])
|
2016-06-30 20:00:00 +02:00
|
|
|
label_for_field_errors(content, label_options, field)
|
2017-03-30 11:08:40 +02:00
|
|
|
label_for_field_for(options, label_options, field)
|
2016-06-30 20:00:00 +02:00
|
|
|
label_for_field_prefix(content, options)
|
2015-11-30 13:23:16 +01:00
|
|
|
|
2020-08-19 15:56:46 +02:00
|
|
|
# Render a help text icon
|
|
|
|
|
if options[:help_text]
|
|
|
|
|
content << content_tag("attribute-help-text", "", data: options[:help_text])
|
|
|
|
|
end
|
|
|
|
|
|
2016-06-30 20:00:00 +02:00
|
|
|
label_options[:lang] = options[:lang]
|
2023-01-26 16:53:35 +02:00
|
|
|
label_options.compact!
|
2016-01-21 13:35:21 +01:00
|
|
|
|
2016-06-30 20:00:00 +02:00
|
|
|
@template.label(@object_name, field, content, label_options)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def label_for_field_errors(content, options, field)
|
2018-08-02 09:27:11 +02:00
|
|
|
if field_has_errors?(field)
|
2025-02-24 11:10:37 +01:00
|
|
|
add_error_class(options)
|
2016-01-21 13:35:21 +01:00
|
|
|
error_label = I18n.t("errors.field_erroneous_label",
|
|
|
|
|
full_errors: @object.errors.full_messages_for(field).join(" "))
|
2025-07-15 18:15:13 +01:00
|
|
|
content << content_tag("p", error_label, class: "sr-only")
|
2016-01-21 16:06:20 +01:00
|
|
|
end
|
2016-06-30 20:00:00 +02:00
|
|
|
end
|
2016-01-21 16:06:20 +01:00
|
|
|
|
2021-02-11 16:02:18 +01:00
|
|
|
def label_for_field_for(options, label_options, _field)
|
2017-03-30 11:08:40 +02:00
|
|
|
label_options[:for] = options[:for]
|
2016-06-30 20:00:00 +02:00
|
|
|
end
|
2014-01-07 17:21:56 +01:00
|
|
|
|
2016-06-30 20:00:00 +02:00
|
|
|
def label_for_field_prefix(content, options)
|
|
|
|
|
if options[:prefix]
|
2025-07-15 18:15:13 +01:00
|
|
|
content << content_tag(:span, options[:prefix].html_safe, class: "sr-only")
|
2016-06-30 20:00:00 +02:00
|
|
|
end
|
2014-01-07 17:21:56 +01:00
|
|
|
end
|
|
|
|
|
|
2020-12-03 17:30:36 +01:00
|
|
|
def label_for_field_class(klass)
|
|
|
|
|
case klass
|
|
|
|
|
when Array
|
|
|
|
|
"form--label #{klass.join(' ')}"
|
|
|
|
|
when String
|
|
|
|
|
"form--label #{klass}"
|
|
|
|
|
else
|
|
|
|
|
"form--label"
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2016-01-22 13:35:18 +01:00
|
|
|
def get_localized_field(field, label)
|
|
|
|
|
if label.is_a?(Symbol)
|
2020-09-16 11:26:15 +02:00
|
|
|
I18n.t(label)
|
2016-01-22 13:35:18 +01:00
|
|
|
elsif label
|
|
|
|
|
label
|
2019-05-03 14:47:02 +00:00
|
|
|
elsif @object.class.respond_to?(:human_attribute_name)
|
2016-01-22 13:35:18 +01:00
|
|
|
@object.class.human_attribute_name(field)
|
|
|
|
|
else
|
2024-01-09 19:35:05 +02:00
|
|
|
I18n.t(field, scope: sanitized_object_name)
|
2016-01-22 13:35:18 +01:00
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2018-08-02 09:27:11 +02:00
|
|
|
def field_has_errors?(field)
|
2025-03-04 17:42:43 +01:00
|
|
|
return false if @object == false || @object.nil?
|
|
|
|
|
|
|
|
|
|
@object.errors&.include?(field)
|
2018-08-02 09:27:11 +02:00
|
|
|
end
|
|
|
|
|
|
2015-03-03 16:08:09 +01:00
|
|
|
def extract_from(options)
|
2017-03-28 09:54:20 +02:00
|
|
|
label_options = options.dup.except(:class)
|
2020-08-19 15:56:46 +02:00
|
|
|
input_options = options.dup.except(:for, :label, :no_label, :prefix, :suffix, :label_options, :help_text)
|
2015-03-03 16:08:09 +01:00
|
|
|
|
2017-03-28 09:54:20 +02:00
|
|
|
label_options.merge!(options.delete(:label_options) || {})
|
|
|
|
|
|
2016-06-30 20:00:00 +02:00
|
|
|
if options[:suffix]
|
|
|
|
|
options[:suffix_id] ||= SecureRandom.uuid
|
|
|
|
|
|
|
|
|
|
input_options[:"aria-describedby"] ||= options[:suffix_id]
|
|
|
|
|
end
|
|
|
|
|
if options[:prefix]
|
|
|
|
|
options[:prefix_id] ||= SecureRandom.uuid
|
|
|
|
|
end
|
|
|
|
|
|
2015-03-03 16:08:09 +01:00
|
|
|
[input_options, label_options]
|
|
|
|
|
end
|
2015-03-03 16:40:26 +01:00
|
|
|
|
|
|
|
|
def sanitized_object_name
|
|
|
|
|
object_name.to_s.gsub(/\]\[|[^-a-zA-Z0-9:.]/, "_").sub(/_$/, "")
|
|
|
|
|
end
|
2015-03-11 15:12:48 +01:00
|
|
|
|
|
|
|
|
def title_from_context(method)
|
|
|
|
|
if object.class.respond_to? :human_attribute_name
|
|
|
|
|
object.class.human_attribute_name method
|
|
|
|
|
else
|
|
|
|
|
method.to_s.camelize
|
|
|
|
|
end
|
|
|
|
|
end
|
2025-02-24 11:10:37 +01:00
|
|
|
|
|
|
|
|
def add_error_class(options)
|
|
|
|
|
options[:class] = Array(options[:class]) + ["-error"]
|
|
|
|
|
end
|
2007-12-30 10:48:11 +00:00
|
|
|
end
|