decidim-forms 0.19.1 → 0.20.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e22e76b94713686b575b5a34fda58a2bc702daf22d6ab13cd00e4768b5ec4280
4
- data.tar.gz: 6533f5486575eddbd71104e54564e856e5e98edfffcb57c4ef5305d051e1bac2
3
+ metadata.gz: 3d333400b35dde698ec6eb56578ce6d339d056abb654a32cf5429b6e525af594
4
+ data.tar.gz: 8bf35eecf87210818501d217c61522f8db293a86f35624ec76642a8a665aa678
5
5
  SHA512:
6
- metadata.gz: 9af0c32621f38375c4c19ec80161e79fc7f6fd2fab368266de7d355131b3eaef7ff46a6282b54f01fe0cf5f57ce2206234717974b7118a0acfed9de94a853074
7
- data.tar.gz: cf11232630856059fc7f382e6b7dd03e9d4d7147ca1f3503f93f48e4c2515103c6a19ed8eb79c05ce009b500db2007a28e1b2f2a00139b2bfe97b1d7302b5d63
6
+ metadata.gz: d4aa63f125fbaf07ef76c0e209d79df0333c374e6675948e310e7787521a74fff17326c7b6e30414bc6ff99bd7f1d39af34bd0f9b68b2d03480e04f0da62105a
7
+ data.tar.gz: 138359228e76cd3b79dc0e4f46ced204204a03e31b0815627074ffbb17a7e301220a12eab0f9209a3a2fb24683ffad4da8aea67e01e24dfb1cf787cb2be1995f
data/README.md CHANGED
@@ -54,7 +54,7 @@ See [Decidim](https://github.com/decidim/decidim).
54
54
 
55
55
  ## Seeds
56
56
 
57
- Since questionnaires cannot exist without a real model we are not including specific seeds for this engine.
57
+ Since questionnaires cannot exist without a component we are not including specific seeds for this engine.
58
58
 
59
59
  Other engines are free to include questionnaires on their seeds like this:
60
60
 
@@ -24,16 +24,20 @@ module Decidim
24
24
  broadcast(:ok)
25
25
  end
26
26
 
27
+ attr_reader :form
28
+
27
29
  private
28
30
 
29
31
  def answer_questionnaire
30
32
  Answer.transaction do
31
- @form.answers.each do |form_answer|
33
+ form.answers.each do |form_answer|
32
34
  answer = Answer.new(
33
35
  user: @current_user,
34
36
  questionnaire: @questionnaire,
35
37
  question: form_answer.question,
36
- body: form_answer.body
38
+ body: form_answer.body,
39
+ session_token: form.context.session_token,
40
+ ip_hash: form.context.ip_hash
37
41
  )
38
42
 
39
43
  form_answer.selected_choices.each do |choice|
@@ -16,7 +16,9 @@ module Decidim
16
16
  helper Decidim::Forms::ApplicationHelper
17
17
  include FormFactory
18
18
 
19
- helper_method :questionnaire_for, :questionnaire, :allow_answers?, :update_url
19
+ helper_method :questionnaire_for, :questionnaire, :allow_answers?, :visitor_can_answer?, :visitor_already_answered?, :update_url
20
+
21
+ invisible_captcha on_spam: :spam_detected
20
22
 
21
23
  def show
22
24
  @form = form(Decidim::Forms::QuestionnaireForm).from_model(questionnaire)
@@ -26,7 +28,7 @@ module Decidim
26
28
  def answer
27
29
  enforce_permission_to :answer, :questionnaire
28
30
 
29
- @form = form(Decidim::Forms::QuestionnaireForm).from_params(params)
31
+ @form = form(Decidim::Forms::QuestionnaireForm).from_params(params, session_token: session_token)
30
32
 
31
33
  Decidim::Forms::AnswerQuestionnaire.call(@form, current_user, questionnaire) do
32
34
  on(:ok) do
@@ -49,6 +51,22 @@ module Decidim
49
51
  raise "#{self.class.name} is expected to implement #allow_answers?"
50
52
  end
51
53
 
54
+ # Public: Method to be implemented at the controller if needed. You need to
55
+ # return true if the questionnaire can receive answers by unregistered users
56
+ def allow_unregistered?
57
+ false
58
+ end
59
+
60
+ # Public: return true if the current user (if logged) can answer the questionnaire
61
+ def visitor_can_answer?
62
+ current_user || allow_unregistered?
63
+ end
64
+
65
+ # Public: return true if the current user (or session visitor) can answer the questionnaire
66
+ def visitor_already_answered?
67
+ questionnaire.answered_by?(current_user || tokenize(session[:session_id]))
68
+ end
69
+
52
70
  # Public: Returns a String or Object that will be passed to `redirect_to` after
53
71
  # answering the questionnaire. By default it redirects to the questionnaire_for.
54
72
  #
@@ -78,6 +96,35 @@ module Decidim
78
96
  def questionnaire
79
97
  @questionnaire ||= Questionnaire.includes(questions: :answer_options).find_by(questionnaire_for: questionnaire_for)
80
98
  end
99
+
100
+ def spam_detected
101
+ enforce_permission_to :answer, :questionnaire
102
+
103
+ @form = form(Decidim::Forms::QuestionnaireForm).from_params(params)
104
+
105
+ flash.now[:alert] = I18n.t("answer.spam_detected", scope: i18n_flashes_scope)
106
+ render template: "decidim/forms/questionnaires/show"
107
+ end
108
+
109
+ def ip_hash
110
+ return nil unless request&.remote_ip
111
+
112
+ @ip_hash ||= tokenize(request&.remote_ip)
113
+ end
114
+
115
+ # token is used as a substitute of user_id if unregistered
116
+ def session_token
117
+ id = current_user&.id
118
+ session_id = request.session[:session_id] if request&.session
119
+
120
+ return nil unless id || session_id
121
+
122
+ @session_token ||= tokenize(id || session_id)
123
+ end
124
+
125
+ def tokenize(id)
126
+ Digest::MD5.hexdigest("#{id}-#{Rails.application.secrets.secret_key_base}")
127
+ end
81
128
  end
82
129
  end
83
130
  end
@@ -8,7 +8,9 @@ module Decidim
8
8
  attribute :user_group_id, Integer
9
9
 
10
10
  attribute :tos_agreement, Boolean
11
+
11
12
  validates :tos_agreement, allow_nil: false, acceptance: true
13
+ validate :session_token_in_context
12
14
 
13
15
  # Private: Create the answers from the questionnaire questions
14
16
  #
@@ -18,6 +20,12 @@ module Decidim
18
20
  AnswerForm.from_model(Decidim::Forms::Answer.new(question: question))
19
21
  end
20
22
  end
23
+
24
+ def session_token_in_context
25
+ return if context&.session_token
26
+
27
+ errors.add(:tos_agreement, I18n.t("activemodel.errors.models.questionnaire.request_invalid"))
28
+ end
21
29
  end
22
30
  end
23
31
  end
@@ -7,7 +7,7 @@ module Decidim
7
7
  include Decidim::DataPortability
8
8
  include Decidim::NewsletterParticipant
9
9
 
10
- belongs_to :user, class_name: "Decidim::User", foreign_key: "decidim_user_id"
10
+ belongs_to :user, class_name: "Decidim::User", foreign_key: "decidim_user_id", optional: true
11
11
  belongs_to :questionnaire, class_name: "Questionnaire", foreign_key: "decidim_questionnaire_id"
12
12
  belongs_to :question, class_name: "Question", foreign_key: "decidim_question_id"
13
13
 
@@ -45,7 +45,7 @@ module Decidim
45
45
  private
46
46
 
47
47
  def user_questionnaire_same_organization
48
- return if user&.organization == questionnaire.questionnaire_for&.organization
48
+ return if user.nil? || user&.organization == questionnaire.questionnaire_for&.organization
49
49
 
50
50
  errors.add(:user, :invalid)
51
51
  end
@@ -16,7 +16,8 @@ module Decidim
16
16
 
17
17
  # Public: returns whether the questionnaire is answered by the user or not.
18
18
  def answered_by?(user)
19
- answers.where(user: user).any? if questions.present?
19
+ query = user.is_a?(String) ? { session_token: user } : { user: user }
20
+ answers.where(query).any? if questions.present?
20
21
  end
21
22
  end
22
23
  end
@@ -21,7 +21,7 @@ module Decidim
21
21
  # Finds and group answers by user for each questionnaire's question.
22
22
  def query
23
23
  answers = Answer.where(questionnaire: @questionnaire)
24
- answers.sort_by { |answer| answer.question.position }.group_by(&:user).values
24
+ answers.sort_by { |answer| answer.question.position }.group_by { |a| a.user || a.session_token }.values
25
25
  end
26
26
  end
27
27
  end
@@ -19,8 +19,8 @@
19
19
  <div class="card">
20
20
  <div class="card__content">
21
21
  <% if allow_answers? %>
22
- <% if current_user %>
23
- <% if questionnaire.answered_by?(current_user) %>
22
+ <% if visitor_can_answer? %>
23
+ <% if visitor_already_answered? %>
24
24
  <div class="section">
25
25
  <div class="callout success">
26
26
  <h5><%= t(".questionnaire_answered.title") %></h5>
@@ -39,6 +39,7 @@
39
39
  <% end %>
40
40
 
41
41
  <%= decidim_form_for(@form, url: update_url, method: :post, html: { class: "form answer-questionnaire" }) do |form| %>
42
+ <%= invisible_captcha %>
42
43
  <% @form.answers.each_with_index do |answer, answer_idx| %>
43
44
  <div class="row column">
44
45
  <%= fields_for "questionnaire[answers][#{answer_idx}]", answer do |answer_form| %>
@@ -80,3 +80,6 @@ ar:
80
80
  user_answers_serializer:
81
81
  created_at: أجاب على
82
82
  id: معرف الإجابة
83
+ registered: مُسجَّل
84
+ unregistered: غير مُسجَّل
85
+ user_status: حالة المستخدم
@@ -15,6 +15,8 @@ cs:
15
15
  choices:
16
16
  missing: nejsou úplné
17
17
  too_many: jsou příliš mnoho
18
+ questionnaire:
19
+ request_invalid: Při zpracování požadavku došlo k chybě. Prosím zkuste to znovu
18
20
  decidim:
19
21
  forms:
20
22
  admin:
@@ -80,3 +82,7 @@ cs:
80
82
  user_answers_serializer:
81
83
  created_at: Zodpovězeno
82
84
  id: ID odpovědi
85
+ ip_hash: Ip Hash
86
+ registered: Registrován
87
+ unregistered: Neregistrovaný
88
+ user_status: Stav uživatele
@@ -16,6 +16,8 @@ en:
16
16
  choices:
17
17
  missing: are not complete
18
18
  too_many: are too many
19
+ questionnaire:
20
+ request_invalid: There's been an error handling the request. Please try again
19
21
  decidim:
20
22
  forms:
21
23
  admin:
@@ -81,3 +83,7 @@ en:
81
83
  user_answers_serializer:
82
84
  created_at: Answered on
83
85
  id: Answer ID
86
+ ip_hash: Ip Hash
87
+ registered: Registered
88
+ unregistered: Unregistered
89
+ user_status: User status
@@ -15,6 +15,8 @@ fi-pl:
15
15
  choices:
16
16
  missing: ovat puutteellisia
17
17
  too_many: liian monta
18
+ questionnaire:
19
+ request_invalid: Pyynnön käsittely ei onnistunut. Yritä uudestaan myöhemmin
18
20
  decidim:
19
21
  forms:
20
22
  admin:
@@ -80,3 +82,7 @@ fi-pl:
80
82
  user_answers_serializer:
81
83
  created_at: Vastattu
82
84
  id: Vastaa tunnus
85
+ ip_hash: IP-tarkistesumma
86
+ registered: Rekisteröitynyt
87
+ unregistered: Ei rekisteröitynyt
88
+ user_status: Käyttäjän tila
@@ -15,6 +15,8 @@ fi:
15
15
  choices:
16
16
  missing: ovat puutteellisia
17
17
  too_many: liian monta
18
+ questionnaire:
19
+ request_invalid: Pyynnön käsittely ei onnistunut. Yritä uudestaan myöhemmin
18
20
  decidim:
19
21
  forms:
20
22
  admin:
@@ -80,3 +82,7 @@ fi:
80
82
  user_answers_serializer:
81
83
  created_at: Vastauksen ajankohta
82
84
  id: Vastauksen ID
85
+ ip_hash: IP-tarkistesumma
86
+ registered: Rekisteröitynyt
87
+ unregistered: Ei rekisteröitynyt
88
+ user_status: Käyttäjän tila
@@ -15,6 +15,8 @@ fr:
15
15
  choices:
16
16
  missing: ne sont pas complets
17
17
  too_many: sont trop nombreux
18
+ questionnaire:
19
+ request_invalid: Il y a eu une erreur lors du traitement de la demande. Veuillez réessayer
18
20
  decidim:
19
21
  forms:
20
22
  admin:
@@ -80,3 +82,7 @@ fr:
80
82
  user_answers_serializer:
81
83
  created_at: Répondu le
82
84
  id: ID de réponse
85
+ ip_hash: Hachage Ip
86
+ registered: Enregistré
87
+ unregistered: Non enregistré
88
+ user_status: Statut de l'utilisateur
@@ -15,6 +15,8 @@ hu:
15
15
  choices:
16
16
  missing: nem teljesek
17
17
  too_many: túl sok
18
+ questionnaire:
19
+ request_invalid: A kérés teljesítése során hiba történt. Kérjük próbálkozzon újra
18
20
  decidim:
19
21
  forms:
20
22
  admin:
@@ -80,3 +82,7 @@ hu:
80
82
  user_answers_serializer:
81
83
  created_at: Válaszolt
82
84
  id: Válasz azonosító
85
+ ip_hash: Ip Hash
86
+ registered: Regisztrált
87
+ unregistered: Nem regisztrált
88
+ user_status: Felhasználó státusza
@@ -15,6 +15,8 @@ it:
15
15
  choices:
16
16
  missing: non sono completi
17
17
  too_many: sono troppi
18
+ questionnaire:
19
+ request_invalid: C'è stato un errore di gestione della richiesta. Si prega di riprovare
18
20
  decidim:
19
21
  forms:
20
22
  admin:
@@ -80,3 +82,7 @@ it:
80
82
  user_answers_serializer:
81
83
  created_at: Risposto
82
84
  id: ID risposta
85
+ ip_hash: Hash Ip
86
+ registered: Registrato
87
+ unregistered: Non registrato
88
+ user_status: Stato utente
@@ -15,6 +15,8 @@ nl:
15
15
  choices:
16
16
  missing: zijn niet compleet
17
17
  too_many: zijn te veel
18
+ questionnaire:
19
+ request_invalid: Er is een fout opgetreden bij het verwerken van het verzoek. Probeer het opnieuw
18
20
  decidim:
19
21
  forms:
20
22
  admin:
@@ -80,3 +82,7 @@ nl:
80
82
  user_answers_serializer:
81
83
  created_at: Beantwoord
82
84
  id: Antwoord ID
85
+ ip_hash: Ip Hash
86
+ registered: Geregistreerd
87
+ unregistered: Niet-geregistreerd
88
+ user_status: Gebruiker-status
@@ -15,6 +15,8 @@ sv:
15
15
  choices:
16
16
  missing: är inte fullständiga
17
17
  too_many: är för många
18
+ questionnaire:
19
+ request_invalid: Ett fel uppstod när begäran skulle hanteras. Vänligen försök igen
18
20
  decidim:
19
21
  forms:
20
22
  admin:
@@ -80,3 +82,7 @@ sv:
80
82
  user_answers_serializer:
81
83
  created_at: Besvarad
82
84
  id: Svars-ID
85
+ ip_hash: Ip Hash
86
+ registered: Registrerad
87
+ unregistered: Oregistrerad
88
+ user_status: Användarstatus
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AddSessionTokenToDecidimFormsAnswers < ActiveRecord::Migration[5.2]
4
+ class Answer < ApplicationRecord
5
+ self.table_name = :decidim_forms_answers
6
+ end
7
+
8
+ def change
9
+ add_column :decidim_forms_answers, :session_token, :string, null: false, default: ""
10
+ add_index :decidim_forms_answers, :session_token
11
+
12
+ Answer.find_each do |answer|
13
+ answer.session_token = Digest::MD5.hexdigest("#{answer.decidim_user_id}-#{Rails.application.secrets.secret_key_base}")
14
+ answer.save!
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AddIpHashToDecidimFormAnswers < ActiveRecord::Migration[5.2]
4
+ class Answer < ApplicationRecord
5
+ self.table_name = :decidim_forms_answers
6
+ end
7
+
8
+ def change
9
+ add_column :decidim_forms_answers, :ip_hash, :string
10
+ add_index :decidim_forms_answers, :ip_hash
11
+ end
12
+ end
@@ -4,7 +4,7 @@ require "decidim/core/test/factories"
4
4
  require "decidim/participatory_processes/test/factories"
5
5
 
6
6
  FactoryBot.define do
7
- factory :questionnaire, class: "Decidim::Forms::Questionnaire" do
7
+ factory :questionnaire, class: Decidim::Forms::Questionnaire do
8
8
  title { generate_localized_title }
9
9
  description do
10
10
  Decidim::Faker::Localized.wrapped("<p>", "</p>") do
@@ -13,11 +13,21 @@ FactoryBot.define do
13
13
  end
14
14
  tos { generate_localized_title }
15
15
  questionnaire_for { build(:participatory_process) }
16
+
17
+ trait :with_questions do
18
+ questions do
19
+ qs = %w(short_answer long_answer).collect do |text_question_type|
20
+ build(:questionnaire_question, question_type: text_question_type)
21
+ end
22
+ qs << build(:questionnaire_question, :with_answer_options, question_type: :single_option)
23
+ qs
24
+ end
25
+ end
16
26
  end
17
27
 
18
- factory :questionnaire_question, class: "Decidim::Forms::Question" do
28
+ factory :questionnaire_question, class: Decidim::Forms::Question do
19
29
  transient do
20
- answer_options { [] }
30
+ options { [] }
21
31
  end
22
32
 
23
33
  body { generate_localized_title }
@@ -27,28 +37,37 @@ FactoryBot.define do
27
37
  questionnaire
28
38
 
29
39
  before(:create) do |question, evaluator|
30
- evaluator.answer_options.each do |answer_option|
31
- question.answer_options.build(
32
- body: answer_option["body"],
33
- free_text: answer_option["free_text"]
34
- )
40
+ if question.answer_options.empty?
41
+ evaluator.options.each do |option|
42
+ question.answer_options.build(
43
+ body: option["body"],
44
+ free_text: option["free_text"]
45
+ )
46
+ end
47
+ end
48
+ end
49
+
50
+ trait :with_answer_options do
51
+ answer_options do
52
+ Array.new(3).collect { build(:answer_option) }
35
53
  end
36
54
  end
37
55
  end
38
56
 
39
- factory :answer, class: "Decidim::Forms::Answer" do
57
+ factory :answer, class: Decidim::Forms::Answer do
40
58
  body { "hola" }
41
59
  questionnaire
42
60
  question { create(:questionnaire_question, questionnaire: questionnaire) }
43
61
  user { create(:user, organization: questionnaire.questionnaire_for.organization) }
44
62
  end
45
63
 
46
- factory :answer_option, class: "Decidim::Forms::AnswerOption" do
64
+ factory :answer_option, class: Decidim::Forms::AnswerOption do
47
65
  question { create(:questionnaire_question) }
48
66
  body { generate_localized_title }
67
+ free_text { false }
49
68
  end
50
69
 
51
- factory :answer_choice, class: "Decidim::Forms::AnswerChoice" do
70
+ factory :answer_choice, class: Decidim::Forms::AnswerChoice do
52
71
  answer
53
72
  answer_option { create(:answer_option, question: answer.question) }
54
73
  end
@@ -54,7 +54,7 @@ shared_examples_for "has questionnaire" do
54
54
  questionnaire: questionnaire,
55
55
  question_type: "single_option",
56
56
  position: 0,
57
- answer_options: [
57
+ options: [
58
58
  { "body" => Decidim::Faker::Localized.sentence },
59
59
  { "body" => Decidim::Faker::Localized.sentence }
60
60
  ]
@@ -163,7 +163,7 @@ shared_examples_for "has questionnaire" do
163
163
  question_type: "single_option",
164
164
  position: 0,
165
165
  mandatory: true,
166
- answer_options: [
166
+ options: [
167
167
  { "body" => Decidim::Faker::Localized.sentence },
168
168
  { "body" => Decidim::Faker::Localized.sentence }
169
169
  ]
@@ -205,7 +205,7 @@ shared_examples_for "has questionnaire" do
205
205
  :questionnaire_question,
206
206
  questionnaire: questionnaire,
207
207
  question_type: question_type,
208
- answer_options: [
208
+ options: [
209
209
  { "body" => answer_option_bodies[0] },
210
210
  { "body" => answer_option_bodies[1] },
211
211
  { "body" => answer_option_bodies[2], "free_text" => true }
@@ -219,7 +219,7 @@ shared_examples_for "has questionnaire" do
219
219
  questionnaire: questionnaire,
220
220
  question_type: "multiple_option",
221
221
  max_choices: 2,
222
- answer_options: [
222
+ options: [
223
223
  { "body" => Decidim::Faker::Localized.sentence },
224
224
  { "body" => Decidim::Faker::Localized.sentence },
225
225
  { "body" => Decidim::Faker::Localized.sentence }
@@ -346,7 +346,7 @@ shared_examples_for "has questionnaire" do
346
346
 
347
347
  context "when question type is single option" do
348
348
  let(:answer_options) { Array.new(2) { { "body" => Decidim::Faker::Localized.sentence } } }
349
- let!(:question) { create(:questionnaire_question, questionnaire: questionnaire, question_type: "single_option", answer_options: answer_options) }
349
+ let!(:question) { create(:questionnaire_question, questionnaire: questionnaire, question_type: "single_option", options: answer_options) }
350
350
 
351
351
  it "renders answers as a collection of radio buttons" do
352
352
  visit questionnaire_public_path
@@ -372,7 +372,7 @@ shared_examples_for "has questionnaire" do
372
372
 
373
373
  context "when question type is multiple option" do
374
374
  let(:answer_options) { Array.new(3) { { "body" => Decidim::Faker::Localized.sentence } } }
375
- let!(:question) { create(:questionnaire_question, questionnaire: questionnaire, question_type: "multiple_option", answer_options: answer_options) }
375
+ let!(:question) { create(:questionnaire_question, questionnaire: questionnaire, question_type: "multiple_option", options: answer_options) }
376
376
 
377
377
  it "renders answers as a collection of radio buttons" do
378
378
  visit questionnaire_public_path
@@ -431,7 +431,7 @@ shared_examples_for "has questionnaire" do
431
431
 
432
432
  context "when question type is multiple option" do
433
433
  let(:answer_options) { Array.new(2) { { "body" => Decidim::Faker::Localized.sentence } } }
434
- let!(:question) { create(:questionnaire_question, questionnaire: questionnaire, question_type: "multiple_option", answer_options: answer_options) }
434
+ let!(:question) { create(:questionnaire_question, questionnaire: questionnaire, question_type: "multiple_option", options: answer_options) }
435
435
 
436
436
  it "renders the question answers as a collection of radio buttons" do
437
437
  visit questionnaire_public_path
@@ -462,7 +462,7 @@ shared_examples_for "has questionnaire" do
462
462
  :questionnaire_question,
463
463
  questionnaire: questionnaire,
464
464
  question_type: "sorting",
465
- answer_options: [
465
+ options: [
466
466
  { "body" => "idiotas" },
467
467
  { "body" => "trates" },
468
468
  { "body" => "No" },
@@ -440,7 +440,7 @@ shared_examples_for "manage questionnaires" do
440
440
  questionnaire: questionnaire,
441
441
  body: body,
442
442
  question_type: "single_option",
443
- answer_options: [
443
+ options: [
444
444
  { "body" => { "en" => "cacarua" } },
445
445
  { "body" => { "en" => "cat" } },
446
446
  { "body" => { "en" => "dog" } }
@@ -18,6 +18,8 @@ module Decidim
18
18
  serialized.update(
19
19
  answer_translated_attribute_name(:id) => answer.id,
20
20
  answer_translated_attribute_name(:created_at) => answer.created_at.to_s(:db),
21
+ answer_translated_attribute_name(:ip_hash) => answer.ip_hash,
22
+ answer_translated_attribute_name(:user_status) => answer_translated_attribute_name(answer.decidim_user_id.present? ? "registered" : "unregistered"),
21
23
  "#{idx + 1}. #{translated_attribute(answer.question.body)}" => normalize_body(answer)
22
24
  )
23
25
  end
@@ -4,7 +4,7 @@ module Decidim
4
4
  # This holds the decidim-forms version.
5
5
  module Forms
6
6
  def self.version
7
- "0.19.1"
7
+ "0.20.0"
8
8
  end
9
9
  end
10
10
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: decidim-forms
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.19.1
4
+ version: 0.20.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Josep Jaume Rey Peroy
@@ -11,7 +11,7 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2019-12-13 00:00:00.000000000 Z
14
+ date: 2020-01-08 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: decidim-core
@@ -19,42 +19,42 @@ dependencies:
19
19
  requirements:
20
20
  - - '='
21
21
  - !ruby/object:Gem::Version
22
- version: 0.19.1
22
+ version: 0.20.0
23
23
  type: :runtime
24
24
  prerelease: false
25
25
  version_requirements: !ruby/object:Gem::Requirement
26
26
  requirements:
27
27
  - - '='
28
28
  - !ruby/object:Gem::Version
29
- version: 0.19.1
29
+ version: 0.20.0
30
30
  - !ruby/object:Gem::Dependency
31
31
  name: decidim-admin
32
32
  requirement: !ruby/object:Gem::Requirement
33
33
  requirements:
34
34
  - - '='
35
35
  - !ruby/object:Gem::Version
36
- version: 0.19.1
36
+ version: 0.20.0
37
37
  type: :development
38
38
  prerelease: false
39
39
  version_requirements: !ruby/object:Gem::Requirement
40
40
  requirements:
41
41
  - - '='
42
42
  - !ruby/object:Gem::Version
43
- version: 0.19.1
43
+ version: 0.20.0
44
44
  - !ruby/object:Gem::Dependency
45
45
  name: decidim-dev
46
46
  requirement: !ruby/object:Gem::Requirement
47
47
  requirements:
48
48
  - - '='
49
49
  - !ruby/object:Gem::Version
50
- version: 0.19.1
50
+ version: 0.20.0
51
51
  type: :development
52
52
  prerelease: false
53
53
  version_requirements: !ruby/object:Gem::Requirement
54
54
  requirements:
55
55
  - - '='
56
56
  - !ruby/object:Gem::Version
57
- version: 0.19.1
57
+ version: 0.20.0
58
58
  description: A forms gem for decidim.
59
59
  email:
60
60
  - josepjaume@gmail.com
@@ -143,6 +143,8 @@ files:
143
143
  - db/migrate/20170515144119_create_decidim_forms_answers.rb
144
144
  - db/migrate/20180405015012_create_decidim_forms_answer_options.rb
145
145
  - db/migrate/20180405015147_create_decidim_forms_answer_choices.rb
146
+ - db/migrate/20190315203056_add_session_token_to_decidim_forms_answers.rb
147
+ - db/migrate/20190930094710_add_ip_hash_to_decidim_form_answers.rb
146
148
  - lib/decidim/forms.rb
147
149
  - lib/decidim/forms/admin.rb
148
150
  - lib/decidim/forms/admin_engine.rb