decidim-forms 0.23.0 → 0.24.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (81) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/decidim/forms/display_conditions.component.js.es6 +5 -6
  3. data/app/assets/javascripts/decidim/forms/forms.js.es6 +4 -4
  4. data/app/commands/decidim/forms/admin/update_questionnaire.rb +2 -1
  5. data/app/commands/decidim/forms/answer_questionnaire.rb +43 -1
  6. data/app/controllers/decidim/forms/admin/concerns/has_questionnaire.rb +10 -2
  7. data/app/controllers/decidim/forms/concerns/has_questionnaire.rb +13 -6
  8. data/app/forms/decidim/forms/admin/question_form.rb +2 -0
  9. data/app/forms/decidim/forms/answer_form.rb +28 -1
  10. data/app/forms/decidim/forms/questionnaire_form.rb +5 -0
  11. data/app/helpers/decidim/forms/admin/application_helper.rb +1 -6
  12. data/app/helpers/decidim/forms/admin/concerns/has_questionnaire_answers_url_helper.rb +3 -3
  13. data/app/helpers/decidim/forms/admin/questionnaire_answers_helper.rb +5 -2
  14. data/app/jobs/decidim/forms/export_questionnaire_answers_job.rb +1 -1
  15. data/app/models/decidim/forms/answer.rb +8 -0
  16. data/app/models/decidim/forms/display_condition.rb +22 -8
  17. data/app/models/decidim/forms/question.rb +9 -3
  18. data/app/models/decidim/forms/questionnaire.rb +11 -0
  19. data/app/presenters/decidim/forms/admin/questionnaire_answer_presenter.rb +20 -0
  20. data/app/presenters/decidim/forms/admin/questionnaire_participant_presenter.rb +4 -4
  21. data/app/queries/decidim/forms/questionnaire_user_answers.rb +2 -1
  22. data/app/views/decidim/forms/admin/questionnaires/_form.html.erb +3 -3
  23. data/app/views/decidim/forms/admin/questionnaires/_question.html.erb +11 -0
  24. data/app/views/decidim/forms/admin/questionnaires/edit.html.erb +1 -1
  25. data/app/views/decidim/forms/questionnaires/_answer.html.erb +1 -1
  26. data/app/views/decidim/forms/questionnaires/answers/_files.html.erb +1 -0
  27. data/app/views/decidim/forms/questionnaires/answers/_long_answer.html.erb +1 -1
  28. data/app/views/decidim/forms/questionnaires/answers/_matrix_multiple.html.erb +2 -1
  29. data/app/views/decidim/forms/questionnaires/answers/_matrix_single.html.erb +2 -1
  30. data/app/views/decidim/forms/questionnaires/answers/_multiple_option.html.erb +2 -1
  31. data/app/views/decidim/forms/questionnaires/answers/_short_answer.html.erb +1 -1
  32. data/app/views/decidim/forms/questionnaires/answers/_single_option.html.erb +2 -1
  33. data/app/views/decidim/forms/questionnaires/show.html.erb +1 -1
  34. data/config/initializers/wicked_pdf.rb +2 -0
  35. data/config/locales/ca.yml +4 -2
  36. data/config/locales/cs.yml +10 -4
  37. data/config/locales/de.yml +10 -2
  38. data/config/locales/el.yml +1 -1
  39. data/config/locales/en.yml +8 -2
  40. data/config/locales/es-MX.yml +2 -2
  41. data/config/locales/es-PY.yml +2 -2
  42. data/config/locales/es.yml +2 -2
  43. data/config/locales/fi-plain.yml +8 -2
  44. data/config/locales/fi.yml +8 -2
  45. data/config/locales/fr-CA.yml +8 -2
  46. data/config/locales/fr.yml +10 -4
  47. data/config/locales/it.yml +1 -2
  48. data/config/locales/ja.yml +3 -3
  49. data/config/locales/lv.yml +1 -1
  50. data/config/locales/nl.yml +4 -5
  51. data/config/locales/no.yml +1 -1
  52. data/config/locales/pl.yml +11 -2
  53. data/config/locales/pt.yml +1 -2
  54. data/config/locales/ro-RO.yml +1 -2
  55. data/config/locales/si-LK.yml +1 -0
  56. data/config/locales/sv.yml +1 -2
  57. data/config/locales/sw-KE.yml +1 -0
  58. data/config/locales/tr-TR.yml +88 -1
  59. data/config/locales/zh-CN.yml +1 -2
  60. data/db/migrate/20201110152921_add_salt_to_decidim_forms_questionnaires.rb +16 -0
  61. data/db/migrate/20210208094442_add_max_characters_to_decidim_forms_questions.rb +7 -0
  62. data/lib/decidim/api/answer_option_type.rb +13 -0
  63. data/lib/decidim/api/question_type.rb +21 -0
  64. data/lib/decidim/api/questionnaire_entity_interface.rb +5 -5
  65. data/lib/decidim/api/questionnaire_type.rb +19 -0
  66. data/lib/decidim/exporters/form_pdf_controller_helper.rb +2 -0
  67. data/lib/decidim/forms.rb +1 -1
  68. data/lib/decidim/forms/api.rb +3 -0
  69. data/lib/decidim/forms/data_portability_user_answers_serializer.rb +7 -1
  70. data/lib/decidim/forms/test/factories.rb +13 -2
  71. data/lib/decidim/forms/test/shared_examples/has_questionnaire.rb +38 -5
  72. data/lib/decidim/forms/test/shared_examples/manage_questionnaires.rb +5 -4
  73. data/lib/decidim/forms/test/shared_examples/manage_questionnaires/add_display_conditions.rb +7 -3
  74. data/lib/decidim/forms/test/shared_examples/manage_questionnaires/add_questions.rb +1 -1
  75. data/lib/decidim/forms/test/shared_examples/manage_questionnaires/update_questions.rb +5 -0
  76. data/lib/decidim/forms/user_answers_serializer.rb +10 -2
  77. data/lib/decidim/forms/version.rb +1 -1
  78. metadata +23 -18
  79. data/app/types/decidim/forms/answer_option_type.rb +0 -14
  80. data/app/types/decidim/forms/question_type.rb +0 -23
  81. data/app/types/decidim/forms/questionnaire_type.rb +0 -22
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0f8714ccc1417cbe818e4eac8a9aba7c5ae7882047c6bbb89c74615421e603cd
4
- data.tar.gz: 5632f71135c4614f6ebcaca269c2779f3ed131b90ba756e4154baa753878ddba
3
+ metadata.gz: 1f06c183113352ec8c74d0d958b1f1936a71f81ddd55c890e7254e1480b044f2
4
+ data.tar.gz: aafe2af6840afd6e2c757916bd44e185bbb2b4474380860e6c0d08641d246cb0
5
5
  SHA512:
