decidim-surveys 0.19.1 → 0.23.0

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/stylesheets/decidim/surveys/surveys.scss +20 -3
  3. data/app/controllers/decidim/surveys/admin/surveys_controller.rb +11 -0
  4. data/app/controllers/decidim/surveys/surveys_controller.rb +12 -1
  5. data/app/jobs/decidim/surveys/clean_survey_answers_job.rb +32 -0
  6. data/app/jobs/decidim/surveys/settings_change_job.rb +4 -0
  7. data/app/models/decidim/surveys/survey.rb +4 -0
  8. data/app/permissions/decidim/surveys/admin/permissions.rb +12 -6
  9. data/app/permissions/decidim/surveys/permissions.rb +1 -1
  10. data/app/queries/decidim/surveys/metrics/answers_metric_manage.rb +2 -8
  11. data/app/serializers/decidim/surveys/data_importer.rb +66 -0
  12. data/app/serializers/decidim/surveys/data_serializer.rb +46 -0
  13. data/app/types/decidim/surveys/survey_type.rb +15 -0
  14. data/app/types/decidim/surveys/surveys_type.rb +32 -0
  15. data/config/locales/am-ET.yml +1 -0
  16. data/config/locales/ar.yml +1 -0
  17. data/config/locales/bg-BG.yml +10 -0
  18. data/config/locales/bg.yml +10 -0
  19. data/config/locales/ca.yml +6 -0
  20. data/config/locales/cs.yml +6 -0
  21. data/config/locales/da-DK.yml +1 -0
  22. data/config/locales/da.yml +1 -0
  23. data/config/locales/de.yml +6 -0
  24. data/config/locales/el-GR.yml +1 -0
  25. data/config/locales/el.yml +59 -0
  26. data/config/locales/en.yml +6 -0
  27. data/config/locales/eo.yml +1 -0
  28. data/config/locales/es-MX.yml +6 -0
  29. data/config/locales/es-PY.yml +6 -0
  30. data/config/locales/es.yml +6 -0
  31. data/config/locales/et-EE.yml +1 -0
  32. data/config/locales/et.yml +1 -0
  33. data/config/locales/fi-plain.yml +6 -0
  34. data/config/locales/fi.yml +7 -1
  35. data/config/locales/fr-CA.yml +62 -0
  36. data/config/locales/fr.yml +6 -0
  37. data/config/locales/ga-IE.yml +1 -0
  38. data/config/locales/hr-HR.yml +1 -0
  39. data/config/locales/hr.yml +1 -0
  40. data/config/locales/hu.yml +3 -0
  41. data/config/locales/is-IS.yml +25 -0
  42. data/config/locales/is.yml +25 -0
  43. data/config/locales/it.yml +11 -7
  44. data/config/locales/ja-JP.yml +58 -0
  45. data/config/locales/ja.yml +60 -0
  46. data/config/locales/ko-KR.yml +1 -0
  47. data/config/locales/ko.yml +1 -0
  48. data/config/locales/lt-LT.yml +1 -0
  49. data/config/locales/lt.yml +1 -0
  50. data/config/locales/lv.yml +61 -0
  51. data/config/locales/mt-MT.yml +1 -0
  52. data/config/locales/mt.yml +1 -0
  53. data/config/locales/nl.yml +10 -4
  54. data/config/locales/no.yml +25 -0
  55. data/config/locales/om-ET.yml +1 -0
  56. data/config/locales/pl.yml +18 -12
  57. data/config/locales/pt-BR.yml +1 -1
  58. data/config/locales/pt.yml +27 -23
  59. data/config/locales/ro-RO.yml +62 -0
  60. data/config/locales/sk-SK.yml +63 -0
  61. data/config/locales/sk.yml +63 -0
  62. data/config/locales/sl.yml +8 -0
  63. data/config/locales/so-SO.yml +1 -0
  64. data/config/locales/sr-CS.yml +1 -0
  65. data/config/locales/sv.yml +5 -1
  66. data/config/locales/ti-ER.yml +1 -0
  67. data/config/locales/vi-VN.yml +1 -0
  68. data/config/locales/vi.yml +1 -0
  69. data/config/locales/zh-CN.yml +60 -0
  70. data/config/locales/zh-TW.yml +1 -0
  71. data/db/migrate/20200609090533_check_legacy_tables.rb +114 -0
  72. data/db/migrate/20200610090533_remove_survey_answer_choices.rb +7 -0
  73. data/db/migrate/20200610090650_remove_survey_answer_options.rb +7 -0
  74. data/db/migrate/20200610090725_remove_survey_answers.rb +7 -0
  75. data/db/migrate/20200610090845_remove_survey_questions.rb +7 -0
  76. data/db/migrate/20200610105927_remove_survey_columns.rb +10 -0
  77. data/lib/decidim/surveys/admin_engine.rb +10 -0
  78. data/lib/decidim/surveys/component.rb +38 -6
  79. data/lib/decidim/surveys/test/factories.rb +1 -1
  80. data/lib/decidim/surveys/version.rb +1 -1
  81. metadata +75 -13
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c5bf3b75387df3baa5f23a051b8c6c3141e4de0b50a318b598ddf921bc16c68e
4
- data.tar.gz: a49fcdd21d45cbcd42c0a8b87a97fd44e6c4ab0f5fe862ff68520fc569fd6715
3
+ metadata.gz: 438c473b61d9bc07cad2c8e3ce3c6c56f900ae0a4aaade52db7dffbcb1e4a433
4
+ data.tar.gz: 182793a9e4e0fee42249e40ec2862f4a756344656f5010c055167c0477240559
5
5
  SHA512:
