decidim-forms 0.22.0 → 0.23.0

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.
Files changed (113) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/config/admin/decidim_forms_manifest.css +3 -0
  3. data/app/assets/config/decidim_forms_manifest.css +1 -0
  4. data/app/assets/javascripts/decidim/forms/admin/auto_select_options_from_url.component.js.es6 +40 -0
  5. data/app/assets/javascripts/decidim/forms/admin/forms.js.es6 +181 -25
  6. data/app/assets/javascripts/decidim/forms/autosortable_checkboxes.component.js.es6 +54 -34
  7. data/app/assets/javascripts/decidim/forms/display_conditions.component.js.es6 +204 -0
  8. data/app/assets/javascripts/decidim/forms/forms.js.es6 +8 -1
  9. data/app/assets/stylesheets/decidim/forms/questionnaire-answers-pdf.scss +69 -0
  10. data/app/commands/decidim/forms/admin/update_questionnaire.rb +25 -1
  11. data/app/commands/decidim/forms/answer_questionnaire.rb +1 -1
  12. data/app/controllers/decidim/forms/admin/concerns/has_questionnaire.rb +47 -1
  13. data/app/controllers/decidim/forms/admin/concerns/has_questionnaire_answers.rb +97 -0
  14. data/app/forms/decidim/forms/admin/display_condition_form.rb +100 -0
  15. data/app/forms/decidim/forms/admin/question_form.rb +1 -0
  16. data/app/forms/decidim/forms/answer_form.rb +15 -0
  17. data/app/helpers/decidim/forms/admin/application_helper.rb +21 -0
  18. data/app/helpers/decidim/forms/admin/concerns/has_questionnaire_answers_pagination_helper.rb +49 -0
  19. data/app/helpers/decidim/forms/admin/concerns/has_questionnaire_answers_url_helper.rb +40 -0
  20. data/app/helpers/decidim/forms/admin/questionnaire_answers_helper.rb +27 -0
  21. data/app/jobs/decidim/forms/export_questionnaire_answers_job.rb +19 -0
  22. data/app/models/decidim/forms/answer.rb +0 -3
  23. data/app/models/decidim/forms/answer_option.rb +14 -0
  24. data/app/models/decidim/forms/display_condition.rb +51 -0
  25. data/app/models/decidim/forms/question.rb +34 -0
  26. data/app/models/decidim/forms/question_matrix_row.rb +3 -0
  27. data/app/models/decidim/forms/questionnaire.rb +11 -1
  28. data/app/presenters/decidim/forms/admin/questionnaire_answer_presenter.rb +43 -0
  29. data/app/presenters/decidim/forms/admin/questionnaire_participant_presenter.rb +60 -0
  30. data/app/presenters/decidim/forms/answer_option_presenter.rb +20 -0
  31. data/app/presenters/decidim/forms/question_presenter.rb +16 -0
  32. data/app/queries/decidim/forms/questionnaire_participant.rb +35 -0
  33. data/app/queries/decidim/forms/questionnaire_participants.rb +43 -0
  34. data/app/views/decidim/forms/admin/questionnaires/_display_condition.html.erb +88 -0
  35. data/app/views/decidim/forms/admin/questionnaires/_display_condition_template.html.erb +7 -0
  36. data/app/views/decidim/forms/admin/questionnaires/_form.html.erb +30 -4
  37. data/app/views/decidim/forms/admin/questionnaires/_question.html.erb +19 -3
  38. data/app/views/decidim/forms/admin/questionnaires/answers/export/_answer.html.erb +31 -0
  39. data/app/views/decidim/forms/admin/questionnaires/answers/export/pdf.html.erb +13 -0
  40. data/app/views/decidim/forms/admin/questionnaires/answers/index.html.erb +53 -0
  41. data/app/views/decidim/forms/admin/questionnaires/answers/show.html.erb +48 -0
  42. data/app/views/decidim/forms/admin/questionnaires/edit.html.erb +9 -5
  43. data/app/views/decidim/forms/questionnaires/_answer.html.erb +1 -1
  44. data/app/views/decidim/forms/questionnaires/show.html.erb +22 -2
  45. data/app/views/layouts/decidim/forms/admin/questionnaires/questionnaire_answers.html.erb +12 -0
  46. data/config/initializers/wicked_pdf.rb +25 -0
  47. data/config/locales/am-ET.yml +1 -0
  48. data/config/locales/ar.yml +0 -1
  49. data/config/locales/bg-BG.yml +0 -2
  50. data/config/locales/bg.yml +14 -0
  51. data/config/locales/ca.yml +56 -4
  52. data/config/locales/cs.yml +54 -2
  53. data/config/locales/da.yml +1 -0
  54. data/config/locales/de.yml +51 -1
  55. data/config/locales/el.yml +0 -2
  56. data/config/locales/en.yml +54 -2
  57. data/config/locales/eo.yml +1 -0
  58. data/config/locales/es-MX.yml +53 -1
  59. data/config/locales/es-PY.yml +53 -1
  60. data/config/locales/es.yml +54 -2
  61. data/config/locales/et.yml +1 -0
  62. data/config/locales/eu.yml +0 -1
  63. data/config/locales/fi-plain.yml +53 -1
  64. data/config/locales/fi.yml +53 -1
  65. data/config/locales/fr-CA.yml +54 -2
  66. data/config/locales/fr.yml +54 -2
  67. data/config/locales/gl.yml +0 -1
  68. data/config/locales/hr.yml +1 -0
  69. data/config/locales/hu.yml +0 -2
  70. data/config/locales/id-ID.yml +0 -1
  71. data/config/locales/is.yml +1 -0
  72. data/config/locales/it.yml +53 -1
  73. data/config/locales/ja-JP.yml +51 -1
  74. data/config/locales/ja.yml +170 -0
  75. data/config/locales/ko-KR.yml +1 -0
  76. data/config/locales/ko.yml +1 -0
  77. data/config/locales/lt.yml +1 -0
  78. data/config/locales/{lv-LV.yml → lv.yml} +0 -1
  79. data/config/locales/mt.yml +1 -0
  80. data/config/locales/nl.yml +59 -7
  81. data/config/locales/no.yml +47 -2
  82. data/config/locales/om-ET.yml +1 -0
  83. data/config/locales/pl.yml +52 -3
  84. data/config/locales/pt-BR.yml +0 -1
  85. data/config/locales/pt.yml +51 -2
  86. data/config/locales/ro-RO.yml +51 -2
  87. data/config/locales/ru.yml +0 -1
  88. data/config/locales/sk.yml +0 -2
  89. data/config/locales/sl.yml +7 -0
  90. data/config/locales/so-SO.yml +1 -0
  91. data/config/locales/sv.yml +51 -2
  92. data/config/locales/ti-ER.yml +1 -0
  93. data/config/locales/tr-TR.yml +0 -1
  94. data/config/locales/vi-VN.yml +1 -0
  95. data/config/locales/vi.yml +1 -0
  96. data/config/locales/zh-CN.yml +172 -0
  97. data/config/locales/zh-TW.yml +1 -0
  98. data/db/migrate/20200130194123_create_decidim_forms_display_conditions.rb +20 -0
  99. data/lib/decidim/exporters/form_pdf.rb +33 -0
  100. data/lib/decidim/exporters/form_pdf_controller_helper.rb +11 -0
  101. data/lib/decidim/forms.rb +5 -0
  102. data/lib/decidim/forms/admin_engine.rb +1 -1
  103. data/lib/decidim/forms/test.rb +6 -0
  104. data/lib/decidim/forms/test/factories.rb +30 -0
  105. data/lib/decidim/forms/test/shared_examples/has_questionnaire.rb +619 -38
  106. data/lib/decidim/forms/test/shared_examples/manage_questionnaire_answers.rb +108 -0
  107. data/lib/decidim/forms/test/shared_examples/manage_questionnaires.rb +21 -894
  108. data/lib/decidim/forms/test/shared_examples/manage_questionnaires/add_display_conditions.rb +179 -0
  109. data/lib/decidim/forms/test/shared_examples/manage_questionnaires/add_questions.rb +463 -0
  110. data/lib/decidim/forms/test/shared_examples/manage_questionnaires/update_display_conditions.rb +93 -0
  111. data/lib/decidim/forms/test/shared_examples/manage_questionnaires/update_questions.rb +461 -0
  112. data/lib/decidim/forms/version.rb +1 -1
  113. metadata +91 -11