6
- metadata.gz: 58a20ef3965ef39541a29a672f2322b284047083750f9d3c9be87db8a8acfebea1aad8a51d8890c04346afd8665cdb9c6aa625422b52102baf3322dd686bd2d1
7
- data.tar.gz: c0658ed8a17502cb807e2616f7ebb09e78b119ab8473e0e65955d5706cc5ba3cad58321f741c17ea3e09ae5c71ac7daed8d18480dcdd2a6cfda892e22efdb18f
6
+ metadata.gz: 5ed91cbe536d809e93dd5a7b4f8723b04d982f25cc98aca21eca9b4a7c4a31d4c7f9c78f9aca9c7eeed8c139bdcc33dee163f0838d84c009f6d8d081585bdaa0
7
+ data.tar.gz: c0eadff0a39fcd19d392dbc4b49db1c7f0715f378a9e4287e4af562e8a008fcac3dc70c281376c00fb83f02d177cb5f1e5b89376af2cc5921288a56bf202330f
@@ -29,14 +29,13 @@
29
29
  let multipleInput = [];
30
30
 
31
31
  $conditionWrapperField.find(".radio-button-collection, .check-box-collection").find(".collection-input").each((idx, el) => {
32
- const $label = $(el).find("label");
33
- const checked = !$label.find("input[type='hidden']").is("[disabled]");
32
+ const $input = $(el).find("input[name$=\\[body\\]]");
33
+ const checked = $input.is(":checked");
34
34
 
35
35
  if (checked) {
36
- const $textField = $(el).find("input[name$=\\[custom_body\\]]");
37
- const text = $textField.val();
38
- const value = $label.find("input:not([type='hidden'])").val();
39
- const id = $label.find("input[type='hidden']").val();
36
+ const text = $(el).find("input[name$=\\[custom_body\\]]").val();
37
+ const value = $input.val();
38
+ const id = $(el).find("input[name$=\\[answer_option_id\\]]").val();
40
39
 
41
40
  multipleInput.push({ id, value, text });
42
41
  }
@@ -53,16 +53,16 @@
53
53
  window.exitUrl = event.currentTarget.action;
54
54
  });
