decidim-surveys 0.15.2 → 0.16.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +15 -9
  3. data/app/assets/stylesheets/decidim/surveys/surveys.scss +2 -2
  4. data/app/commands/decidim/surveys/create_survey.rb +1 -1
  5. data/app/controllers/decidim/surveys/admin/surveys_controller.rb +6 -37
  6. data/app/controllers/decidim/surveys/surveys_controller.rb +8 -22
  7. data/app/jobs/decidim/surveys/settings_change_job.rb +6 -3
  8. data/app/models/decidim/surveys/survey.rb +2 -12
  9. data/app/permissions/decidim/surveys/admin/permissions.rb +1 -1
  10. data/app/permissions/decidim/surveys/permissions.rb +1 -1
  11. data/app/queries/decidim/surveys/metrics/answers_metric_manage.rb +70 -0
  12. data/app/queries/decidim/surveys/metrics/survey_participants_metric_measure.rb +30 -0
  13. data/config/locales/ca.yml +5 -66
  14. data/config/locales/de.yml +5 -66
  15. data/config/locales/en.yml +5 -66
  16. data/config/locales/es-PY.yml +5 -66
  17. data/config/locales/es.yml +5 -66
  18. data/config/locales/eu.yml +5 -66
  19. data/config/locales/fi-pl.yml +5 -66
  20. data/config/locales/fi.yml +5 -66
  21. data/config/locales/fr.yml +5 -66
  22. data/config/locales/gl.yml +5 -66
  23. data/config/locales/hu.yml +5 -66
  24. data/config/locales/id-ID.yml +0 -70
  25. data/config/locales/it.yml +5 -66
  26. data/config/locales/nl.yml +5 -66
  27. data/config/locales/pl.yml +5 -66
  28. data/config/locales/pt-BR.yml +5 -66
  29. data/config/locales/pt.yml +5 -66
  30. data/config/locales/ru.yml +0 -66
  31. data/config/locales/sv.yml +5 -66
  32. data/config/locales/tr-TR.yml +0 -70
  33. data/config/locales/uk.yml +0 -66
  34. data/lib/decidim/surveys.rb +0 -2
  35. data/lib/decidim/surveys/admin_engine.rb +1 -5
  36. data/lib/decidim/surveys/component.rb +18 -15
  37. data/lib/decidim/surveys/engine.rb +17 -4
  38. data/lib/decidim/surveys/test/factories.rb +2 -42
  39. data/lib/decidim/surveys/version.rb +1 -1
  40. data/lib/tasks/decidim_surveys_tasks.rake +92 -0
  41. metadata +28 -42
  42. data/app/assets/config/admin/decidim_surveys_manifest.js +0 -1
  43. data/app/assets/config/decidim_surveys_manifest.js +0 -1
  44. data/app/assets/images/decidim/surveys/icon.svg +0 -19
  45. data/app/assets/javascripts/decidim/surveys/admin/auto_buttons_by_min_items.component.js.es6 +0 -25
  46. data/app/assets/javascripts/decidim/surveys/admin/auto_select_options_by_total_items.component.js.es6 +0 -23
  47. data/app/assets/javascripts/decidim/surveys/admin/surveys.js.es6 +0 -188
  48. data/app/assets/javascripts/decidim/surveys/autosortable_checkboxes.component.js.es6 +0 -65
  49. data/app/assets/javascripts/decidim/surveys/option_attached_inputs.component.js.es6 +0 -32
  50. data/app/assets/javascripts/decidim/surveys/surveys.js.es6 +0 -20
  51. data/app/commands/decidim/surveys/admin/update_survey.rb +0 -86
  52. data/app/commands/decidim/surveys/answer_survey.rb +0 -54
  53. data/app/forms/decidim/surveys/admin/survey_answer_option_form.rb +0 -23
  54. data/app/forms/decidim/surveys/admin/survey_form.rb +0 -27
  55. data/app/forms/decidim/surveys/admin/survey_question_form.rb +0 -35
  56. data/app/forms/decidim/surveys/survey_answer_choice_form.rb +0 -15
  57. data/app/forms/decidim/surveys/survey_answer_form.rb +0 -69
  58. data/app/forms/decidim/surveys/survey_form.rb +0 -22
  59. data/app/helpers/decidim/surveys/admin/application_helper.rb +0 -19
  60. data/app/models/decidim/surveys/survey_answer.rb +0 -45
  61. data/app/models/decidim/surveys/survey_answer_choice.rb +0 -15
  62. data/app/models/decidim/surveys/survey_answer_option.rb +0 -11
  63. data/app/models/decidim/surveys/survey_question.rb +0 -36
  64. data/app/queries/decidim/surveys/survey_user_answers.rb +0 -28
  65. data/app/views/decidim/surveys/admin/surveys/_answer_option.html.erb +0 -44
  66. data/app/views/decidim/surveys/admin/surveys/_form.html.erb +0 -51
  67. data/app/views/decidim/surveys/admin/surveys/_question.html.erb +0 -115
  68. data/app/views/decidim/surveys/admin/surveys/edit.html.erb +0 -7
  69. data/app/views/decidim/surveys/surveys/_answer.html.erb +0 -94
  70. data/app/views/decidim/surveys/surveys/show.html.erb +0 -84
  71. data/lib/decidim/surveys/data_portability_survey_user_answers_serializer.rb +0 -37
  72. data/lib/decidim/surveys/survey_user_answers_serializer.rb +0 -29