6
- metadata.gz: 58e2ec2f61f751056ec1f0f4337a3dfe10bef9916f9c8f13c309fde6b95b3b15e3bdf9db116138ce651bb67ccda829af2bc43ea27e4d585500d706b415590396
7
- data.tar.gz: 1734f209975336d6af102216a05554eb857154b2825974a67498f50c7e0373d82e5011c7b6cfcd93507fb3ecf4204ae55ff273d0d9b3ab3ff2096b009f8473af
6
+ metadata.gz: fd718b0903ddd737e42ea62e71039e4fdc34bf7c5cc13444f8b1f4353a5d197dd0b95cbd20434d1c52cbefe6c74d82ed64fb590732fddbe6f1f1a17ea5df685d
7
+ data.tar.gz: 92a9f208813e602e656b27f7549da9aafbfb88da073f2d2280d5eddde519fd0756a95223f5860cdbb4f3c207406a0420e4e0dbac8a19f6b5ae32129485dab7c0
@@ -1,6 +1,10 @@
1
1
  @import "decidim/forms/forms";
2
2
 
3
- form.answer-questionnaire{
3
+ .answer-questionnaire{
4
+ label{
5
+ font-size: inherit;
6
+ }
7
+
4
8
  .radio-button-collection,
5
9
  .check-box-collection,
6
10
  .sortable-check-box-collection{
@@ -15,8 +19,10 @@ form.answer-questionnaire{
15
19
  }
16
20
  }
17
21
 
18
- label.questionnaire-question{
19
- font-weight: bold;
22
+ .questionnaire-question{
23
+ font-weight: 600;
24
+ line-height: 1.4;
25
+ margin-bottom: .3em;
20
26
  }
21
27
 
22
28
  .help-text{
@@ -30,4 +36,15 @@ form.answer-questionnaire{
30
36
  .last-sorted{
31
37
  margin-bottom: 1rem;
32
38
  }
39
+
40
+ &__steps{
41
+ font-size: .8rem;
42
+ }
43
+
44
+ &__submit{
45
+ display: flex;
46
+ align-items: baseline;
47
+ justify-content: space-between;
48
+ margin-top: 2rem;
49
+ }
33
50
  }
@@ -6,11 +6,22 @@ module Decidim
6
6
  # This controller allows the user to update a Page.
7
7
  class SurveysController < Admin::ApplicationController
8
8
  include Decidim::Forms::Admin::Concerns::HasQuestionnaire
9
+ include Decidim::Forms::Admin::Concerns::HasQuestionnaireAnswers
9
10
 
10
11
  def questionnaire_for
11
12
  survey
12
13
  end
13
14
 
15
+ # Specify the public url from which the survey can be viewed and answered
16
+ def public_url
17
+ Decidim::EngineRouter.main_proxy(current_component).survey_path(survey)
18
+ end
19
+
20
+ # Specify where to redirect after exporting a user response
21
+ def questionnaire_participant_answers_url(session_token)
22
+ Decidim::EngineRouter.admin_proxy(survey.component).show_survey_path(session_token: session_token)
23
+ end
24
+
14
25
  private
15
26
 
16
27
  def i18n_flashes_scope
@@ -5,9 +5,10 @@ module Decidim
5
5
  # Exposes the survey resource so users can view and answer them.
6
6
  class SurveysController < Decidim::Surveys::ApplicationController
7
7
  include Decidim::Forms::Concerns::HasQuestionnaire
8
+ include Decidim::ComponentPathHelper
8
9
  helper Decidim::Surveys::SurveyHelper
9
10
 
10
- delegate :allow_answers?, to: :current_settings
11
+ delegate :allow_unregistered?, to: :current_settings
11
12
 
12
13
  before_action :check_permissions
13
14
 
@@ -19,6 +20,16 @@ module Decidim
19
20
  survey
20
21
  end
21
22
 
23
+ protected
24
+
25
+ def allow_answers?
26
+ !current_component.published? || current_settings.allow_answers?
27
+ end
28
+
29
+ def form_path
30
+ main_component_path(current_component)
31
+ end
32
+
22
33
  private
23
34
 
24
35
  def i18n_flashes_scope
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Surveys
5
+ class CleanSurveyAnswersJob < ApplicationJob
6
+ def perform(_event_name, data)
7
+ @component = data[:resource]
8
+ return unless component&.manifest_name == "surveys"
9
+
10
+ @survey = Survey.find_by(component: component)
11
+ return unless survey&.questionnaire
12
+
13
+ case data[:event_class]
14
+ when "Decidim::ComponentPublishedEvent"
15
+ clean_answers
16
+ end
17
+ end
18
+
19
+ private
20
+
21
+ attr_reader :survey, :component
22
+
23
+ def clean_answers
24
+ return unless survey.clean_after_publish?
25
+
26
+ survey.questionnaire.answers.destroy_all
27
+ component.settings[:clean_after_publish] = false
28
+ component.save
29
+ end
30
+ end
31
+ end
32
+ end
@@ -42,6 +42,10 @@ module Decidim
42
42
  !!current_settings[:allow_answers] == false &&
43
43
  previous_settings[:allow_answers] == true
44
44
  end
45
+
46
+ def clean_after_publish_changed?(previous_settings, current_settings)
47
+ current_settings[:clean_after_publish] != previous_settings[:clean_after_publish]
48
+ end
45
49
  # rubocop:enable Style/DoubleNegation
46
50
  end
47
51
  end
@@ -11,6 +11,10 @@ module Decidim
11
11
  component_manifest_name "surveys"
12
12
 
13
13
  validates :questionnaire, presence: true
14
+
15
+ def clean_after_publish?
16
+ component.settings.clean_after_publish?
17
+ end
14
18
  end
15
19
  end
16
20
  end
@@ -7,13 +7,19 @@ module Decidim
7
7
  def permissions
8
8
  return permission_action unless user
9
9
 
10
- return permission_action if permission_action.scope != :admin
10
+ return permission_action unless permission_action.scope == :admin
11
11
 
12
- return permission_action if permission_action.subject != :questionnaire
13
-
14
- case permission_action.action
15
- when :export_answers, :update
16
- permission_action.allow!
12
+ case permission_action.subject
13
+ when :questionnaire
14
+ case permission_action.action
15
+ when :export_answers, :update
16
+ permission_action.allow!
17
+ end
18
+ when :questionnaire_answers
19
+ case permission_action.action
20
+ when :index, :show, :export_response
21
+ permission_action.allow!
22
+ end
17
23
  end
18
24
 
19
25
  permission_action
@@ -4,7 +4,7 @@ module Decidim
4
4
  module Surveys
5
5
  class Permissions < Decidim::DefaultPermissions
6
6
  def permissions
7
- return permission_action unless user
7
+ return permission_action unless user || context[:current_settings].allow_unregistered?
8
8
 
9
9
  return Decidim::Surveys::Admin::Permissions.new(user, permission_action, context).permissions if permission_action.scope == :admin
10
10
  return permission_action if permission_action.scope != :public
@@ -9,9 +9,6 @@ module Decidim
9
9
  end
10
10
 
11
11
  def save
12
- return @registry if @registry
13
-
14
- @registry = []
15
12
  query.each do |key, results|
16
13
  cumulative_value = results[:cumulative]
17
14
  next if cumulative_value.zero?
@@ -23,10 +20,8 @@ module Decidim
23
20
  participatory_space_type: space_type, participatory_space_id: space_id,
24
21
  related_object_type: Decidim::Surveys::Survey.name, related_object_id: survey_id)
25
22
  record.assign_attributes(cumulative: cumulative_value, quantity: quantity_value)
26
- @registry << record
23
+ record.save!
27
24
  end
28
- @registry.each(&:save!)
29
- @registry
30
25
  end
31
26
 
32
27
  private
@@ -49,8 +44,7 @@ module Decidim
49
44
  end
50
45
 
51
46
  def retrieve_surveys
52
- components = Decidim::Component.where(participatory_space: retrieve_participatory_spaces).published
53
- Decidim::Surveys::Survey.where(component: components)
47
+ Decidim::Surveys::Survey.where(component: visible_component_ids_from_spaces(retrieve_participatory_spaces))
54
48
  end
55
49
 
56
50
  def retrieve_questionnaires(survey)
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Surveys
5
+ # Importer for Surveys specific data (this is, its questionnaire).
6
+ class DataImporter < Decidim::Importers::Importer
7
+ def initialize(component)
8
+ @component = component
9
+ end
10
+
11
+ # Public: Creates a new Decidim::Surveys::Survey and Decidim::Forms::Questionnaire associated to the given +component+
12
+ # for each serialized survey object.
13
+ # It imports the whole tree of Survey->Questionnaire->questions->answer_options.
14
+ #
15
+ # serialized - The Hash of attributes for the Questionnaire and its relations.
16
+ # user - The +user+ that is performing this action
17
+ #
18
+ # Returns the ser.
19
+ def import(serialized, user)
20
+ ActiveRecord::Base.transaction do
21
+ # we duplicate so that we can delete without affecting the received Hash
22
+ serialized.dup.collect do |serialized_survey|
23
+ import_survey(serialized_survey, user)
24
+ end
25
+ end
26
+ end
27
+
28
+ private
29
+
30
+ # Returns a persisted Survey instance build from +serialized_survey+.
31
+ def import_survey(serialized_survey, user)
32
+ serialized_survey = serialized_survey.with_indifferent_access
33
+ survey = build_survey(serialized_survey)
34
+ serialized_questionnaire = serialized_survey[:questionnaire]
35
+ serialized_questions = serialized_questionnaire.delete(:questions)
36
+
37
+ questionnaire = build_questionnaire(survey, serialized_questionnaire)
38
+ Decidim.traceability.perform_action!(:create, Decidim::Surveys::Survey, user) do
39
+ survey.save!
40
+ survey
41
+ end
42
+ import_questions(questionnaire, serialized_questions)
43
+ survey
44
+ end
45
+
46
+ def build_survey(_serialized)
47
+ Survey.new(component: @component)
48
+ end
49
+
50
+ # Builds a Decidim::Forms::Questionnaire with all its questions and answer_options.
51
+ def build_questionnaire(survey, serialized_questionnaire)
52
+ survey.build_questionnaire(serialized_questionnaire.except(:id, :published_at))
53
+ end
54
+
55
+ def import_questions(questionnaire, serialized_questions)
56
+ serialized_questions.each do |serialized_question|
57
+ serialized_answer_options = serialized_question.delete(:answer_options)
58
+ question = questionnaire.questions.create!(serialized_question.except(:id, :created_at, :updated_at))
59
+ serialized_answer_options.each do |serialized_answer_option|
60
+ question.answer_options.create!(serialized_answer_option.except(:id, :created_at, :updated_at))
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Surveys
5
+ # This class serializes the specific data in each Survey.
6
+ # This is `Questionnaire->questions->answer_options` but not `answers`
7
+ # and `answer_choices`.
8
+ class DataSerializer < Decidim::Exporters::Serializer
9
+ # Returns: Array of Decidim::Forms::Questionnaire as a json hash,
10
+ # or nil if none exists.
11
+ def serialize
12
+ component = resource
13
+ surveys = Decidim::Surveys::Survey.where(component: component)
14
+ surveys.collect do |survey|
15
+ next if survey.questionnaire.nil?
16
+
17
+ json = serialize_survey(survey)
18
+ json.with_indifferent_access.merge(survey_id: survey.id)
19
+ end
20
+ end
21
+
22
+ def serialize_survey(survey)
23
+ questionnaire = survey.questionnaire
24
+ questionnaire_json = questionnaire.attributes.as_json
25
+ questionnaire_json[:questions] = serialize_questions(questionnaire.questions.order(:position))
26
+ json = survey.attributes.as_json
27
+ json[:questionnaire] = questionnaire_json
28
+ json
29
+ end
30
+
31
+ def serialize_questions(questions)
32
+ questions.collect do |question|
33
+ json = question.attributes.as_json
34
+ json[:answer_options] = serialize_answer_options(question.answer_options)
35
+ json
36
+ end
37
+ end
38
+
39
+ def serialize_answer_options(answer_options)
40
+ answer_options.collect do |option|
41
+ option.attributes.as_json
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Surveys
5
+ SurveyType = GraphQL::ObjectType.define do
6
+ name "Survey"
7
+ description "A survey"
8
+
9
+ field :id, !types.ID, "The internal ID for this survey"
10
+ field :createdAt, Decidim::Core::DateTimeType, "The time this survey was created", property: :created_at
11
+ field :updatedAt, Decidim::Core::DateTimeType, "The time this survey was updated", property: :updated_at
12
+ field :questionnaire, Decidim::Forms::QuestionnaireType, "The questionnaire for this survey"
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Surveys
5
+ SurveysType = GraphQL::ObjectType.define do
6
+ interfaces [-> { Decidim::Core::ComponentInterface }]
7
+
8
+ name "Surveys"
9
+ description "A surveys component of a participatory space."
10
+
11
+ connection :surveys, SurveyType.connection_type do
12
+ resolve ->(component, _args, _ctx) {
13
+ SurveysTypeHelper.base_scope(component).includes(:component)
14
+ }
15
+ end
16
+
17
+ field(:survey, SurveyType) do
18
+ argument :id, !types.ID
19
+
20
+ resolve ->(component, args, _ctx) {
21
+ SurveysTypeHelper.base_scope(component).find_by(id: args[:id])
22
+ }
23
+ end
24
+ end
25
+
26
+ module SurveysTypeHelper
27
+ def self.base_scope(component)
28
+ Survey.where(component: component)
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1 @@
1
+ am:
@@ -30,6 +30,7 @@ ar:
30
30
  announcement: إعلان
31
31
  step:
32
32
  allow_answers: السماح بالإجابات
33
+ allow_unregistered: السماح للمستخدمين غير المسجَّلِين بالإجابة على استطلاع الرأي
33
34
  announcement: إعلان
34
35
  events:
35
36
  surveys:
@@ -0,0 +1,10 @@
1
+ bg:
2
+ activemodel:
3
+ models:
4
+ decidim/surveys/closed_survey_event: Анкетата завърши
5
+ decidim/surveys/opened_survey_event: Анкетата започна
6
+ activerecord:
7
+ models:
8
+ decidim/surveys/survey:
9
+ one: Анкета
10
+ other: Анкети
@@ -0,0 +1,10 @@
1
+ bg:
2
+ activemodel:
3
+ models:
4
+ decidim/surveys/closed_survey_event: Анкетата завърши
5
+ decidim/surveys/opened_survey_event: Анкетата започна
6
+ activerecord:
7
+ models:
8
+ decidim/surveys/survey:
9
+ one: Анкета
10
+ other: Анкети
@@ -20,8 +20,13 @@ ca:
20
20
  settings:
21
21
  global:
22
22
  announcement: Avís
23
+ clean_after_publish: Esborrar les respostes en publicar l'enquesta
24
+ scope_id: Àmbit
25
+ scopes_enabled: Àmbits habilitats
23
26
  step:
24
27
  allow_answers: Permetre respostes
28
+ allow_unregistered: Permet a usuàries no registrades contestar l'enquesta
29
+ allow_unregistered_help: Si s'activa, no caldrà iniciar sessió per a contestar l'enquesta. Això pot comportar que les dades siguin poc fiables i serà més vulnerable a atacs automatitzats. Feu-ho servir amb cura!
25
30
  announcement: Avís
26
31
  events:
27
32
  surveys:
@@ -53,4 +58,5 @@ ca:
53
58
  surveys:
54
59
  answer:
55
60
  invalid: S'ha produït un error en respondre l'enquesta.
61
+ spam_detected: Hi ha hagut un problema contestant el formulari. Potser has estat massa ràpid, ho pots tornar a intentar?
56
62
  success: Enquesta resposta correctament.