55
55
 
56
- window.onbeforeunload = () => {
56
+ window.addEventListener("beforeunload", (event) => {
57
57
  const exitUrl = window.exitUrl;
58
58
  const hasChanged = $form.data("changed");
59
59
  window.exitUrl = null;
60
60
 
61
61
  if (!hasChanged || (exitUrl && exitUrl.includes(safePath))) {
62
- return null;
62
+ return;
63
63
  }
64
64
 
65
- return "";
66
- }
65
+ event.returnValue = true;
66
+ });
67
67
  }
68
68
  })(window);
@@ -48,7 +48,8 @@ module Decidim
48
48
  position: form_question.position,
49
49
  mandatory: form_question.mandatory,
50
50
  question_type: form_question.question_type,
51
- max_choices: form_question.max_choices
51
+ max_choices: form_question.max_choices,
52
+ max_characters: form_question.max_characters
52
53
  }
53
54
 
54
55
  update_nested_model(form_question, question_attributes, @questionnaire.questions) do |question|
@@ -4,6 +4,8 @@ module Decidim
4
4
  module Forms
5
5
  # This command is executed when the user answers a Questionnaire.
6
6
  class AnswerQuestionnaire < Rectify::Command
7
+ include ::Decidim::MultipleAttachmentsMethods
8
+
7
9
  # Initializes a AnswerQuestionnaire Command.
8
10
  #
9
11
  # form - The form from which to get the data.
@@ -21,14 +23,34 @@ module Decidim
21
23
  return broadcast(:invalid) if @form.invalid?
22
24
 
23
25
  answer_questionnaire
24
- broadcast(:ok)
26
+
27
+ if @errors
28
+ reset_form_attachments
29
+ broadcast(:invalid)
30
+ else
31
+ broadcast(:ok)
32
+ end
25
33
  end
26
34
 
27
35
  attr_reader :form
28
36
 
29
37
  private
30
38
 
39
+ # This method will add an error to the `add_documents` field only if there's
40
+ # any error in any other field or an error in another answer in the
41
+ # questionnaire. This is needed because when the form has
42
+ # an error, the attachments are lost, so we need a way to inform the user
43
+ # of this problem.
44
+ def reset_form_attachments
45
+ @form.responses.each do |answer|
46
+ answer.errors.add(:add_documents, :needs_to_be_reattached) if answer.has_attachments?
47
+ end
48
+ end
49
+
31
50
  def answer_questionnaire
51
+ @main_form = @form
52
+ @errors = nil
53
+
32
54
  Answer.transaction do
33
55
  form.responses_by_step.flatten.select(&:display_conditions_fulfilled?).each do |form_answer|