@@ -1 +0,0 @@
1
- // = link decidim/surveys/surveys.js
@@ -1,19 +0,0 @@
1
- <?xml version="1.0" encoding="utf-8"?>
2
- <!-- Generator: Adobe Illustrator 19.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
3
- <svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
4
- viewBox="0 0 36 36" style="enable-background:new 0 0 36 36;" xml:space="preserve">
5
- <path d="M27,28H9c-0.6,0-1-0.4-1-1V11c0-0.6,0.4-1,1-1h3.1c0.6,0,1,0.4,1,1v1.9h9.7V11c0-0.6,0.4-1,1-1H27c0.6,0,1,0.4,1,1v16
6
- C28,27.6,27.6,28,27,28z M10,26h16V12h-1.2v1.9c0,0.6-0.4,1-1,1H12.1c-0.6,0-1-0.4-1-1V12H10V26z"/>
7
- <g>
8
- <g>
9
- <path d="M21,11h-6c-0.6,0-1-0.4-1-1s0.4-1,1-1h6c0.6,0,1,0.4,1,1S21.6,11,21,11z"/>
10
- </g>
11
- <g>
12
- <path d="M18.7,11h-1.5c-0.6,0-1-0.4-1-1V8c0-0.6,0.4-1,1-1h1.5c0.6,0,1,0.4,1,1v2C19.7,10.5,19.3,11,18.7,11z"/>
13
- </g>
14
- </g>
15
- <g>
16
- <path d="M18,36.1c-9.9,0-18-8.1-18-18s8.1-18,18-18s18,8.1,18,18S27.9,36.1,18,36.1z M18,2.1c-8.8,0-16,7.2-16,16s7.2,16,16,16
17
- s16-7.2,16-16S26.8,2.1,18,2.1z"/>
18
- </g>
19
- </svg>
@@ -1,25 +0,0 @@
1
- ((exports) => {
2
- class AutoButtonsByMinItemsComponent {
3
- constructor(options = {}) {
4
- this.listSelector = options.listSelector;
5
- this.minItems = options.minItems;
6
- this.hideOnMinItemsOrLessSelector = options.hideOnMinItemsOrLessSelector;
7
-
8
- this.run();
9
- }
10
-
11
- run() {
12
- const $list = $(this.listSelector);
13
- const $items = $list.find(this.hideOnMinItemsOrLessSelector);
14
-
15
- if ($list.length <= this.minItems) {
16
- $items.hide();
17
- } else {
18
- $items.show();
19
- }
20
- }
21
- }
22
-
23
- exports.DecidimAdmin = exports.DecidimAdmin || {};
24
- exports.DecidimAdmin.AutoButtonsByMinItemsComponent = AutoButtonsByMinItemsComponent;
25
- })(window);
@@ -1,23 +0,0 @@
1
- ((exports) => {
2
- class AutoSelectOptionsByTotalItemsComponent {
3
- constructor(options = {}) {
4
- this.wrapperSelector = options.wrapperSelector;
5
- this.selectSelector = options.selectSelector;
6
- this.listSelector = options.listSelector;
7
- }
8
-
9
- run() {
10
- const $list = $(this.listSelector);
11
- const $selectField = $list.parents(this.wrapperSelector).find(this.selectSelector);
12
-
13
- $selectField.find("option").slice(1).remove();
14
-
15
- for (let idx = 2; idx <= $list.length; idx += 1) {
16
- $(`<option value="${idx}">${idx}</option>`).appendTo($selectField);
17
- }
18
- }
19
- }
20
-
21
- exports.DecidimAdmin = exports.DecidimAdmin || {};
22
- exports.DecidimAdmin.AutoSelectOptionsByTotalItemsComponent = AutoSelectOptionsByTotalItemsComponent;
23
- })(window);
@@ -1,188 +0,0 @@
1
- // = require ./auto_buttons_by_min_items.component
2
- // = require ./auto_select_options_by_total_items.component
3
-
4
- ((exports) => {
5
- const { AutoLabelByPositionComponent, AutoButtonsByPositionComponent, AutoButtonsByMinItemsComponent, AutoSelectOptionsByTotalItemsComponent, createFieldDependentInputs, createDynamicFields, createSortList } = exports.DecidimAdmin;
6
- const { createQuillEditor } = exports.Decidim;
7
-
8
- const wrapperSelector = ".survey-questions";
9
- const fieldSelector = ".survey-question";
10
- const questionTypeSelector = "select[name$=\\[question_type\\]]";
11
- const answerOptionFieldSelector = ".survey-question-answer-option";
12
- const answerOptionsWrapperSelector = ".survey-question-answer-options";
13
- const answerOptionRemoveFieldButtonSelector = ".remove-answer-option";
14
- const maxChoicesWrapperSelector = ".survey-question-max-choices";
15
-
16
- const autoLabelByPosition = new AutoLabelByPositionComponent({
17
- listSelector: ".survey-question:not(.hidden)",
18
- labelSelector: ".card-title span:first",
19
- onPositionComputed: (el, idx) => {
20
- $(el).find("input[name$=\\[position\\]]").val(idx);
21
- }
22
- });
23
-
24
- const autoButtonsByPosition = new AutoButtonsByPositionComponent({
25
- listSelector: ".survey-question:not(.hidden)",
26
- hideOnFirstSelector: ".move-up-question",
27
- hideOnLastSelector: ".move-down-question"
28
- });
29
-
30
- const createAutoMaxChoicesByNumberOfAnswerOptions = (fieldId) => {
31
- return new AutoSelectOptionsByTotalItemsComponent({
32
- wrapperSelector: fieldSelector,
33
- selectSelector: `${maxChoicesWrapperSelector} select`,
34
- listSelector: `#${fieldId} ${answerOptionsWrapperSelector} .survey-question-answer-option:not(.hidden)`
35
- })
36
- };
37
-
38
- const createAutoButtonsByMinItemsForAnswerOptions = (fieldId) => {
39
- return new AutoButtonsByMinItemsComponent({
40
- wrapperSelector: fieldSelector,
41
- listSelector: `#${fieldId} ${answerOptionsWrapperSelector} .survey-question-answer-option:not(.hidden)`,
42
- minItems: 2,
43
- hideOnMinItemsOrLessSelector: answerOptionRemoveFieldButtonSelector
44
- })
45
- };
46
-
47
- const createSortableList = () => {
48
- createSortList(".survey-questions-list:not(.published)", {
49
- handle: ".question-divider",
50
- placeholder: '<div style="border-style: dashed; border-color: #000"></div>',
51
- forcePlaceholderSize: true,
52
- onSortUpdate: () => { autoLabelByPosition.run() }
53
- });
54
- };
55
-
56
- const createDynamicFieldsForAnswerOptions = (fieldId) => {
57
- const autoButtons = createAutoButtonsByMinItemsForAnswerOptions(fieldId);
58
- const autoSelectOptions = createAutoMaxChoicesByNumberOfAnswerOptions(fieldId);
59
-
60
- return createDynamicFields({
61
- placeholderId: "survey-question-answer-option-id",
62
- wrapperSelector: `#${fieldId} ${answerOptionsWrapperSelector}`,
63
- containerSelector: ".survey-question-answer-options-list",
64
- fieldSelector: answerOptionFieldSelector,
65
- addFieldButtonSelector: ".add-answer-option",
66
- removeFieldButtonSelector: answerOptionRemoveFieldButtonSelector,
67
- onAddField: () => {
68
- autoButtons.run();
69
- autoSelectOptions.run();
70
- },
71
- onRemoveField: () => {
72
- autoButtons.run();
73
- autoSelectOptions.run();
74
- }
75
- });
76
- };
77
-
78
- const dynamicFieldsForAnswerOptions = {};
79
-
80
- const isMultipleChoiceOption = ($selectField) => {
81
- const value = $selectField.val();
82
-
83
- return value === "single_option" || value === "multiple_option" || value === "sorting"
84
- }
85
-
86
- const setupInitialQuestionAttributes = ($target) => {
87
- const fieldId = $target.attr("id");
88
- const $fieldQuestionTypeSelect = $target.find(questionTypeSelector);
89
-
90
- createFieldDependentInputs({
91
- controllerField: $fieldQuestionTypeSelect,
92
- wrapperSelector: fieldSelector,
93
- dependentFieldsSelector: answerOptionsWrapperSelector,
94
- dependentInputSelector: `${answerOptionFieldSelector} input`,
95
- enablingCondition: ($field) => {
96
- return isMultipleChoiceOption($field);
97
- }
98
- });
99
-
100
- createFieldDependentInputs({
101
- controllerField: $fieldQuestionTypeSelect,
102
- wrapperSelector: fieldSelector,
103
- dependentFieldsSelector: maxChoicesWrapperSelector,
104
- dependentInputSelector: "select",
105
- enablingCondition: ($field) => {
106
- return $field.val() === "multiple_option"
107
- }
108
- });
109
-
110
- dynamicFieldsForAnswerOptions[fieldId] = createDynamicFieldsForAnswerOptions(fieldId);
111
-
112
- const dynamicFields = dynamicFieldsForAnswerOptions[fieldId];
113
-
114
- const onQuestionTypeChange = () => {
115
- if (isMultipleChoiceOption($fieldQuestionTypeSelect)) {
116
- const nOptions = $fieldQuestionTypeSelect.parents(fieldSelector).find(answerOptionFieldSelector).length;
117
-
118
- if (nOptions === 0) {
119
- dynamicFields._addField();
120
- dynamicFields._addField();
121
- }
122
- }
123
- };
124
-
125
- $fieldQuestionTypeSelect.on("change", onQuestionTypeChange);
126
-
127
- onQuestionTypeChange();
128
- }
129
-
130
- const hideDeletedQuestion = ($target) => {
131
- const inputDeleted = $target.find("input[name$=\\[deleted\\]]").val();
132
-
133
- if (inputDeleted === "true") {
134
- $target.addClass("hidden");
135
- $target.hide();
136
- }
137
- }
138
-
139
- createDynamicFields({
140
- placeholderId: "survey-question-id",
141
- wrapperSelector: wrapperSelector,
142
- containerSelector: ".survey-questions-list",
143
- fieldSelector: fieldSelector,
144
- addFieldButtonSelector: ".add-question",
145
- removeFieldButtonSelector: ".remove-question",
146
- moveUpFieldButtonSelector: ".move-up-question",
147
- moveDownFieldButtonSelector: ".move-down-question",
148
- onAddField: ($field) => {
149
- setupInitialQuestionAttributes($field);
150
- createSortableList();
151
-
152
- $field.find(".editor-container").each((idx, el) => {
153
- createQuillEditor(el);
154
- });
155
-
156
- autoLabelByPosition.run();
157
- autoButtonsByPosition.run();
158
- },
159
- onRemoveField: ($field) => {
160
- autoLabelByPosition.run();
161
- autoButtonsByPosition.run();
162
-
163
- $field.find(answerOptionRemoveFieldButtonSelector).each((idx, el) => {
164
- dynamicFieldsForAnswerOptions[$field.attr("id")]._removeField(el);
165
- });
166
- },
167
- onMoveUpField: () => {
168
- autoLabelByPosition.run();
169
- autoButtonsByPosition.run();
170
- },
171
- onMoveDownField: () => {
172
- autoLabelByPosition.run();
173
- autoButtonsByPosition.run();
174
- }
175
- });
176
-
177
- createSortableList();
178
-
179
- $(fieldSelector).each((idx, el) => {
180
- const $target = $(el);
181
-
182
- hideDeletedQuestion($target);
183
- setupInitialQuestionAttributes($target);
184
- });
185
-
186
- autoLabelByPosition.run();
187
- autoButtonsByPosition.run();
188
- })(window);
@@ -1,65 +0,0 @@
1
- ((exports) => {
2
- class AutosortableCheckboxesComponent {
3
- constructor(options = {}) {
4
- this.wrapperField = options.wrapperField;
5
- this._bindEvent();
6
- this._run();
7
- }
8
-
9
- _run() {
10
- $(this.wrapperField).find("input[type=checkbox]").each((idx, el) => {
11
- const $parentLabel = $(el).parents("label");
12
-
13
- if ($(el).is(":checked")) {
14
- const $lastSorted = this.wrapperField.find("label.sorted").last();
15
-
16
- if ($lastSorted.length > 0) {
17
- $lastSorted.removeClass("last-sorted");
18
- $parentLabel.insertAfter($lastSorted);
19
- } else {
20
- $parentLabel.insertBefore(this.wrapperField.find("label:first-child"));
21
- }
22
-
23
- $parentLabel.addClass("sorted");
24
- $parentLabel.addClass("last-sorted");
25
- } else {
26
- const $lastUnsorted = this.wrapperField.find("label:not(.sorted)").last();
27
-
28
- if ($lastUnsorted.length > 0) {
29
- $parentLabel.insertBefore($lastUnsorted);
30
- } else {
31
- $parentLabel.insertAfter(this.wrapperField.find("label:last-child"));
32
- }
33
-
34
- $parentLabel.removeClass("sorted");
35
- }
36
- });
37
-
38
- $(this.wrapperField).find("label").each((idx, el) => {
39
- const $positionSelector = $(el).find(".position");
40
- const $positionField = $(el).find("input[name$=\\[position\\]]");
41
-
42
- if ($(el).hasClass("sorted")) {
43
- $positionField.val(idx);
44
- $positionField.prop("disabled", false);
45
- $positionSelector.html(`${idx + 1}. `);
46
- } else {
47
- $positionField.val("");
48
- $positionField.prop("disabled", true);
49
- $positionSelector.html("");
50
- }
51
- });
52
- }
53
-
54
- _bindEvent() {
55
- $(this.wrapperField).find("input[type=checkbox]").on("change", () => {
56
- this._run();
57
- });
58
- }
59
- }
60
-
61
- exports.Decidim = exports.Decidim || {};
62
- exports.Decidim.createAutosortableCheckboxes = (options) => {
63
- return new AutosortableCheckboxesComponent(options);
64
- };
65
- })(window);
@@ -1,32 +0,0 @@
1
- ((exports) => {
2
- class OptionAttachedInputsComponent {
3
- constructor(options = {}) {
4
- this.wrapperField = options.wrapperField;
5
- this.controllerFieldSelector = options.controllerFieldSelector;
6
- this.dependentInputSelector = options.dependentInputSelector;
7
- this.controllerSelector = this.wrapperField.find(this.controllerFieldSelector);
8
- this._bindEvent();
9
- this._run();
10
- }
11
-
12
- _run() {
13
- this.controllerSelector.each((idx, el) => {
14
- const $field = $(el);
15
- const enabled = $field.is(":checked");
16
-
17
- $field.parents("label").find(this.dependentInputSelector).prop("disabled", !enabled);
18
- });
19
- }
20
-
21
- _bindEvent() {
22
- this.controllerSelector.on("change", () => {
23
- this._run();
24
- });
25
- }
26
- }
27
-
28
- exports.Decidim = exports.Decidim || {};
29
- exports.Decidim.createOptionAttachedInputs = (options) => {
30
- return new OptionAttachedInputsComponent(options);
31
- };
32
- })(window);
@@ -1,20 +0,0 @@
1
- // = require ./option_attached_inputs.component
2
- // = require ./autosortable_checkboxes.component
3
-
4
- ((exports) => {
5
- const { createOptionAttachedInputs, createAutosortableCheckboxes } = exports.Decidim;
6
-
7
- $(".radio-button-collection, .check-box-collection").each((idx, el) => {
8
- createOptionAttachedInputs({
9
- wrapperField: $(el),
10
- controllerFieldSelector: "input[type=radio], input[type=checkbox]",
11
- dependentInputSelector: "input[type=text], input[type=hidden]"
12
- });
13
- });
14
-
15
- $(".sortable-check-box-collection").each((idx, el) => {
16
- createAutosortableCheckboxes({
17
- wrapperField: $(el)
18
- })
19
- });
20
- })(window);
@@ -1,86 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Decidim
4
- module Surveys
5
- module Admin
6
- # This command is executed when the user changes a Survey from the admin
7
- # panel.
8
- class UpdateSurvey < Rectify::Command
9
- # Initializes a UpdateSurvey Command.
10
- #
11
- # form - The form from which to get the data.
12
- # survey - The current instance of the survey to be updated.
13
- def initialize(form, survey)
14
- @form = form
15
- @survey = survey
16
- end
17
-
18
- # Updates the survey if valid.
19
- #
20
- # Broadcasts :ok if successful, :invalid otherwise.
21
- def call
22
- return broadcast(:invalid) if @form.invalid?
23
-
24
- Survey.transaction do
25
- update_survey_questions if @survey.questions_editable?
26
- update_survey
27
- end
28
-
29
- broadcast(:ok)
30
- end
31
-
32
- private
33
-
34
- def update_survey_questions
35
- @form.questions.each do |form_question|
36
- update_survey_question(form_question)
37
- end
38
- end
39
-
40
- def update_survey_question(form_question)
41
- question_attributes = {
42
- body: form_question.body,
43
- description: form_question.description,
44
- position: form_question.position,
45
- mandatory: form_question.mandatory,
46
- question_type: form_question.question_type,
47
- max_choices: form_question.max_choices
48
- }
49
-
50
- update_nested_model(form_question, question_attributes, @survey.questions) do |question|
51
- form_question.answer_options.each do |form_answer_option|
52
- answer_option_attributes = {
53
- body: form_answer_option.body,
54
- free_text: form_answer_option.free_text
55
- }
56
-
57
- update_nested_model(form_answer_option, answer_option_attributes, question.answer_options)
58
- end
59
- end
60
- end
61
-
62
- def update_nested_model(form, attributes, parent_association)
63
- record = parent_association.find_by(id: form.id) || parent_association.build(attributes)
64
-
65
- yield record if block_given?
66
-
67
- if record.persisted?
68
- if form.deleted?
69
- record.destroy!
70
- else
71
- record.update!(attributes)
72
- end
73
- else
74
- record.save!
75
- end
76
- end
77
-
78
- def update_survey
79
- @survey.update!(title: @form.title,
80
- description: @form.description,
81
- tos: @form.tos)
82
- end
83
- end
84
- end
85
- end
86
- end