@@ -0,0 +1,204 @@
1
+ /* eslint-disable no-ternary, no-plusplus */
2
+
3
+ ((exports) => {
4
+ class DisplayCondition {
5
+ constructor(options = {}) {
6
+ this.wrapperField = options.wrapperField;
7
+ this.type = options.type;
8
+ this.conditionQuestion = options.conditionQuestion;
9
+ this.answerOption = options.answerOption;
10
+ this.mandatory = options.mandatory;
11
+ this.value = options.value;
12
+ this.onFulfilled = options.onFulfilled;
13
+ this.bindEvent();
14
+ }
15
+
16
+ bindEvent() {
17
+ this.checkCondition();
18
+ this.getInputsToListen().on("change", this.checkCondition.bind(this));
19
+ }
20
+
21
+ getInputValue() {
22
+ const $conditionWrapperField = $(`.question[data-question-id='${this.conditionQuestion}']`);
23
+ const $textInput = $conditionWrapperField.find("textarea, input[type='text']:not([name$=\\[custom_body\\]])");
24
+
25
+ if ($textInput.length) {
26
+ return $textInput.val();
27
+ }
28
+
29
+ let multipleInput = [];
30
+
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]");
34
+
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();
40
+
41
+ multipleInput.push({ id, value, text });
42
+ }
43
+ });
44
+
45
+ return multipleInput;
46
+ }
47
+
48
+ getInputsToListen() {
49
+ const $conditionWrapperField = $(`.question[data-question-id='${this.conditionQuestion}']`);
50
+ const $textInput = $conditionWrapperField.find("textarea, input[type='text']:not([name$=\\[custom_body\\]])");
51
+
52
+ if ($textInput.length) {
53
+ return $textInput;
54
+ }
55
+
56
+ return $conditionWrapperField.find(".collection-input").find("input:not([type='hidden'])");
57
+ }
58
+
59
+ checkAnsweredCondition(value) {
60
+ if (typeof (value) !== "object") {
61
+ return Boolean(value);
62
+ }
63
+
64
+ return Boolean(value.some((it) => it.value));
65
+ }
66
+
67
+ checkNotAnsweredCondition(value) {
68
+ return !this.checkAnsweredCondition(value);
69
+ }
70
+
71
+ checkEqualCondition(value) {
72
+ if (value.length) {
73
+ return value.some((it) => it.id === this.answerOption.toString());
74
+ }
75
+ return false;
76
+ }
77
+
78
+ checkNotEqualCondition(value) {
79
+ if (value.length) {
80
+ return value.every((it) => it.id !== this.answerOption.toString());
81
+ }
82
+ return false;
83
+ }
84
+
85
+ checkMatchCondition(value) {
86
+ let regexp = new RegExp(this.value, "i");
87
+
88
+ if (typeof (value) !== "object") {
89
+ return Boolean(value.match(regexp));
90
+ }
91
+
92
+ return value.some(function (it) {
93
+ return it.text
94
+ ? it.text.match(regexp)
95
+ : it.value.match(regexp)
96
+ });
97
+ }
98
+
99
+ checkCondition() {
100
+ const value = this.getInputValue();
101
+ let fulfilled = false;
102
+
103
+ switch (this.type) {
104
+ case "answered":
105
+ fulfilled = this.checkAnsweredCondition(value);
106
+ break;
107
+ case "not_answered":
108
+ fulfilled = this.checkNotAnsweredCondition(value);
109
+ break;
110
+ case "equal":
111
+ fulfilled = this.checkEqualCondition(value);
112
+ break;
113
+ case "not_equal":
114
+ fulfilled = this.checkNotEqualCondition(value);
115
+ break;
116
+ case "match":
117
+ fulfilled = this.checkMatchCondition(value);
118
+ break;
119
+ default:
120
+ fulfilled = false;
121
+ break;
122
+ }
123
+
124
+ this.onFulfilled(fulfilled);
125
+ }
126
+ }
127
+
128
+ class DisplayConditionsComponent {
129
+ constructor(options = {}) {
130
+ this.wrapperField = options.wrapperField;
131
+ this.conditions = {};
132
+ this.showCount = 0;
133
+ this.initializeConditions();
134
+ }
135
+
136
+ initializeConditions() {
137
+ const $conditionElements = this.wrapperField.find(".display-condition");
138
+
139
+ $conditionElements.each((idx, el) => {
140
+ const $condition = $(el);
141
+ const id = $condition.data("id");
142
+ this.conditions[id] = {};
143
+
144
+ this.conditions[id] = new DisplayCondition({
145
+ wrapperField: this.wrapperField,
146
+ type: $condition.data("type"),
147
+ conditionQuestion: $condition.data("condition"),
148
+ answerOption: $condition.data("option"),
149
+ mandatory: $condition.data("mandatory"),
150
+ value: $condition.data("value"),
151
+ onFulfilled: (fulfilled) => {
152
+ this.onFulfilled(id, fulfilled);
153
+ }
154
+ });
155
+ });
156
+ }
157
+
158
+ mustShow() {
159
+ const conditions = Object.values(this.conditions);
160
+ const mandatoryConditions = conditions.filter((condition) => condition.mandatory);
161
+ const nonMandatoryConditions = conditions.filter((condition) => !condition.mandatory);
162
+
163
+ if (mandatoryConditions.length) {
164
+ return mandatoryConditions.every((condition) => condition.fulfilled);
165
+ }
166
+
167
+ return nonMandatoryConditions.some((condition) => condition.fulfilled);
168
+
169
+ }
170
+
171
+ onFulfilled(id, fulfilled) {
172
+ this.conditions[id].fulfilled = fulfilled;
173
+
174
+ if (this.mustShow()) {
175
+ this.showQuestion();
176
+ }
177
+ else {
178
+ this.hideQuestion();
179
+ }
180
+ }
181
+
182
+ showQuestion() {
183
+ this.wrapperField.fadeIn();
184
+ this.wrapperField.find("input, textarea").prop("disabled", null);
185
+ this.showCount++;
186
+ }
187
+
188
+ hideQuestion() {
189
+ if (this.showCount) {
190
+ this.wrapperField.fadeOut();
191
+ }
192
+ else {
193
+ this.wrapperField.hide();
194
+ }
195
+
196
+ this.wrapperField.find("input, textarea").prop("disabled", "disabled");
197
+ }
198
+ }
199
+
200
+ exports.Decidim = exports.Decidim || {};
201
+ exports.Decidim.createDisplayConditions = (options) => {
202
+ return new DisplayConditionsComponent(options);
203
+ };
204
+ })(window);
@@ -1,9 +1,10 @@
1
1
  // = require ./option_attached_inputs.component