34
56
  answer = Answer.new(
@@ -51,7 +73,27 @@ module Decidim
51
73
  end
52
74
 
53
75
  answer.save!
76
+
77
+ next unless form_answer.question.has_attachments?
78
+
79
+ # The attachments module expects `@form` to be the form with the
80
+ # attachments
81
+ @form = form_answer
82
+ @attached_to = answer
83
+
84
+ build_attachments
85
+
86
+ if attachments_invalid?
87
+ @errors = true
88
+ next
89
+ end
90
+
91
+ create_attachments if process_attachments?
92
+ document_cleanup!
54
93
  end
94
+
95
+ @form = @main_form
96
+ raise ActiveRecord::Rollback if @errors
55
97
  end
56
98
  end
57
99
  end
@@ -15,8 +15,10 @@ module Decidim
15
15
 
16
16
  included do
17
17
  helper Decidim::Forms::Admin::ApplicationHelper
18
+ include Decidim::TranslatableAttributes
19
+
18
20
  helper_method :questionnaire_for, :questionnaire, :blank_question, :blank_answer_option, :blank_matrix_row,
19
- :blank_display_condition, :question_types, :display_condition_types, :update_url, :public_url, :answer_options_url
21
+ :blank_display_condition, :question_types, :display_condition_types, :update_url, :public_url, :answer_options_url, :edit_questionnaire_title
20
22
 
21
23
  if defined? Decidim::Templates::Admin
22
24
  include Decidim::Templates::Admin::Concerns::Templatable
@@ -97,7 +99,13 @@ module Decidim
97
99
  # Returns the url to get the answer options json (for the display conditions form)
98
100
  # for the question with id = params[:id]
99
101
  def answer_options_url(params)
100
- url_for([questionnaire.questionnaire_for, action: :answer_options, format: :json, **params])
102
+ url_for([questionnaire.questionnaire_for, { action: :answer_options, format: :json, **params }])
103
+ end
104
+
105
+ # Implement this method in your controller to set the title
106
+ # of the edit form.
107
+ def edit_questionnaire_title
108
+ t(:title, scope: "decidim.forms.admin.questionnaires.form", questionnaire_for: translated_attribute(questionnaire_for.try(:title)))
101
109
  end
102
110
 
103
111
  private
@@ -26,7 +26,7 @@ module Decidim
26
26
  end
27
27
 
28
28
  def answer
29
- enforce_permission_to :answer, :questionnaire
29
+ enforce_permission_to_answer_questionnaire
30
30
 
31
31
  @form = form(Decidim::Forms::QuestionnaireForm).from_params(params, session_token: session_token, ip_hash: ip_hash)
32
32
 
@@ -78,7 +78,7 @@ module Decidim
78
78
  # You can implement this method in your controller to change the URL
79
79
  # where the questionnaire will be submitted.
80
80
  def update_url
81
- url_for([questionnaire_for, action: :answer])
81
+ url_for([questionnaire_for, { action: :answer }])
82
82
  end
83
83
 
84
84
  # Points to the shortest path accessing the current form. This will be
@@ -87,7 +87,7 @@ module Decidim
87
87
  #
88
88
  # Overwrite this method at the controller.
89
89
  def form_path
90
- url_for([questionnaire_for, only_path: true])
90
+ url_for([questionnaire_for, { only_path: true }])
91
91
  end
92
92
 
93
93
  # Public: Method to be implemented at the controller. You need to
@@ -107,7 +107,7 @@ module Decidim
107
107
  end
108
108
 
109
109
  def spam_detected
110
- enforce_permission_to :answer, :questionnaire
110
+ enforce_permission_to_answer_questionnaire
111
111
 
112
112
  @form = form(Decidim::Forms::QuestionnaireForm).from_params(params)
113
113
 
@@ -115,6 +115,12 @@ module Decidim
115
115
  render template: "decidim/forms/questionnaires/show"
116
116
  end
117
117
 
118
+ # You can implement this method in your controller to change the
119
+ # enforce_permission_to arguments.
120
+ def enforce_permission_to_answer_questionnaire
121
+ enforce_permission_to :answer, :questionnaire
122
+ end
123
+
118
124
  def ip_hash
119
125
  return nil unless request&.remote_ip
120
126
 
@@ -131,8 +137,9 @@ module Decidim
131
137
  @session_token ||= tokenize(id || session_id)
132
138
  end
133
139
 
134
- def tokenize(id)
135
- Digest::MD5.hexdigest("#{id}-#{Rails.application.secrets.secret_key_base}")
140
+ def tokenize(id, length: 10)
141
+ tokenizer = Decidim::Tokenizer.new(salt: questionnaire.salt || questionnaire.id, length: length)
142
+ tokenizer.int_digest(id).to_s
136
143
  end
137
144
  end
138
145
  end
@@ -14,6 +14,7 @@ module Decidim
14
14
  attribute :display_conditions, Array[DisplayConditionForm]
15
15
  attribute :matrix_rows, Array[QuestionMatrixRowForm]
16
16
  attribute :max_choices, Integer
17
+ attribute :max_characters, Integer, default: 0
17
18
  attribute :deleted, Boolean, default: false
18
19
 
19
20
  translatable_attribute :body, String
@@ -22,6 +23,7 @@ module Decidim
22
23
  validates :position, numericality: { greater_than_or_equal_to: 0 }
23
24
  validates :question_type, inclusion: { in: Decidim::Forms::Question::TYPES }
24
25
  validates :max_choices, numericality: { only_integer: true, greater_than: 1, less_than_or_equal_to: ->(form) { form.number_of_options } }, allow_blank: true
26
+ validates :max_characters, numericality: { only_integer: true, greater_than_or_equal_to: 0 }
25
27
  validates :body, translatable_presence: true, if: :requires_body?
26
28
  validates :matrix_rows, presence: true, if: :matrix?
27
29
  validates :answer_options, presence: true, if: :matrix?
@@ -5,18 +5,23 @@ module Decidim
5
5
  # This class holds a Form to save the questionnaire answers from Decidim's public page
6
6
  class AnswerForm < Decidim::Form
7
7
  include Decidim::TranslationsHelper
8
+ include Decidim::AttachmentAttributes
8
9
 
9
10
  attribute :question_id, String
10
11
  attribute :body, String
11
12
  attribute :choices, Array[AnswerChoiceForm]
12
13
  attribute :matrix_choices, Array[AnswerChoiceForm]
13
14
 
15
+ attachments_attribute :documents
16
+
14
17
  validates :body, presence: true, if: :mandatory_body?
15
18
  validates :selected_choices, presence: true, if: :mandatory_choices?
16
19
 
17
20
  validate :max_choices, if: -> { question.max_choices }
18
21
  validate :all_choices, if: -> { question.question_type == "sorting" }
19
22
  validate :min_choices, if: -> { question.matrix? && question.mandatory? }
23
+ validate :documents_present, if: -> { question.question_type == "files" && question.mandatory? }
24
+ validate :max_characters, if: -> { question.max_characters.positive? }
20
25
 
21
26
  delegate :mandatory_body?, :mandatory_choices?, :matrix?, to: :question
22
27
 
@@ -49,13 +54,21 @@ module Decidim
49
54
  choices.select(&:body)
50
55
  end
51
56
 
57
+ def custom_choices
58
+ choices.select(&:custom_body)
59
+ end
60
+
52
61
  def display_conditions_fulfilled?
53
62
  question.display_conditions.all? do |condition|
54
- answer = question.questionnaire.answers.find_by(question: condition.condition_question)
63
+ answer = context.responses&.find { |r| r.question_id&.to_i == condition.condition_question.id }
55
64
  condition.fulfilled?(answer)
56
65
  end
57
66
  end
58
67
 
68
+ def has_attachments?
69
+ question.has_attachments? && errors[:add_documents].empty? && add_documents.present?
70
+ end
71
+
59
72
  private
60
73
 
61
74
  def mandatory_body?
@@ -78,6 +91,16 @@ module Decidim
78
91
  end
79
92
  end
80
93
 
94
+ def max_characters
95
+ if body.present?
96
+ errors.add(:body, :too_long) if body.size > question.max_characters
97
+ elsif custom_choices.any?
98
+ custom_choices.each do |choice|
99
+ errors.add(:body, :too_long) if choice.custom_body.size > question.max_characters
100
+ end
101
+ end
102
+ end
103
+
81
104
  def min_choices
82
105
  errors.add(:choices, :missing) if grouped_choices.count != question.matrix_rows.count
83
106
  end
@@ -93,6 +116,10 @@ module Decidim
93
116
  def max_choices_label
94
117
  I18n.t("questionnaires.question.max_choices", scope: "decidim.forms", n: question.max_choices)
95
118
  end
119
+
120
+ def documents_present
121
+ errors.add(:add_documents, :blank) if add_documents.empty? && errors[:add_documents].empty?
122
+ end
96
123
  end
97
124
  end
98
125
  end
@@ -23,6 +23,11 @@ module Decidim
23
23
  end
24
24
  end
25
25
 
26
+ # Add other responses to the context so AnswerForm can validate conditional questions
27
+ def before_validation
28
+ context.responses = attributes[:responses]
29
+ end
30
+
26
31
  # Public: Splits reponses by step, keeping the separator.
27
32
  #
28
33
  # Returns an array of steps. Each step is a list of the questions in that
@@ -31,7 +31,7 @@ module Decidim
31
31
  "placeholder" => options[:placeholder],
32
32
  "locale" => I18n.locale
33
33
  }
