decidim-forms 0.30.2 → 0.31.0.rc1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +10 -10
- data/app/cells/decidim/forms/matrix_readonly/show.erb +1 -1
- data/app/cells/decidim/forms/matrix_readonly_cell.rb +3 -3
- data/app/cells/decidim/forms/question_readonly/show.erb +5 -5
- data/app/cells/decidim/forms/question_readonly/title_and_description.erb +3 -3
- data/app/cells/decidim/forms/response_readonly_cell.rb +9 -0
- data/app/cells/decidim/forms/step_navigation/show.erb +3 -3
- data/app/cells/decidim/forms/step_navigation_cell.rb +3 -3
- data/app/commands/decidim/forms/admin/update_questions.rb +6 -6
- data/app/commands/decidim/forms/{answer_questionnaire.rb → response_questionnaire.rb} +35 -35
- data/app/controllers/decidim/forms/admin/concerns/has_questionnaire.rb +12 -12
- data/app/controllers/decidim/forms/admin/concerns/{has_questionnaire_answers.rb → has_questionnaire_responses.rb} +23 -19
- data/app/controllers/decidim/forms/concerns/has_questionnaire.rb +38 -33
- data/app/forms/decidim/forms/admin/display_condition_form.rb +14 -14
- data/app/forms/decidim/forms/admin/question_form.rb +3 -3
- data/app/forms/decidim/forms/admin/{answer_option_form.rb → response_option_form.rb} +3 -3
- data/app/forms/decidim/forms/questionnaire_form.rb +8 -9
- data/app/forms/decidim/forms/{answer_choice_form.rb → response_choice_form.rb} +5 -5
- data/app/forms/decidim/forms/{answer_form.rb → response_form.rb} +9 -9
- data/app/helpers/decidim/forms/admin/application_helper.rb +2 -2
- data/app/helpers/decidim/forms/admin/concerns/{has_questionnaire_answers_pagination_helper.rb → has_questionnaire_responses_pagination_helper.rb} +4 -4
- data/app/helpers/decidim/forms/admin/concerns/{has_questionnaire_answers_url_helper.rb → has_questionnaire_responses_url_helper.rb} +5 -5
- data/app/helpers/decidim/forms/admin/questionnaire_responses_helper.rb +32 -0
- data/app/helpers/decidim/forms/application_helper.rb +1 -2
- data/app/jobs/decidim/forms/{export_questionnaire_answers_job.rb → export_questionnaire_responses_job.rb} +5 -5
- data/app/models/decidim/forms/display_condition.rb +20 -20
- data/app/models/decidim/forms/question.rb +14 -13
- data/app/models/decidim/forms/question_matrix_row.rb +1 -1
- data/app/models/decidim/forms/questionnaire.rb +11 -6
- data/app/models/decidim/forms/{answer.rb → response.rb} +9 -9
- data/app/models/decidim/forms/response_choice.rb +22 -0
- data/app/models/decidim/forms/{answer_option.rb → response_option.rb} +5 -5
- data/app/packs/entrypoints/decidim_forms_admin.js +3 -3
- data/app/packs/src/decidim/forms/admin/collapsible_questions.js +12 -10
- data/app/packs/src/decidim/forms/admin/forms.js +35 -35
- data/app/packs/src/decidim/forms/admin/{publish_answers_buttons.js → publish_responses_buttons.js} +12 -12
- data/app/packs/src/decidim/forms/display_conditions.component.js +12 -12
- data/app/packs/src/decidim/forms/forms.js +4 -4
- data/app/packs/stylesheets/decidim/forms/forms.scss +2 -2
- data/app/presenters/decidim/forms/admin/questionnaire_participant_presenter.rb +13 -13
- data/app/presenters/decidim/forms/admin/{questionnaire_answer_presenter.rb → questionnaire_response_presenter.rb} +15 -15
- data/app/presenters/decidim/forms/admin_log/question_presenter.rb +3 -3
- data/app/presenters/decidim/forms/{answer_option_presenter.rb → response_option_presenter.rb} +2 -2
- data/app/queries/decidim/forms/questionnaire_participant.rb +5 -5
- data/app/queries/decidim/forms/questionnaire_participants.rb +6 -6
- data/app/queries/decidim/forms/questionnaire_user_responses.rb +32 -0
- data/app/views/decidim/forms/admin/questionnaires/_display_condition.html.erb +9 -9
- data/app/views/decidim/forms/admin/questionnaires/_form.html.erb +2 -2
- data/app/views/decidim/forms/admin/questionnaires/_matrix_row.html.erb +3 -3
- data/app/views/decidim/forms/admin/questionnaires/_question.html.erb +16 -16
- data/app/views/decidim/forms/admin/questionnaires/_questions_form.html.erb +11 -10
- data/app/views/decidim/forms/admin/questionnaires/{_answer_option.html.erb → _response_option.html.erb} +7 -7
- data/app/views/decidim/forms/admin/questionnaires/_response_option_template.html.erb +7 -0
- data/app/views/decidim/forms/admin/questionnaires/_separator.html.erb +10 -7
- data/app/views/decidim/forms/admin/questionnaires/_title_and_description.html.erb +7 -7
- data/app/views/decidim/forms/admin/questionnaires/edit.html.erb +1 -1
- data/app/views/decidim/forms/admin/questionnaires/edit_questions.html.erb +8 -8
- data/app/views/decidim/forms/admin/questionnaires/responses/index.html.erb +80 -0
- data/app/views/decidim/forms/admin/questionnaires/responses/show.html.erb +43 -0
- data/app/views/decidim/forms/questionnaires/_questionnaire.html.erb +22 -26
- data/app/views/decidim/forms/questionnaires/_questionnaire_readonly.html.erb +8 -3
- data/app/views/decidim/forms/questionnaires/_response.html.erb +61 -0
- data/app/views/decidim/forms/questionnaires/edit.html.erb +2 -2
- data/app/views/decidim/forms/questionnaires/responses/_files.html.erb +1 -0
- data/app/views/decidim/forms/questionnaires/responses/_long_response.html.erb +3 -0
- data/app/views/decidim/forms/questionnaires/{answers → responses}/_matrix_multiple.html.erb +16 -15
- data/app/views/decidim/forms/questionnaires/{answers → responses}/_matrix_single.html.erb +17 -16
- data/app/views/decidim/forms/questionnaires/responses/_multiple_option.html.erb +26 -0
- data/app/views/decidim/forms/questionnaires/responses/_separator.html.erb +1 -0
- data/app/views/decidim/forms/questionnaires/responses/_short_response.html.erb +3 -0
- data/app/views/decidim/forms/questionnaires/responses/_single_option.html.erb +33 -0
- data/app/views/decidim/forms/questionnaires/responses/_sorting.html.erb +26 -0
- data/app/views/decidim/forms/questionnaires/responses/_title_and_description.html.erb +1 -0
- data/app/views/decidim/forms/questionnaires/show.html.erb +10 -10
- data/config/assets.rb +2 -2
- data/config/locales/ar.yml +0 -22
- data/config/locales/bg.yml +3 -36
- data/config/locales/ca-IT.yml +73 -73
- data/config/locales/ca.yml +73 -73
- data/config/locales/cs.yml +72 -75
- data/config/locales/de.yml +72 -72
- data/config/locales/el.yml +3 -36
- data/config/locales/en.yml +83 -83
- data/config/locales/es-MX.yml +75 -75
- data/config/locales/es-PY.yml +75 -75
- data/config/locales/es.yml +73 -73
- data/config/locales/eu.yml +71 -71
- data/config/locales/fi-plain.yml +71 -71
- data/config/locales/fi.yml +69 -69
- data/config/locales/fr-CA.yml +70 -67
- data/config/locales/fr.yml +70 -67
- data/config/locales/ga-IE.yml +0 -7
- data/config/locales/gl.yml +0 -22
- data/config/locales/hu.yml +0 -22
- data/config/locales/id-ID.yml +0 -22
- data/config/locales/it.yml +3 -40
- data/config/locales/ja.yml +80 -80
- data/config/locales/lb.yml +3 -36
- data/config/locales/lt.yml +3 -36
- data/config/locales/lv.yml +0 -22
- data/config/locales/nl.yml +3 -36
- data/config/locales/no.yml +3 -36
- data/config/locales/pl.yml +3 -36
- data/config/locales/pt-BR.yml +3 -36
- data/config/locales/pt.yml +3 -36
- data/config/locales/ro-RO.yml +22 -51
- data/config/locales/ru.yml +0 -9
- data/config/locales/sk.yml +0 -22
- data/config/locales/sl.yml +0 -5
- data/config/locales/sv.yml +82 -54
- data/config/locales/tr-TR.yml +3 -36
- data/config/locales/zh-CN.yml +3 -36
- data/config/locales/zh-TW.yml +3 -36
- data/db/migrate/20190315203056_add_session_token_to_decidim_forms_answers.rb +1 -1
- data/db/migrate/20250314150250_rename_answer_to_response_in_decidim_forms.rb +30 -0
- data/db/migrate/20250319130003_change_question_types_in_questions.rb +14 -0
- data/lib/decidim/api/question_matrix_row_type.rb +13 -0
- data/lib/decidim/api/question_type.rb +4 -3
- data/lib/decidim/api/questionnaire_type.rb +1 -0
- data/lib/decidim/api/response_option_type.rb +13 -0
- data/lib/decidim/exporters/form_pdf.rb +33 -33
- data/lib/decidim/forms/api.rb +2 -1
- data/lib/decidim/forms/{download_your_data_user_answers_serializer.rb → download_your_data_user_responses_serializer.rb} +3 -3
- data/lib/decidim/forms/engine.rb +2 -2
- data/lib/decidim/forms/test/factories.rb +24 -24
- data/lib/decidim/forms/test/shared_examples/has_questionnaire.rb +176 -176
- data/lib/decidim/forms/test/shared_examples/manage_questionnaire_responses.rb +159 -0
- data/lib/decidim/forms/test/shared_examples/manage_questionnaires/add_display_conditions.rb +19 -19
- data/lib/decidim/forms/test/shared_examples/manage_questionnaires/add_questions.rb +44 -44
- data/lib/decidim/forms/test/shared_examples/manage_questionnaires/update_display_conditions.rb +10 -10
- data/lib/decidim/forms/test/shared_examples/manage_questionnaires/update_questions.rb +18 -18
- data/lib/decidim/forms/test/shared_examples/manage_questionnaires.rb +4 -4
- data/lib/decidim/forms/test.rb +1 -1
- data/lib/decidim/forms/user_responses_serializer.rb +110 -0
- data/lib/decidim/forms/version.rb +1 -1
- data/lib/decidim/forms.rb +2 -2
- metadata +48 -45
- data/app/cells/decidim/forms/answer_readonly_cell.rb +0 -9
- data/app/helpers/decidim/forms/admin/questionnaire_answers_helper.rb +0 -30
- data/app/models/decidim/forms/answer_choice.rb +0 -22
- data/app/queries/decidim/forms/questionnaire_user_answers.rb +0 -32
- data/app/views/decidim/forms/admin/questionnaires/_answer_option_template.html.erb +0 -7
- data/app/views/decidim/forms/admin/questionnaires/answers/index.html.erb +0 -49
- data/app/views/decidim/forms/admin/questionnaires/answers/show.html.erb +0 -43
- data/app/views/decidim/forms/questionnaires/_answer.html.erb +0 -61
- data/app/views/decidim/forms/questionnaires/answers/_files.html.erb +0 -1
- data/app/views/decidim/forms/questionnaires/answers/_long_answer.html.erb +0 -3
- data/app/views/decidim/forms/questionnaires/answers/_multiple_option.html.erb +0 -25
- data/app/views/decidim/forms/questionnaires/answers/_separator.html.erb +0 -1
- data/app/views/decidim/forms/questionnaires/answers/_short_answer.html.erb +0 -3
- data/app/views/decidim/forms/questionnaires/answers/_single_option.html.erb +0 -32
- data/app/views/decidim/forms/questionnaires/answers/_sorting.html.erb +0 -26
- data/app/views/decidim/forms/questionnaires/answers/_title_and_description.html.erb +0 -1
- data/lib/decidim/api/answer_option_type.rb +0 -13
- data/lib/decidim/forms/test/shared_examples/manage_questionnaire_answers.rb +0 -149
- data/lib/decidim/forms/user_answers_serializer.rb +0 -105
- /data/app/cells/decidim/forms/{answer_readonly → response_readonly}/show.erb +0 -0
@@ -16,74 +16,79 @@ module Decidim
|
|
16
16
|
helper Decidim::Forms::ApplicationHelper
|
17
17
|
include FormFactory
|
18
18
|
|
19
|
-
helper_method :questionnaire_for, :questionnaire, :
|
19
|
+
helper_method :questionnaire_for, :questionnaire, :allow_responses?, :visitor_can_respond?, :visitor_already_responded?, :update_url, :visitor_can_edit_responses?,
|
20
|
+
:form_path
|
20
21
|
|
21
22
|
invisible_captcha on_spam: :spam_detected
|
22
23
|
|
23
24
|
def show
|
24
25
|
@form = form(Decidim::Forms::QuestionnaireForm).from_model(questionnaire)
|
25
|
-
render template:
|
26
|
+
render template:
|
26
27
|
end
|
27
28
|
|
28
|
-
# i18n-tasks-use t("decidim.forms.questionnaires.
|
29
|
-
# i18n-tasks-use t("decidim.forms.questionnaires.
|
30
|
-
def
|
31
|
-
|
29
|
+
# i18n-tasks-use t("decidim.forms.questionnaires.response.success")
|
30
|
+
# i18n-tasks-use t("decidim.forms.questionnaires.response.invalid")
|
31
|
+
def respond
|
32
|
+
enforce_permission_to_respond_questionnaire
|
32
33
|
|
33
34
|
@form = form(Decidim::Forms::QuestionnaireForm).from_params(params, session_token:, ip_hash:)
|
34
35
|
|
35
|
-
Decidim::Forms::
|
36
|
+
Decidim::Forms::ResponseQuestionnaire.call(@form, questionnaire, allow_editing_responses: allow_editing_responses?) do
|
36
37
|
on(:ok) do
|
37
|
-
flash[:notice] = I18n.t("
|
38
|
-
redirect_to
|
38
|
+
flash[:notice] = I18n.t("response.success", scope: i18n_flashes_scope)
|
39
|
+
redirect_to after_response_path
|
39
40
|
end
|
40
41
|
|
41
42
|
on(:invalid) do
|
42
|
-
flash.now[:alert] = I18n.t("
|
43
|
-
render template:
|
43
|
+
flash.now[:alert] = I18n.t("response.invalid", scope: i18n_flashes_scope)
|
44
|
+
render template:, status: :unprocessable_entity
|
44
45
|
end
|
45
46
|
end
|
46
47
|
end
|
47
48
|
|
49
|
+
def template
|
50
|
+
"decidim/forms/questionnaires/show"
|
51
|
+
end
|
52
|
+
|
48
53
|
# Public: Method to be implemented at the controller. You need to
|
49
|
-
# return true if the questionnaire can receive
|
50
|
-
def
|
51
|
-
raise "#{self.class.name} is expected to implement #
|
54
|
+
# return true if the questionnaire can receive responses
|
55
|
+
def allow_responses?
|
56
|
+
raise "#{self.class.name} is expected to implement #allow_responses?"
|
52
57
|
end
|
53
58
|
|
54
59
|
# Public: Method to be implemented at the controller if needed. You need to
|
55
|
-
# return true if the questionnaire can receive
|
60
|
+
# return true if the questionnaire can receive responses by unregistered users
|
56
61
|
def allow_unregistered?
|
57
62
|
false
|
58
63
|
end
|
59
64
|
|
60
|
-
# Public: return true if the current user (if logged) can
|
61
|
-
def
|
65
|
+
# Public: return true if the current user (if logged) can response the questionnaire
|
66
|
+
def visitor_can_respond?
|
62
67
|
current_user || allow_unregistered?
|
63
68
|
end
|
64
69
|
|
65
|
-
# Public: return true if the current user (or session visitor) can
|
66
|
-
def
|
67
|
-
questionnaire.
|
70
|
+
# Public: return true if the current user (or session visitor) can respond the questionnaire
|
71
|
+
def visitor_already_responded?
|
72
|
+
questionnaire.responded_by?(current_user || tokenize(session[:session_id]))
|
68
73
|
end
|
69
74
|
|
70
75
|
# Public: Returns a String or Object that will be passed to `redirect_to` after
|
71
|
-
#
|
76
|
+
# responding the questionnaire. By default it redirects to the questionnaire_for.
|
72
77
|
#
|
73
78
|
# It can be redefined at controller level if you need to redirect elsewhere.
|
74
|
-
def
|
79
|
+
def after_response_path
|
75
80
|
questionnaire_for
|
76
81
|
end
|
77
82
|
|
78
83
|
# You can implement this method in your controller to change the URL
|
79
84
|
# where the questionnaire will be submitted.
|
80
85
|
def update_url
|
81
|
-
url_for([questionnaire_for, { action: :
|
86
|
+
url_for([questionnaire_for, { action: :respond }])
|
82
87
|
end
|
83
88
|
|
84
89
|
# Points to the shortest path accessing the current form. This will be
|
85
90
|
# used to detect whether a user is leaving the form with some partial
|
86
|
-
#
|
91
|
+
# responses, so that we can warn them.
|
87
92
|
#
|
88
93
|
# Overwrite this method at the controller.
|
89
94
|
def form_path
|
@@ -98,12 +103,12 @@ module Decidim
|
|
98
103
|
|
99
104
|
private
|
100
105
|
|
101
|
-
def
|
106
|
+
def allow_editing_responses?
|
102
107
|
false
|
103
108
|
end
|
104
109
|
|
105
|
-
def
|
106
|
-
current_user.present? && questionnaire_for.try(:
|
110
|
+
def visitor_can_edit_responses?
|
111
|
+
current_user.present? && questionnaire_for.try(:allow_editing_responses?)
|
107
112
|
end
|
108
113
|
|
109
114
|
def i18n_flashes_scope
|
@@ -111,22 +116,22 @@ module Decidim
|
|
111
116
|
end
|
112
117
|
|
113
118
|
def questionnaire
|
114
|
-
@questionnaire ||= Questionnaire.includes(questions: :
|
119
|
+
@questionnaire ||= Questionnaire.includes(questions: :response_options).find_by(questionnaire_for:)
|
115
120
|
end
|
116
121
|
|
117
122
|
def spam_detected
|
118
|
-
|
123
|
+
enforce_permission_to_respond_questionnaire
|
119
124
|
|
120
125
|
@form = form(Decidim::Forms::QuestionnaireForm).from_params(params)
|
121
126
|
|
122
|
-
flash.now[:alert] = I18n.t("
|
123
|
-
render template:
|
127
|
+
flash.now[:alert] = I18n.t("response.spam_detected", scope: i18n_flashes_scope)
|
128
|
+
render template:
|
124
129
|
end
|
125
130
|
|
126
131
|
# You can implement this method in your controller to change the
|
127
132
|
# enforce_permission_to arguments.
|
128
|
-
def
|
129
|
-
enforce_permission_to :
|
133
|
+
def enforce_permission_to_respond_questionnaire
|
134
|
+
enforce_permission_to :respond, :questionnaire
|
130
135
|
end
|
131
136
|
|
132
137
|
def ip_hash
|
@@ -9,7 +9,7 @@ module Decidim
|
|
9
9
|
|
10
10
|
attribute :decidim_question_id, Integer
|
11
11
|
attribute :decidim_condition_question_id, Integer
|
12
|
-
attribute :
|
12
|
+
attribute :decidim_response_option_id, Integer
|
13
13
|
attribute :condition_type, String
|
14
14
|
attribute :mandatory, Boolean, default: false
|
15
15
|
attribute :deleted, Boolean, default: false
|
@@ -18,13 +18,13 @@ module Decidim
|
|
18
18
|
|
19
19
|
validates :question, presence: true, unless: :deleted
|
20
20
|
validates :condition_question, presence: true, unless: :deleted
|
21
|
-
validates :
|
21
|
+
validates :response_option, presence: true, if: :response_option_mandatory?
|
22
22
|
|
23
23
|
validates :condition_value, translatable_presence: true, if: :condition_value_mandatory?
|
24
24
|
validates :condition_type, presence: true, unless: :deleted
|
25
25
|
|
26
26
|
validate :condition_question_position, unless: :deleted
|
27
|
-
validate :
|
27
|
+
validate :valid_response_option?, unless: :deleted
|
28
28
|
|
29
29
|
def to_param
|
30
30
|
return id if id.present?
|
@@ -32,10 +32,10 @@ module Decidim
|
|
32
32
|
"questionnaire-display-condition-id"
|
33
33
|
end
|
34
34
|
|
35
|
-
def
|
35
|
+
def response_options
|
36
36
|
return [] if condition_question.blank?
|
37
37
|
|
38
|
-
condition_question.
|
38
|
+
condition_question.response_options
|
39
39
|
end
|
40
40
|
|
41
41
|
def questions_for_select(questionnaire, id)
|
@@ -65,11 +65,11 @@ module Decidim
|
|
65
65
|
@condition_question ||= Question.find_by(id: decidim_condition_question_id)
|
66
66
|
end
|
67
67
|
|
68
|
-
# Finds the
|
68
|
+
# Finds the Response Option from the given decidim_response_option_id
|
69
69
|
#
|
70
|
-
# Returns a Decidim::Forms::
|
71
|
-
def
|
72
|
-
@
|
70
|
+
# Returns a Decidim::Forms::ResponseOption
|
71
|
+
def response_option
|
72
|
+
@response_option ||= ResponseOption.find_by(id: decidim_response_option_id)
|
73
73
|
end
|
74
74
|
|
75
75
|
private
|
@@ -78,15 +78,15 @@ module Decidim
|
|
78
78
|
!deleted && condition_type == "match"
|
79
79
|
end
|
80
80
|
|
81
|
-
def
|
81
|
+
def response_option_mandatory?
|
82
82
|
!deleted && %w(equal not_equal).include?(condition_type)
|
83
83
|
end
|
84
84
|
|
85
|
-
def
|
86
|
-
return unless
|
87
|
-
return if
|
85
|
+
def valid_response_option?
|
86
|
+
return unless response_option_mandatory?
|
87
|
+
return if response_option.blank?
|
88
88
|
|
89
|
-
errors.add(:
|
89
|
+
errors.add(:decidim_response_option_id, :invalid) if response_option.question.id != decidim_condition_question_id
|
90
90
|
end
|
91
91
|
|
92
92
|
def condition_question_position
|
@@ -10,7 +10,7 @@ module Decidim
|
|
10
10
|
attribute :position, Integer
|
11
11
|
attribute :mandatory, Boolean, default: false
|
12
12
|
attribute :question_type, String
|
13
|
-
attribute :
|
13
|
+
attribute :response_options, Array[ResponseOptionForm]
|
14
14
|
attribute :display_conditions, Array[DisplayConditionForm]
|
15
15
|
attribute :matrix_rows, Array[QuestionMatrixRowForm]
|
16
16
|
attribute :max_choices, Integer
|
@@ -26,7 +26,7 @@ module Decidim
|
|
26
26
|
validates :max_characters, numericality: { only_integer: true, greater_than_or_equal_to: 0 }
|
27
27
|
validates :body, translatable_presence: true, if: :requires_body?
|
28
28
|
validates :matrix_rows, presence: true, if: :matrix?
|
29
|
-
validates :
|
29
|
+
validates :response_options, presence: true, if: :matrix?
|
30
30
|
|
31
31
|
def to_param
|
32
32
|
return id if id.present?
|
@@ -35,7 +35,7 @@ module Decidim
|
|
35
35
|
end
|
36
36
|
|
37
37
|
def number_of_options
|
38
|
-
|
38
|
+
response_options.size
|
39
39
|
end
|
40
40
|
|
41
41
|
def separator?
|
@@ -3,8 +3,8 @@
|
|
3
3
|
module Decidim
|
4
4
|
module Forms
|
5
5
|
module Admin
|
6
|
-
# This class holds a Form to update
|
7
|
-
class
|
6
|
+
# This class holds a Form to update response options
|
7
|
+
class ResponseOptionForm < Decidim::Form
|
8
8
|
include TranslatableAttributes
|
9
9
|
|
10
10
|
attribute :deleted, Boolean, default: false
|
@@ -17,7 +17,7 @@ module Decidim
|
|
17
17
|
def to_param
|
18
18
|
return id if id.present?
|
19
19
|
|
20
|
-
"questionnaire-question-
|
20
|
+
"questionnaire-question-response-option-id"
|
21
21
|
end
|
22
22
|
end
|
23
23
|
end
|
@@ -2,18 +2,17 @@
|
|
2
2
|
|
3
3
|
module Decidim
|
4
4
|
module Forms
|
5
|
-
# This class holds a Form to
|
5
|
+
# This class holds a Form to response a questionnaire from Decidim's public page.
|
6
6
|
class QuestionnaireForm < Decidim::Form
|
7
7
|
include ActiveModel::Validations::Callbacks
|
8
8
|
|
9
|
-
# as questionnaire uses "
|
9
|
+
# as questionnaire uses "responses" for the database relationships is
|
10
10
|
# important not to use the same word here to avoid querying all the entries, resulting in a high performance penalty
|
11
|
-
attribute :responses, Array[
|
12
|
-
attribute :user_group_id, Integer
|
11
|
+
attribute :responses, Array[ResponseForm]
|
13
12
|
attribute :public_participation, Boolean, default: false
|
14
13
|
|
15
14
|
attribute :tos_agreement, Boolean
|
16
|
-
attribute :
|
15
|
+
attribute :allow_editing_responses, Boolean, default: false
|
17
16
|
|
18
17
|
before_validation :before_validation
|
19
18
|
|
@@ -25,17 +24,17 @@ module Decidim
|
|
25
24
|
# Returns nothing.
|
26
25
|
def map_model(model)
|
27
26
|
self.responses = model.questions.map do |question|
|
28
|
-
|
27
|
+
ResponseForm.from_model(Decidim::Forms::Response.new(question:))
|
29
28
|
end
|
30
29
|
end
|
31
30
|
|
32
|
-
def
|
31
|
+
def add_responses!(questionnaire:, session_token:, ip_hash:)
|
33
32
|
self.responses = questionnaire.questions.map do |question|
|
34
|
-
|
33
|
+
ResponseForm.from_model(Decidim::Forms::Response.where(question:, user: current_user, session_token:, ip_hash:).first_or_initialize)
|
35
34
|
end
|
36
35
|
end
|
37
36
|
|
38
|
-
# Add other responses to the context so
|
37
|
+
# Add other responses to the context so ResponseForm can validate conditional questions
|
39
38
|
def before_validation
|
40
39
|
context.responses = attributes[:responses]
|
41
40
|
end
|
@@ -2,18 +2,18 @@
|
|
2
2
|
|
3
3
|
module Decidim
|
4
4
|
module Forms
|
5
|
-
# This class holds a Form to save the chosen option for an
|
6
|
-
class
|
5
|
+
# This class holds a Form to save the chosen option for an response
|
6
|
+
class ResponseChoiceForm < Decidim::Form
|
7
7
|
attribute :body, String
|
8
8
|
attribute :custom_body, String
|
9
9
|
attribute :position, Integer
|
10
|
-
attribute :
|
10
|
+
attribute :response_option_id, Integer
|
11
11
|
attribute :matrix_row_id, Integer
|
12
12
|
|
13
|
-
validates :
|
13
|
+
validates :response_option_id, presence: true
|
14
14
|
|
15
15
|
def map_model(model)
|
16
|
-
self.
|
16
|
+
self.response_option_id = model.decidim_response_option_id
|
17
17
|
self.matrix_row_id = model.decidim_question_matrix_row_id
|
18
18
|
end
|
19
19
|
end
|
@@ -2,15 +2,15 @@
|
|
2
2
|
|
3
3
|
module Decidim
|
4
4
|
module Forms
|
5
|
-
# This class holds a Form to save the questionnaire
|
6
|
-
class
|
5
|
+
# This class holds a Form to save the questionnaire responses from Decidim's public page
|
6
|
+
class ResponseForm < Decidim::Form
|
7
7
|
include Decidim::TranslationsHelper
|
8
8
|
include Decidim::AttachmentAttributes
|
9
9
|
|
10
10
|
attribute :question_id, String
|
11
11
|
attribute :body, String
|
12
|
-
attribute :choices, Array[
|
13
|
-
attribute :matrix_choices, Array[
|
12
|
+
attribute :choices, Array[ResponseChoiceForm]
|
13
|
+
attribute :matrix_choices, Array[ResponseChoiceForm]
|
14
14
|
|
15
15
|
attachments_attribute :documents
|
16
16
|
|
@@ -47,7 +47,7 @@ module Decidim
|
|
47
47
|
self.documents = model.attachments
|
48
48
|
|
49
49
|
self.choices = model.choices.map do |choice|
|
50
|
-
|
50
|
+
ResponseChoiceForm.from_model(choice)
|
51
51
|
end
|
52
52
|
end
|
53
53
|
|
@@ -67,8 +67,8 @@ module Decidim
|
|
67
67
|
|
68
68
|
def mandatory_conditions_fulfilled?
|
69
69
|
question.display_conditions.where(mandatory: true).all? do |condition|
|
70
|
-
|
71
|
-
condition.fulfilled?(
|
70
|
+
response = context.responses&.find { |r| r.question_id&.to_i == condition.condition_question.id }
|
71
|
+
condition.fulfilled?(response)
|
72
72
|
end
|
73
73
|
end
|
74
74
|
|
@@ -76,8 +76,8 @@ module Decidim
|
|
76
76
|
return true unless question.display_conditions.where(mandatory: false).any?
|
77
77
|
|
78
78
|
question.display_conditions.where(mandatory: false).any? do |condition|
|
79
|
-
|
80
|
-
condition.fulfilled?(
|
79
|
+
response = context.responses&.find { |r| r.question_id&.to_i == condition.condition_question.id }
|
80
|
+
condition.fulfilled?(response)
|
81
81
|
end
|
82
82
|
end
|
83
83
|
|
@@ -12,8 +12,8 @@ module Decidim
|
|
12
12
|
"questionnaire_question_#{question.to_param}"
|
13
13
|
end
|
14
14
|
|
15
|
-
def
|
16
|
-
"questionnaire_question_#{question.to_param}
|
15
|
+
def tabs_id_for_question_response_option(question, response_option)
|
16
|
+
"questionnaire_question_#{question.to_param}_response_option_#{response_option.to_param}"
|
17
17
|
end
|
18
18
|
|
19
19
|
def tabs_id_for_question_display_condition(question, display_condition)
|
@@ -4,9 +4,9 @@ module Decidim
|
|
4
4
|
module Forms
|
5
5
|
module Admin
|
6
6
|
module Concerns
|
7
|
-
# Pagination helper for
|
7
|
+
# Pagination helper for HasQuestionnaireResponses controller concern
|
8
8
|
#
|
9
|
-
module
|
9
|
+
module HasQuestionnaireResponsesPaginationHelper
|
10
10
|
def self.included(base)
|
11
11
|
base.helper_method :prev_url, :next_url, :first?, :last?, :current_idx
|
12
12
|
end
|
@@ -15,14 +15,14 @@ module Decidim
|
|
15
15
|
return if first?
|
16
16
|
|
17
17
|
token = participant_ids[current_idx - 1]
|
18
|
-
|
18
|
+
questionnaire_participant_responses_url(token)
|
19
19
|
end
|
20
20
|
|
21
21
|
def next_url
|
22
22
|
return if last?
|
23
23
|
|
24
24
|
token = participant_ids[current_idx + 1]
|
25
|
-
|
25
|
+
questionnaire_participant_responses_url(token)
|
26
26
|
end
|
27
27
|
|
28
28
|
def first?
|
@@ -4,12 +4,12 @@ module Decidim
|
|
4
4
|
module Forms
|
5
5
|
module Admin
|
6
6
|
module Concerns
|
7
|
-
# Url helper for
|
7
|
+
# Url helper for HasQuestionnaireResponses controller concern
|
8
8
|
#
|
9
|
-
module
|
9
|
+
module HasQuestionnaireResponsesUrlHelper
|
10
10
|
def self.included(base)
|
11
11
|
base.helper_method :questionnaire_url, :questionnaire_participants_url,
|
12
|
-
:
|
12
|
+
:questionnaire_participant_responses_url, :questionnaire_export_response_url
|
13
13
|
end
|
14
14
|
|
15
15
|
# You can implement this method in your controller to change the URL
|
@@ -25,8 +25,8 @@ module Decidim
|
|
25
25
|
end
|
26
26
|
|
27
27
|
# You can implement this method in your controller to change the URL
|
28
|
-
# where the user's questionnaire
|
29
|
-
def
|
28
|
+
# where the user's questionnaire responses will be shown.
|
29
|
+
def questionnaire_participant_responses_url(id)
|
30
30
|
url_for([questionnaire.questionnaire_for, { id: }])
|
31
31
|
end
|
32
32
|
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Forms
|
5
|
+
module Admin
|
6
|
+
# Custom helpers for questionnaire responses
|
7
|
+
#
|
8
|
+
module QuestionnaireResponsesHelper
|
9
|
+
def first_table_th(response)
|
10
|
+
return nil if response.nil?
|
11
|
+
|
12
|
+
if response.first_short_response
|
13
|
+
@first_short_response = response.first_short_response
|
14
|
+
return translated_attribute @first_short_response.question.body
|
15
|
+
end
|
16
|
+
|
17
|
+
t("session_token", scope: "decidim.forms.user_responses_serializer")
|
18
|
+
end
|
19
|
+
|
20
|
+
def first_table_td(response)
|
21
|
+
return response.first_short_response&.body if @first_short_response
|
22
|
+
|
23
|
+
response.session_token
|
24
|
+
end
|
25
|
+
|
26
|
+
def display_percentage(number)
|
27
|
+
number_to_percentage(number, precision: 0, strip_insignificant_zeros: true, locale: I18n.locale)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -5,12 +5,11 @@ module Decidim
|
|
5
5
|
# Custom helpers, scoped to the forms engine.
|
6
6
|
module ApplicationHelper
|
7
7
|
# Show cell for selected models
|
8
|
-
def
|
8
|
+
def show_public_participation?
|
9
9
|
model_name = questionnaire_for.model_name.element
|
10
10
|
|
11
11
|
permitted_models.include?(model_name)
|
12
12
|
end
|
13
|
-
alias show_public_participation? show_represent_user_group?
|
14
13
|
|
15
14
|
def permitted_models
|
16
15
|
%(meeting)
|
@@ -2,17 +2,17 @@
|
|
2
2
|
|
3
3
|
module Decidim
|
4
4
|
module Forms
|
5
|
-
class
|
5
|
+
class ExportQuestionnaireResponsesJob < ApplicationJob
|
6
6
|
include Decidim::PrivateDownloadHelper
|
7
7
|
|
8
8
|
queue_as :exports
|
9
9
|
|
10
|
-
def perform(user, file_name,
|
10
|
+
def perform(user, file_name, responses, export_type = nil)
|
11
11
|
return if user&.email.blank?
|
12
|
-
return if
|
12
|
+
return if responses.blank?
|
13
13
|
|
14
|
-
serializer = Decidim::Forms::
|
15
|
-
export_data = Decidim::Exporters::FormPDF.new(
|
14
|
+
serializer = Decidim::Forms::UserResponsesSerializer
|
15
|
+
export_data = Decidim::Exporters::FormPDF.new(responses, serializer).export
|
16
16
|
|
17
17
|
private_export = attach_archive(export_data, file_name, user, export_type)
|
18
18
|
|
@@ -5,12 +5,12 @@ module Decidim
|
|
5
5
|
# The data store for a DisplayCondition in the Decidim::Forms component.
|
6
6
|
# A display condition is associated to two questions. :question is the question
|
7
7
|
# that we want to display or hide based on some conditions, and :condition_question
|
8
|
-
# is the question the
|
9
|
-
# Conditions can be whether the question is
|
10
|
-
# if the selected
|
11
|
-
# the text value of the
|
8
|
+
# is the question the responses of which are checked against the conditions.
|
9
|
+
# Conditions can be whether the question is responded ("responded") or it is not ("not_responded"),
|
10
|
+
# if the selected response option is ("equal") or is not ("not_equal") a given one, or whether
|
11
|
+
# the text value of the response matches a string ("match").
|
12
12
|
class DisplayCondition < Forms::ApplicationRecord
|
13
|
-
enum condition_type
|
13
|
+
enum :condition_type, [:responded, :not_responded, :equal, :not_equal, :match], prefix: true
|
14
14
|
|
15
15
|
# Question which will be displayed or hidden
|
16
16
|
belongs_to :question,
|
@@ -19,29 +19,29 @@ module Decidim
|
|
19
19
|
inverse_of: :display_conditions,
|
20
20
|
counter_cache: :display_conditions_count
|
21
21
|
|
22
|
-
# Question the
|
22
|
+
# Question the responses of which are checked against conditions
|
23
23
|
belongs_to :condition_question,
|
24
24
|
class_name: "Question",
|
25
25
|
foreign_key: "decidim_condition_question_id",
|
26
26
|
inverse_of: :display_conditions_for_other_questions,
|
27
27
|
counter_cache: :display_conditions_for_other_questions_count
|
28
28
|
|
29
|
-
#
|
30
|
-
belongs_to :
|
29
|
+
# Response option provided to check for "equal" or "not_equal" (optional)
|
30
|
+
belongs_to :response_option, class_name: "ResponseOption", foreign_key: "decidim_response_option_id", optional: true
|
31
31
|
|
32
|
-
def fulfilled?(
|
33
|
-
return
|
34
|
-
return
|
32
|
+
def fulfilled?(response_form)
|
33
|
+
return response_form.present? if condition_type == "responded"
|
34
|
+
return response_form.blank? if condition_type == "not_responded"
|
35
35
|
# rest of options require presence
|
36
|
-
return if
|
36
|
+
return if response_form.blank?
|
37
37
|
|
38
38
|
case condition_type
|
39
39
|
when "equal"
|
40
|
-
|
40
|
+
response_form.choices.pluck(:response_option_id).include?(response_option.id)
|
41
41
|
when "not_equal"
|
42
|
-
|
42
|
+
response_form.choices.pluck(:response_option_id).exclude?(response_option.id)
|
43
43
|
when "match"
|
44
|
-
condition_value.values.compact_blank.any? { |value|
|
44
|
+
condition_value.values.compact_blank.any? { |value| response_form_matches?(response_form, value) }
|
45
45
|
end
|
46
46
|
end
|
47
47
|
|
@@ -50,7 +50,7 @@ module Decidim
|
|
50
50
|
id:,
|
51
51
|
type: condition_type,
|
52
52
|
condition: decidim_condition_question_id,
|
53
|
-
option:
|
53
|
+
option: decidim_response_option_id,
|
54
54
|
mandatory:,
|
55
55
|
value: condition_value&.dig(I18n.locale.to_s)
|
56
56
|
}.compact
|
@@ -58,12 +58,12 @@ module Decidim
|
|
58
58
|
|
59
59
|
private
|
60
60
|
|
61
|
-
def
|
61
|
+
def response_form_matches?(response_form, value)
|
62
62
|
search = Regexp.new(value, Regexp::IGNORECASE)
|
63
|
-
if
|
64
|
-
|
63
|
+
if response_form.body
|
64
|
+
response_form.body.match?(search)
|
65
65
|
else
|
66
|
-
|
66
|
+
response_form.choices.any? do |choice_value|
|
67
67
|
choice_value.body&.match?(search) || choice_value.custom_body&.match?(search)
|
68
68
|
end
|
69
69
|
end
|