2
2
  // = require ./autosortable_checkboxes.component
3
+ // = require ./display_conditions.component
3
4
  // = require ./max_choices_alert.component
4
5
 
5
6
  ((exports) => {
6
- const { createOptionAttachedInputs, createAutosortableCheckboxes, createMaxChoicesAlertComponent } = exports.Decidim;
7
+ const { createOptionAttachedInputs, createAutosortableCheckboxes, createMaxChoicesAlertComponent, createDisplayConditions } = exports.Decidim;
7
8
 
8
9
  $(".radio-button-collection, .check-box-collection").each((idx, el) => {
9
10
  createOptionAttachedInputs({
@@ -32,6 +33,12 @@
32
33
  })
33
34
  });
34
35
 
36
+ $(".answer-questionnaire .question[data-conditioned='true']").each((idx, el) => {
37
+ createDisplayConditions({
38
+ wrapperField: $(el)
39
+ });
40
+ });
41
+
35
42
  const $form = $("form.answer-questionnaire");
36
43
  if ($form.length > 0) {
37
44
  $form.find("input, textarea, select").on("change", () => {
@@ -0,0 +1,69 @@
1
+ .questionnaire-answers{
2
+ .header{
3
+ h1{
4
+ margin: 0;
5
+ padding: 25px;
6
+ background: rgb(59, 69, 87);
7
+ color: white;
8
+ border-radius: 4px 4px 0 0;
9
+ }
10
+
11
+ .description{
12
+ margin: 0;
13
+ padding: 25px;
14
+ background: #f6f6f6;
15
+ color: #202751;
16
+ border-radius: 0 0 4px 4px;
17
+ }
18
+ }
19
+
20
+ .answer{
21
+ margin-top: 25px;
22
+ background: #f6f6f6;
23
+ border-radius: 0 0 4px 4px;
24
+
25
+ .title{
26
+ page-break-inside: avoid;
27
+ border-radius: 4px 4px 0 0;
28
+ padding: 10px 25px;
29
+ background: rgb(59, 69, 87);
30
+ color: white;
31
+ }
32
+
33
+ .participant-info{
34
+ margin-top: 0;
35
+ padding: 10px 25px;
36
+ width: 100%;
37
+ text-align: center;
38
+ background: #eee;
39
+ color: #202751;
40
+ page-break-inside: avoid;
41
+
42
+ th:first-child{
43
+ text-align: left;
44
+ }
45
+
46
+ td:first-child{
47
+ text-align: left;
48
+ }
49
+
50
+ th:last-child{
51
+ text-align: right;
52
+ }
53
+
54
+ td:last-child{
55
+ text-align: right;
56
+ }
57
+ }
58
+
59
+ .answers{
60
+ padding: 25px;
61
+
62
+ .question{
63
+ page-break-inside: avoid;
64
+ font-size: inherit;
65
+ color: #202751;
66
+ }
67
+ }
68
+ }
69
+ }
@@ -22,7 +22,11 @@ module Decidim
22
22
  return broadcast(:invalid) if @form.invalid?
23
23
 
24
24
  Decidim::Forms::Questionnaire.transaction do
25
- update_questionnaire_questions if @questionnaire.questions_editable?
25
+ if @questionnaire.questions_editable?
26
+ update_questionnaire_questions
27
+ delete_answers unless @questionnaire.published?
28
+ end
29
+
26
30
  update_questionnaire
27
31
  end
28
32
 
@@ -57,6 +61,22 @@ module Decidim
57
61
  update_nested_model(form_answer_option, answer_option_attributes, question.answer_options)
58
62
  end
59
63
 
64
+ form_question.display_conditions.each do |form_display_condition|
65
+ type = form_display_condition.condition_type
66
+
67
+ display_condition_attributes = {
68
+ condition_question: form_display_condition.condition_question,
69
+ condition_type: form_display_condition.condition_type,
70
+ condition_value: type == "match" ? form_display_condition.condition_value : nil,
71
+ answer_option: %w(equal not_equal).include?(type) ? form_display_condition.answer_option : nil,
72
+ mandatory: form_display_condition.mandatory
73
+ }
74
+
75
+ next if form_display_condition.deleted? && form_display_condition.id.blank?
76
+
77
+ update_nested_model(form_display_condition, display_condition_attributes, question.display_conditions)
78
+ end
79
+
60
80
  form_question.matrix_rows.each do |form_matrix_row|
61
81
  matrix_row_attributes = {
62
82
  body: form_matrix_row.body
@@ -88,6 +108,10 @@ module Decidim
88
108
  description: @form.description,
89
109
  tos: @form.tos)
90
110
  end
111
+
112
+ def delete_answers
113
+ @questionnaire.answers.destroy_all
114
+ end
91
115
  end
92
116
  end
93
117
  end
@@ -30,7 +30,7 @@ module Decidim
30
30
 
31
31
  def answer_questionnaire
32
32
  Answer.transaction do
33
- form.responses_by_step.flatten.each do |form_answer|
33
+ form.responses_by_step.flatten.select(&:display_conditions_fulfilled?).each do |form_answer|
34
34
  answer = Answer.new(
35
35
  user: @current_user,
36
36
  questionnaire: @questionnaire,
@@ -15,7 +15,21 @@ module Decidim
15
15
 
16
16
  included do
17
17
  helper Decidim::Forms::Admin::ApplicationHelper
18
- helper_method :questionnaire_for, :questionnaire, :blank_question, :blank_answer_option, :blank_matrix_row, :question_types, :update_url
18
+ 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
20
+
21
+ if defined? Decidim::Templates::Admin
22
+ include Decidim::Templates::Admin::Concerns::Templatable
23
+ helper Decidim::Templates::Admin::TemplatesHelper
24
+
25
+ def templatable_type
26
+ "Decidim::Forms::Questionnaire"
27
+ end
28
+
29
+ def templatable
30
+ questionnaire
31
+ end
32
+ end
19
33
 
20
34
  def edit
21
35
  enforce_permission_to :update, :questionnaire, questionnaire: questionnaire
@@ -46,6 +60,16 @@ module Decidim
46
60
  end
47
61
  end
48
62
 
63
+ def answer_options
64
+ respond_to do |format|
65
+ format.json do
66
+ question_id = params["id"]
67
+ question = Question.find_by(id: question_id)
68
+ render json: question.answer_options.map { |answer_option| AnswerOptionPresenter.new(answer_option).as_json } if question.present?
69
+ end
70
+ end
71
+ end
72
+
49
73
  # Public: The only method to be implemented at the controller. You need to
50
74
  # return the object that will hold the questionnaire.
51
75
  def questionnaire_for
@@ -64,6 +88,18 @@ module Decidim
64
88
  url_for(questionnaire.questionnaire_for)
65
89
  end
66
90
 
91
+ # Implement this method in your controller to set the URL
92
+ # where the questionnaire can be answered.
93
+ def public_url
94
+ raise "#{self.class.name} is expected to implement #public_url"
95
+ end
96
+
97
+ # Returns the url to get the answer options json (for the display conditions form)
98
+ # for the question with id = params[:id]
99
+ def answer_options_url(params)
100
+ url_for([questionnaire.questionnaire_for, action: :answer_options, format: :json, **params])
101
+ end
102
+
67
103
  private
68
104
 
69
105
  def i18n_flashes_scope
@@ -82,6 +118,10 @@ module Decidim
82
118
  @blank_answer_option ||= Admin::AnswerOptionForm.new
83
119
  end
84
120
 
121
+ def blank_display_condition
122
+ @blank_display_condition ||= Admin::DisplayConditionForm.new
123
+ end
124
+
85
125
  def blank_matrix_row
86
126
  @blank_matrix_row ||= Admin::QuestionMatrixRowForm.new
87
127
  end
@@ -91,6 +131,12 @@ module Decidim
91
131
  [question_type, I18n.t("decidim.forms.question_types.#{question_type}")]
92
132
  end
93
133
  end
134
+
135
+ def display_condition_types
136
+ @display_condition_types ||= DisplayCondition.condition_types.keys.map do |condition_type|
137
+ [condition_type, I18n.t("decidim.forms.admin.questionnaires.display_condition.condition_types.#{condition_type}")]
138
+ end
139
+ end
94
140
  end
95
141
  end
96
142
  end
@@ -0,0 +1,97 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Forms
5
+ module Admin
6
+ module Concerns
7
+ # Questionnaires can be related to any class in Decidim. In order to
8
+ # manage the questionnaires answers for a given type, you should create a new
9
+ # controller and include the HasQuestionnaire concern as well as this one.
10
+ #
11
+ # In the controller that includes this concern, you should define a
12
+ # `questionnaire_for` method that returns an instance of the model that the
13
+ # questionnaire belongs to. You should also define the routes for:
14
+ # `index_<model>_url` and `export_<model>_url` as well as
15
+ # `show_<model>_url` and `export_response_<model>_url` (which are passed
16
+ # a `:session_token` parameter)
17
+ module HasQuestionnaireAnswers
18
+ extend ActiveSupport::Concern
19
+
20
+ included do
21
+ include Decidim::Paginable
22
+ include Decidim::Forms::Admin::Concerns::HasQuestionnaireAnswersUrlHelper
23
+ include Decidim::Forms::Admin::Concerns::HasQuestionnaireAnswersPaginationHelper
24
+
25
+ helper Decidim::Forms::Admin::QuestionnaireAnswersHelper
26
+
27
+ def index
28
+ enforce_permission_to :index, :questionnaire_answers
29
+
30
+ @query = paginate(collection)
31
+ @participants = participants(@query)
32
+ @total = participants_query.count_participants
33
+
34
+ render template: "decidim/forms/admin/questionnaires/answers/index"
35
+ end
36
+
37
+ def show
38
+ enforce_permission_to :show, :questionnaire_answers
39
+
40
+ @participant = participant(participants_query.participant(params[:session_token]))
41
+
42
+ render template: "decidim/forms/admin/questionnaires/answers/show"
43
+ end
44
+
45
+ def export_response
46
+ enforce_permission_to :export_response, :questionnaire_answers
47
+
48
+ session_token = params[:session_token]
49
+ answers = QuestionnaireUserAnswers.for(questionnaire)
50
+
51
+ # i18n-tasks-use t("decidim.forms.admin.questionnaires.answers.export_response.title")
52
+ title = t("export_response.title", scope: i18n_scope, token: session_token)
53
+
54
+ Decidim::Forms::ExportQuestionnaireAnswersJob.perform_later(current_user, title, answers.select { |a| a.first.session_token == session_token })
55
+
56
+ flash[:notice] = t("decidim.admin.exports.notice")
57
+
58
+ redirect_back(fallback_location: questionnaire_participant_answers_url(session_token))
59
+ end
60
+
61
+ # Public: The only method to be implemented at the controller. You need to
62
+ # return the object that will hold the questionnaire.
63
+ def questionnaire_for
64
+ raise "#{self.class.name} is expected to implement #questionnaire_for"
65
+ end
66
+
67
+ private
68
+
69
+ def i18n_scope
70
+ "decidim.forms.admin.questionnaires.answers"
71
+ end
72
+
73
+ def questionnaire
74
+ @questionnaire ||= Questionnaire.find_by(questionnaire_for: questionnaire_for)
75
+ end
76
+
77
+ def participants_query
78
+ QuestionnaireParticipants.new(questionnaire)
79
+ end
80
+
81
+ def collection
82
+ @collection ||= participants_query.participants
83
+ end
84
+
85
+ def participant(answer)
86
+ Decidim::Forms::Admin::QuestionnaireParticipantPresenter.new(participant: answer)
87
+ end
88
+
89
+ def participants(query)
90
+ query.map { |answer| participant(answer) }
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end