34
- content_tag :span, class: options[:class], data: data do
34
+ tag.span(class: options[:class], data: data) do
35
35
  truncate translated_attribute(title), length: options[:max_length], omission: options[:omission]
36
36
  end
37
37
  end
@@ -45,11 +45,6 @@ module Decidim
45
45
  def templates_defined?
46
46
  defined? Decidim::Templates::Admin::Concerns::Templatable
47
47
  end
48
-
49
- def title_for_questionnaire
50
- scope = templates_defined? ? "decidim.templates.admin.questionnaire_templates" : "decidim.forms.admin.questionnaires"
51
- t("form.title", scope: scope)
52
- end
53
48
  end
54
49
  end
55
50
  end
@@ -21,17 +21,17 @@ module Decidim
21
21
  # You can implement this method in your controller to change the URL
22
22
  # where the questionnaire participants' info will be shown.
23
23
  def questionnaire_participants_url
24
- url_for([:index, questionnaire.questionnaire_for, format: nil])
24
+ url_for([:index, questionnaire.questionnaire_for, { format: nil }])
25
25
  end
26
26
 
27
27
  # You can implement this method in your controller to change the URL
28
28
  # where the user's questionnaire answers will be shown.
29
29
  def questionnaire_participant_answers_url(session_token)
30
- url_for([:show, questionnaire.questionnaire_for, session_token: session_token])
30
+ url_for([:show, questionnaire.questionnaire_for, { session_token: session_token }])
31
31
  end
