decidim-forms 0.19.1 → 0.20.0

Sign up to get free protection for your applications and to get access to all the features.
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