32
32
 
33
33
  def questionnaire_export_response_url(session_token)
34
- url_for([:export_response, questionnaire.questionnaire_for, session_token: session_token, format: "pdf"])
34
+ url_for([:export_response, questionnaire.questionnaire_for, { session_token: session_token, format: "pdf" }])
35
35
  end
36
36
  end
37
37
  end
@@ -7,13 +7,16 @@ module Decidim
7
7
  #
8
8
  module QuestionnaireAnswersHelper
9
9
  def first_table_th(answer)
10
- return translated_attribute answer.first_short_answer.question.body if answer.first_short_answer
10
+ if answer.first_short_answer
11
+ @first_short_answer = answer.first_short_answer
12
+ return translated_attribute @first_short_answer.question.body
13
+ end
11
14
 
12
15
  t("session_token", scope: "decidim.forms.user_answers_serializer")
13
16
  end
14
17
 
15
18
  def first_table_td(answer)
16
- return answer.first_short_answer.body if answer.first_short_answer
19
+ return answer.first_short_answer&.body if @first_short_answer
17
20
 
18
21
  answer.session_token
19
22
  end
@@ -7,7 +7,7 @@ module Decidim
7
7
 
8
8
  def perform(user, title, answers)
9
9
  return if user&.email.blank?
10
- return if answers&.blank?
10
+ return if answers.blank?
11
11
 
12
12
  serializer = Decidim::Forms::UserAnswersSerializer
13
13
  export_data = Decidim::Exporters::FormPDF.new(answers, serializer).export
@@ -6,6 +6,7 @@ module Decidim
6
6
  class Answer < Forms::ApplicationRecord
7
7
  include Decidim::DataPortability
8
8
  include Decidim::NewsletterParticipant
9
+ include Decidim::HasAttachments
9
10
 
10
11
  belongs_to :user, class_name: "Decidim::User", foreign_key: "decidim_user_id", optional: true
11
12
  belongs_to :questionnaire, class_name: "Questionnaire", foreign_key: "decidim_questionnaire_id"
@@ -20,6 +21,8 @@ module Decidim
20
21
  validate :user_questionnaire_same_organization
21
22
  validate :question_belongs_to_questionnaire
22
23
 
24
+ scope :not_separator, -> { joins(:question).where.not(decidim_forms_questions: { question_type: Decidim::Forms::Question::SEPARATOR_TYPE }) }
25
+
23
26
  def self.user_collection(user)
24
27
  where(decidim_user_id: user.id)
25
28
  end
@@ -39,6 +42,11 @@ module Decidim
39
42
  answers.pluck(:decidim_user_id).flatten.compact.uniq
40
43
  end
41
44
 
45
+ def organization
46
+ user.organization if user.present?
47
+ questionnaire&.questionnaire_for.try(:organization)
48
+ end
49
+
42
50
  private
43
51
 
44
52
  def user_questionnaire_same_organization
@@ -21,18 +21,19 @@ module Decidim
21
21
  # Answer option provided to check for "equal" or "not_equal" (optional)
22
22
  belongs_to :answer_option, class_name: "AnswerOption", foreign_key: "decidim_answer_option_id", optional: true
23
23
 
24
- def fulfilled?(answer)
24
+ def fulfilled?(answer_form)
25
+ return answer_form.present? if condition_type == "answered"
26
+ return answer_form.blank? if condition_type == "not_answered"
27
+ # rest of options require presence
28
+ return if answer_form.blank?
29
+
25
30
  case condition_type
26
- when "answered"
27
- answer.present?
28
- when "not_answered"
29
- answer.blank?
30
31
  when "equal"
31
- answer.present? ? answer.choices.pluck(:decidim_answer_option_id).include?(answer_option.id) : false
32
+ answer_form.choices.pluck(:answer_option_id).include?(answer_option.id)
32
33
  when "not_equal"
33
- answer.present? ? !answer.choices.pluck(:decidim_answer_option_id).include?(answer_option.id) : true
34
+ answer_form.choices.pluck(:answer_option_id).exclude?(answer_option.id)
34
35
  when "match"
35
- answer.present? ? condition_value.values.any? { |value| answer.body.match?(Regexp.new(value, Regexp::IGNORECASE)) } : false
36
+ condition_value.values.reject(&:blank?).any? { |value| answer_form_matches?(answer_form, value) }
36
37
  end
37
38
  end
38
39
 
@@ -46,6 +47,19 @@ module Decidim
46
47
  value: condition_value&.dig(I18n.locale.to_s)
47
48
  }.compact
48
49
  end
50
+
51
+ private
52
+
53
+ def answer_form_matches?(answer_form, value)
54
+ search = Regexp.new(value, Regexp::IGNORECASE)
55
+ if answer_form.body
56
+ answer_form.body.match?(search)
57
+ else
58
+ answer_form.choices.any? do |choice_value|
59
+ choice_value.body&.match?(search) || choice_value.custom_body&.match?(search)
60
+ end
61
+ end
62
+ end
49
63
  end
50
64
  end
51
65
  end