decidim-initiatives 0.16.1 → 0.17.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/assets/javascripts/decidim/initiatives/scoped_type.js +13 -10
- data/app/assets/stylesheet/decidim/initiatives/initiatives-votes.css.scss +97 -0
- data/app/cells/decidim/initiatives/initiative_m_cell.rb +1 -1
- data/app/cells/decidim/initiatives_votes/vote/show.erb +29 -0
- data/app/cells/decidim/initiatives_votes/vote_cell.rb +51 -0
- data/app/commands/decidim/initiatives/admin/create_initiative_type.rb +8 -1
- data/app/commands/decidim/initiatives/admin/update_initiative.rb +7 -6
- data/app/commands/decidim/initiatives/admin/update_initiative_answer.rb +76 -0
- data/app/commands/decidim/initiatives/admin/update_initiative_type.rb +17 -1
- data/app/commands/decidim/initiatives/create_initiative.rb +12 -0
- data/app/commands/decidim/initiatives/spawn_committee_request.rb +8 -7
- data/app/commands/decidim/initiatives/validate_mobile_phone.rb +61 -0
- data/app/commands/decidim/initiatives/validate_sms_code.rb +40 -0
- data/app/commands/decidim/initiatives/vote_initiative.rb +23 -7
- data/app/controllers/concerns/decidim/initiatives/needs_initiative.rb +12 -1
- data/app/controllers/decidim/initiatives/admin/answers_controller.rb +46 -0
- data/app/controllers/decidim/initiatives/admin/initiatives_controller.rb +31 -5
- data/app/controllers/decidim/initiatives/admin/initiatives_types_controller.rb +1 -0
- data/app/controllers/decidim/initiatives/admin/initiatives_types_permissions_controller.rb +20 -0
- data/app/controllers/decidim/initiatives/admin/moderations_controller.rb +16 -0
- data/app/controllers/decidim/initiatives/application_controller.rb +0 -2
- data/app/controllers/decidim/initiatives/authorization_sign_modals_controller.rb +26 -0
- data/app/controllers/decidim/initiatives/committee_requests_controller.rb +4 -1
- data/app/controllers/decidim/initiatives/create_initiative_controller.rb +16 -5
- data/app/controllers/decidim/initiatives/initiative_signatures_controller.rb +207 -0
- data/app/controllers/decidim/initiatives/initiative_votes_controller.rb +3 -1
- data/app/controllers/decidim/initiatives/initiatives_type_signature_types_controller.rb +21 -0
- data/app/forms/decidim/initiatives/admin/initiative_answer_form.rb +29 -0
- data/app/forms/decidim/initiatives/admin/initiative_form.rb +25 -7
- data/app/forms/decidim/initiatives/admin/initiative_type_form.rb +14 -0
- data/app/forms/decidim/initiatives/committee_member_form.rb +30 -0
- data/app/forms/decidim/initiatives/initiative_form.rb +5 -0
- data/app/forms/decidim/initiatives/vote_form.rb +151 -0
- data/app/helpers/decidim/initiatives/create_initiative_helper.rb +25 -12
- data/app/helpers/decidim/initiatives/initiative_helper.rb +18 -0
- data/app/models/decidim/initiative.rb +54 -3
- data/app/models/decidim/initiatives_committee_member.rb +1 -0
- data/app/models/decidim/initiatives_type.rb +30 -0
- data/app/permissions/decidim/initiatives/admin/permissions.rb +12 -3
- data/app/permissions/decidim/initiatives/permissions.rb +37 -8
- data/app/queries/decidim/initiatives/admin/admin_users.rb +39 -0
- data/app/services/decidim/initiatives/data_encryptor.rb +26 -0
- data/app/services/decidim/initiatives/dummy_timestamp.rb +22 -0
- data/app/services/decidim/initiatives/pdf_signature_example.rb +121 -0
- data/app/views/decidim/initiatives/admin/answers/_info_initiative.html.erb +23 -0
- data/app/views/decidim/initiatives/admin/answers/edit.html.erb +35 -0
- data/app/views/decidim/initiatives/admin/initiatives/_form.html.erb +17 -16
- data/app/views/decidim/initiatives/admin/initiatives/edit.html.erb +10 -3
- data/app/views/decidim/initiatives/admin/initiatives/export_pdf_signatures.pdf.erb +35 -0
- data/app/views/decidim/initiatives/admin/initiatives/index.html.erb +6 -0
- data/app/views/decidim/initiatives/admin/initiatives_types/_form.html.erb +36 -0
- data/app/views/decidim/initiatives/admin/initiatives_types/index.html.erb +2 -0
- data/app/views/decidim/initiatives/create_initiative/fill_data.html.erb +3 -3
- data/app/views/decidim/initiatives/create_initiative/finish.html.erb +2 -2
- data/app/views/decidim/initiatives/create_initiative/previous_form.html.erb +2 -2
- data/app/views/decidim/initiatives/create_initiative/promotal_committee.html.erb +3 -3
- data/app/views/decidim/initiatives/create_initiative/select_initiative_type.html.erb +37 -22
- data/app/views/decidim/initiatives/initiative_signatures/_wizard_steps.html.erb +19 -0
- data/app/views/decidim/initiatives/initiative_signatures/fill_personal_data.html.erb +43 -0
- data/app/views/decidim/initiatives/initiative_signatures/finish.html.erb +17 -0
- data/app/views/decidim/initiatives/initiative_signatures/sms_code.html.erb +22 -0
- data/app/views/decidim/initiatives/initiative_signatures/sms_phone_number.html.erb +22 -0
- data/app/views/decidim/initiatives/initiative_signatures/update_buttons_and_counters.js.erb +21 -0
- data/app/views/decidim/initiatives/initiative_votes/update_buttons_and_counters.js.erb +1 -1
- data/app/views/decidim/initiatives/initiatives/_author.html.erb +1 -1
- data/app/views/decidim/initiatives/initiatives/_interactions.html.erb +11 -0
- data/app/views/decidim/initiatives/initiatives/_progress_bar.html.erb +9 -0
- data/app/views/decidim/initiatives/initiatives/_result.html.erb +3 -3
- data/app/views/decidim/initiatives/initiatives/_vote_button.html.erb +39 -18
- data/app/views/decidim/initiatives/initiatives/_vote_cabin.html.erb +9 -9
- data/app/views/decidim/initiatives/initiatives/show.html.erb +12 -12
- data/app/views/decidim/initiatives/initiatives/signature_identities.html.erb +19 -9
- data/app/views/decidim/initiatives/initiatives_type_signature_types/search.html.erb +1 -0
- data/app/views/layouts/decidim/_initiative_creation_header.html.erb +24 -34
- data/app/views/layouts/decidim/_initiative_signature_creation_header.html.erb +27 -0
- data/app/views/layouts/decidim/admin/initiative.html.erb +22 -15
- data/app/views/layouts/decidim/admin/initiatives_votes.pdf.erb +11 -0
- data/app/views/layouts/decidim/initiative_creation.html.erb +15 -3
- data/app/views/layouts/decidim/initiative_signature_creation.html.erb +12 -0
- data/config/initializers/wicked_pdf.rb +23 -0
- data/config/locales/ar-SA.yml +138 -7
- data/config/locales/ca.yml +120 -28
- data/config/locales/cs-CZ.yml +103 -9
- data/config/locales/cs.yml +494 -0
- data/config/locales/de.yml +101 -9
- data/config/locales/en.yml +133 -40
- data/config/locales/es-MX.yml +101 -9
- data/config/locales/es-PY.yml +101 -9
- data/config/locales/es.yml +124 -32
- data/config/locales/eu.yml +101 -9
- data/config/locales/fi-pl.yml +101 -9
- data/config/locales/fi-plain.yml +478 -0
- data/config/locales/fi.yml +117 -25
- data/config/locales/fr.yml +102 -10
- data/config/locales/gl.yml +101 -9
- data/config/locales/hu.yml +102 -10
- data/config/locales/id-ID.yml +100 -9
- data/config/locales/it.yml +101 -9
- data/config/locales/nl.yml +101 -9
- data/config/locales/pl.yml +104 -10
- data/config/locales/pt-BR.yml +101 -9
- data/config/locales/pt.yml +101 -9
- data/config/locales/ru.yml +4 -9
- data/config/locales/sv.yml +101 -9
- data/config/locales/tr-TR.yml +101 -9
- data/config/locales/uk.yml +4 -9
- data/db/migrate/20181212154456_add_collect_extra_user_fields_to_initiatives_types.rb +7 -0
- data/db/migrate/20181212155125_add_online_signature_enabled_to_initiative_type.rb +7 -0
- data/db/migrate/20181212155740_add_extra_fields_legal_information_to_initiatives_types.rb +7 -0
- data/db/migrate/20181213184712_add_min_committee_members_to_initiative_type.rb +7 -0
- data/db/migrate/20181220134322_add_encrypted_metadata_to_decidim_initiatives_votes.rb +7 -0
- data/db/migrate/20181224100803_add_timestamp_to_decidim_initiatives_votes.rb +7 -0
- data/db/migrate/20181224101041_add_hash_id_to_decidim_initiatives_votes.rb +7 -0
- data/db/migrate/20190124170442_add_validate_sms_code_on_votes_to_initiatives_types.rb +7 -0
- data/db/migrate/20190125131847_add_document_number_authorization_handler_to_initiatives_types.rb +7 -0
- data/db/migrate/20190213184301_add_undo_online_signatures_enabled_to_initiatives_types.rb +7 -0
- data/lib/decidim/initiatives.rb +12 -0
- data/lib/decidim/initiatives/admin_engine.rb +12 -0
- data/lib/decidim/initiatives/engine.rb +3 -0
- data/lib/decidim/initiatives/participatory_space.rb +6 -1
- data/lib/decidim/initiatives/test/factories.rb +39 -0
- data/lib/decidim/initiatives/version.rb +1 -1
- metadata +115 -14
- data/app/views/decidim/initiatives/initiatives/_statistics.html.erb +0 -21
- data/app/views/decidim/initiatives/initiatives/_votes_count.html.erb +0 -12
@@ -0,0 +1,207 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Initiatives
|
5
|
+
require "wicked"
|
6
|
+
|
7
|
+
class InitiativeSignaturesController < Decidim::Initiatives::ApplicationController
|
8
|
+
layout "layouts/decidim/initiative_signature_creation"
|
9
|
+
|
10
|
+
include Wicked::Wizard
|
11
|
+
include Decidim::Initiatives::NeedsInitiative
|
12
|
+
include Decidim::FormFactory
|
13
|
+
|
14
|
+
prepend_before_action :set_wizard_steps
|
15
|
+
before_action :authenticate_user!
|
16
|
+
|
17
|
+
helper InitiativeHelper
|
18
|
+
|
19
|
+
helper_method :initiative_type, :extra_data_legal_information
|
20
|
+
|
21
|
+
# GET /initiatives/:initiative_id/initiative_signatures/:step
|
22
|
+
def show
|
23
|
+
group_id = params[:group_id] || (session[:initiative_vote_form] ||= {})["group_id"]
|
24
|
+
enforce_permission_to :sign_initiative, :initiative, initiative: current_initiative, group_id: group_id, signature_has_steps: signature_has_steps?
|
25
|
+
send("#{step}_step", initiative_vote_form: session[:initiative_vote_form])
|
26
|
+
end
|
27
|
+
|
28
|
+
# PUT /initiatives/:initiative_id/initiative_signatures/:step
|
29
|
+
def update
|
30
|
+
group_id = params.dig(:initiatives_vote, :group_id) || session[:initiative_vote_form]["group_id"]
|
31
|
+
enforce_permission_to :sign_initiative, :initiative, initiative: current_initiative, group_id: group_id, signature_has_steps: signature_has_steps?
|
32
|
+
send("#{step}_step", params)
|
33
|
+
end
|
34
|
+
|
35
|
+
# POST /initiatives/:initiative_id/initiative_signatures
|
36
|
+
def create
|
37
|
+
group_id = params[:group_id] || session[:initiative_vote_form]&.dig("group_id")
|
38
|
+
enforce_permission_to :vote, :initiative, initiative: current_initiative, group_id: group_id
|
39
|
+
@form = form(Decidim::Initiatives::VoteForm)
|
40
|
+
.from_params(
|
41
|
+
initiative_id: current_initiative.id,
|
42
|
+
author_id: current_user.id,
|
43
|
+
group_id: group_id
|
44
|
+
)
|
45
|
+
|
46
|
+
VoteInitiative.call(@form, current_user) do
|
47
|
+
on(:ok) do
|
48
|
+
current_initiative.reload
|
49
|
+
render :update_buttons_and_counters
|
50
|
+
end
|
51
|
+
|
52
|
+
on(:invalid) do
|
53
|
+
render json: {
|
54
|
+
error: I18n.t("create.error", scope: "decidim.initiatives.initiative_votes")
|
55
|
+
}, status: 422
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def fill_personal_data_step(_unused)
|
63
|
+
@form = form(Decidim::Initiatives::VoteForm)
|
64
|
+
.from_params(
|
65
|
+
initiative_id: current_initiative.id,
|
66
|
+
author_id: current_user.id,
|
67
|
+
group_id: params[:group_id]
|
68
|
+
)
|
69
|
+
session[:initiative_vote_form] = { group_id: @form.group_id }
|
70
|
+
skip_step unless initiative_type.collect_user_extra_fields
|
71
|
+
render_wizard
|
72
|
+
end
|
73
|
+
|
74
|
+
def sms_phone_number_step(parameters)
|
75
|
+
if parameters.has_key?(:initiatives_vote) || !fill_personal_data_step?
|
76
|
+
build_vote_form(parameters)
|
77
|
+
else
|
78
|
+
check_session_personal_data
|
79
|
+
end
|
80
|
+
clear_session_sms_code
|
81
|
+
|
82
|
+
if @vote_form.invalid?
|
83
|
+
flash[:alert] = I18n.t("personal_data.invalid", scope: "decidim.initiatives.initiative_votes")
|
84
|
+
jump_to(previous_step)
|
85
|
+
end
|
86
|
+
|
87
|
+
@form = Decidim::Verifications::Sms::MobilePhoneForm.new
|
88
|
+
render_wizard
|
89
|
+
end
|
90
|
+
|
91
|
+
def sms_code_step(parameters)
|
92
|
+
check_session_personal_data if fill_personal_data_step?
|
93
|
+
@phone_form = Decidim::Verifications::Sms::MobilePhoneForm.from_params(parameters.merge(user: current_user))
|
94
|
+
@form = Decidim::Verifications::Sms::ConfirmationForm.new
|
95
|
+
render_wizard && return if session_sms_code.present?
|
96
|
+
|
97
|
+
ValidateMobilePhone.call(@phone_form, current_user) do
|
98
|
+
on(:ok) do |metadata|
|
99
|
+
store_session_sms_code(metadata)
|
100
|
+
render_wizard
|
101
|
+
end
|
102
|
+
|
103
|
+
on(:invalid) do
|
104
|
+
flash[:alert] = I18n.t("sms_phone.invalid", scope: "decidim.initiatives.initiative_votes")
|
105
|
+
redirect_to wizard_path(:sms_phone_number)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def finish_step(parameters)
|
111
|
+
if parameters.has_key?(:initiatives_vote) || !fill_personal_data_step?
|
112
|
+
build_vote_form(parameters)
|
113
|
+
else
|
114
|
+
check_session_personal_data
|
115
|
+
end
|
116
|
+
|
117
|
+
if sms_step?
|
118
|
+
@confirmation_code_form = Decidim::Verifications::Sms::ConfirmationForm.from_params(parameters)
|
119
|
+
|
120
|
+
ValidateSmsCode.call(@confirmation_code_form, session_sms_code) do
|
121
|
+
on(:ok) { clear_session_sms_code }
|
122
|
+
|
123
|
+
on(:invalid) do
|
124
|
+
flash[:alert] = I18n.t("sms_code.invalid", scope: "decidim.initiatives.initiative_votes")
|
125
|
+
jump_to :sms_code
|
126
|
+
render_wizard && return
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
VoteInitiative.call(@vote_form, current_user) do
|
132
|
+
on(:ok) do
|
133
|
+
session[:initiative_vote_form] = {}
|
134
|
+
end
|
135
|
+
|
136
|
+
on(:invalid) do |vote|
|
137
|
+
logger.fatal "Failed creating signature: #{vote.errors.full_messages.join(", ")}" if vote
|
138
|
+
flash[:alert] = I18n.t("create.invalid", scope: "decidim.initiatives.initiative_votes")
|
139
|
+
jump_to previous_step
|
140
|
+
end
|
141
|
+
end
|
142
|
+
render_wizard
|
143
|
+
end
|
144
|
+
|
145
|
+
def build_vote_form(parameters)
|
146
|
+
@vote_form = form(Decidim::Initiatives::VoteForm).from_params(parameters).tap do |form|
|
147
|
+
form.initiative_id = current_initiative.id
|
148
|
+
form.author_id = current_user.id
|
149
|
+
end
|
150
|
+
|
151
|
+
session[:initiative_vote_form] = session[:initiative_vote_form].merge(@vote_form.attributes_with_values)
|
152
|
+
end
|
153
|
+
|
154
|
+
def session_vote_form
|
155
|
+
raw_birth_date = session[:initiative_vote_form]["date_of_birth"]
|
156
|
+
return unless raw_birth_date
|
157
|
+
|
158
|
+
@vote_form = form(Decidim::Initiatives::VoteForm).from_params(
|
159
|
+
session[:initiative_vote_form].merge("date_of_birth" => Date.parse(raw_birth_date))
|
160
|
+
)
|
161
|
+
end
|
162
|
+
|
163
|
+
def initiative_type
|
164
|
+
@initiative_type ||= current_initiative&.scoped_type&.type
|
165
|
+
end
|
166
|
+
|
167
|
+
def extra_data_legal_information
|
168
|
+
@extra_data_legal_information ||= initiative_type.extra_fields_legal_information
|
169
|
+
end
|
170
|
+
|
171
|
+
def check_session_personal_data
|
172
|
+
return if session[:initiative_vote_form].present? && session_vote_form&.valid?
|
173
|
+
|
174
|
+
flash[:alert] = I18n.t("create.error", scope: "decidim.initiatives.initiative_votes")
|
175
|
+
jump_to(:fill_personal_data)
|
176
|
+
end
|
177
|
+
|
178
|
+
def store_session_sms_code(metadata)
|
179
|
+
session[:initiative_sms_code] = metadata
|
180
|
+
end
|
181
|
+
|
182
|
+
def session_sms_code
|
183
|
+
session[:initiative_sms_code]
|
184
|
+
end
|
185
|
+
|
186
|
+
def clear_session_sms_code
|
187
|
+
session[:initiative_sms_code] = {}
|
188
|
+
end
|
189
|
+
|
190
|
+
def sms_step?
|
191
|
+
current_initiative.validate_sms_code_on_votes?
|
192
|
+
end
|
193
|
+
|
194
|
+
def fill_personal_data_step?
|
195
|
+
initiative_type.collect_user_extra_fields?
|
196
|
+
end
|
197
|
+
|
198
|
+
def set_wizard_steps
|
199
|
+
initial_wizard_steps = [:finish]
|
200
|
+
initial_wizard_steps.unshift(:sms_phone_number, :sms_code) if sms_step?
|
201
|
+
initial_wizard_steps.unshift(:fill_personal_data) if fill_personal_data_step?
|
202
|
+
|
203
|
+
self.steps = initial_wizard_steps
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
@@ -5,6 +5,7 @@ module Decidim
|
|
5
5
|
# Exposes the initiative vote resource so users can vote initiatives.
|
6
6
|
class InitiativeVotesController < Decidim::Initiatives::ApplicationController
|
7
7
|
include Decidim::Initiatives::NeedsInitiative
|
8
|
+
include Decidim::FormFactory
|
8
9
|
|
9
10
|
before_action :authenticate_user!
|
10
11
|
|
@@ -13,7 +14,8 @@ module Decidim
|
|
13
14
|
# POST /initiatives/:initiative_id/initiative_vote
|
14
15
|
def create
|
15
16
|
enforce_permission_to :vote, :initiative, initiative: current_initiative, group_id: params[:group_id]
|
16
|
-
|
17
|
+
@form = form(Decidim::Initiatives::VoteForm).from_params(initiative_id: current_initiative.id, author_id: current_user.id, group_id: params[:group_id])
|
18
|
+
VoteInitiative.call(@form, current_user) do
|
17
19
|
on(:ok) do
|
18
20
|
current_initiative.reload
|
19
21
|
render :update_buttons_and_counters
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Initiatives
|
5
|
+
class InitiativesTypeSignatureTypesController < Decidim::Initiatives::ApplicationController
|
6
|
+
helper_method :allowed_signature_types_for_initiatives
|
7
|
+
|
8
|
+
# GET /initiative_type_signature_types/search
|
9
|
+
def search
|
10
|
+
enforce_permission_to :search, :initiative_type_signature_types
|
11
|
+
render layout: false
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def allowed_signature_types_for_initiatives
|
17
|
+
@allowed_signature_types_for_initiatives ||= InitiativesType.find(params[:type_id]).allowed_signature_types_for_initiatives
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Initiatives
|
5
|
+
module Admin
|
6
|
+
# A form object used to manage the initiative answer in the
|
7
|
+
# administration panel.
|
8
|
+
class InitiativeAnswerForm < Form
|
9
|
+
include TranslatableAttributes
|
10
|
+
|
11
|
+
mimic :initiative
|
12
|
+
|
13
|
+
translatable_attribute :answer, String
|
14
|
+
attribute :answer_url, String
|
15
|
+
attribute :signature_start_date, Decidim::Attributes::LocalizedDate
|
16
|
+
attribute :signature_end_date, Decidim::Attributes::LocalizedDate
|
17
|
+
|
18
|
+
validates :signature_start_date, :signature_end_date, presence: true, if: :signature_dates_required?
|
19
|
+
validates :signature_end_date, date: { after: :signature_start_date }, if: lambda { |form|
|
20
|
+
form.signature_start_date.present? && form.signature_end_date.present?
|
21
|
+
}
|
22
|
+
|
23
|
+
def signature_dates_required?
|
24
|
+
@signature_dates_required ||= context.initiative.state == "published"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -19,21 +19,16 @@ module Decidim
|
|
19
19
|
attribute :signature_end_date, Decidim::Attributes::LocalizedDate
|
20
20
|
attribute :hashtag, String
|
21
21
|
attribute :offline_votes, Integer
|
22
|
-
|
23
|
-
translatable_attribute :answer, String
|
24
|
-
attribute :answer_url, String
|
22
|
+
attribute :state, String
|
25
23
|
|
26
24
|
validates :title, :description, presence: true
|
27
|
-
validates :signature_type, presence: true
|
25
|
+
validates :signature_type, presence: true, if: :signature_type_updatable?
|
28
26
|
validates :signature_start_date, presence: true, if: ->(form) { form.context.initiative.published? }
|
29
27
|
validates :signature_end_date, presence: true, if: ->(form) { form.context.initiative.published? }
|
30
28
|
validates :signature_end_date, date: { after: :signature_start_date }, if: lambda { |form|
|
31
29
|
form.signature_start_date.present? && form.signature_end_date.present?
|
32
30
|
}
|
33
31
|
|
34
|
-
validates :answer, translatable_presence: true, if: ->(form) { form.context.initiative.accepted? }
|
35
|
-
validates :answer_url, presence: true, if: ->(form) { form.context.initiative.accepted? }
|
36
|
-
|
37
32
|
validates :offline_votes,
|
38
33
|
numericality: {
|
39
34
|
only_integer: true,
|
@@ -44,6 +39,29 @@ module Decidim
|
|
44
39
|
self.type_id = model.type.id
|
45
40
|
self.decidim_scope_id = model.scope.id
|
46
41
|
end
|
42
|
+
|
43
|
+
def signature_type_updatable?
|
44
|
+
@signature_type_updatable ||= begin
|
45
|
+
state ||= context.initiative.state
|
46
|
+
state == "validating" && context.current_user.admin? || state == "created"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def state_updatable?
|
51
|
+
false
|
52
|
+
end
|
53
|
+
|
54
|
+
def scoped_type_id
|
55
|
+
return unless type && decidim_scope_id
|
56
|
+
|
57
|
+
type.scopes.find_by!(decidim_scopes_id: decidim_scope_id).id
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def type
|
63
|
+
@type ||= type_id ? Decidim::InitiativesType.find(type_id) : context.initiative.type
|
64
|
+
end
|
47
65
|
end
|
48
66
|
end
|
49
67
|
end
|
@@ -12,11 +12,25 @@ module Decidim
|
|
12
12
|
translatable_attribute :title, String
|
13
13
|
translatable_attribute :description, String
|
14
14
|
attribute :banner_image, String
|
15
|
+
attribute :online_signature_enabled, Boolean
|
16
|
+
attribute :undo_online_signatures_enabled, Boolean
|
17
|
+
attribute :minimum_committee_members, Integer
|
18
|
+
attribute :collect_user_extra_fields, Boolean
|
19
|
+
translatable_attribute :extra_fields_legal_information, String
|
20
|
+
attribute :validate_sms_code_on_votes, Boolean
|
21
|
+
attribute :document_number_authorization_handler, String
|
15
22
|
|
16
23
|
validates :title, :description, translatable_presence: true
|
24
|
+
validates :online_signature_enabled, inclusion: { in: [true, false] }
|
25
|
+
validates :undo_online_signatures_enabled, inclusion: { in: [true, false] }
|
26
|
+
validates :minimum_committee_members, numericality: { only_integer: true }, allow_nil: true
|
17
27
|
validates :banner_image, presence: true, if: lambda { |form|
|
18
28
|
form.context.initiative_type.nil?
|
19
29
|
}
|
30
|
+
|
31
|
+
def minimum_committee_members=(value)
|
32
|
+
super(value.presence)
|
33
|
+
end
|
20
34
|
end
|
21
35
|
end
|
22
36
|
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Initiatives
|
5
|
+
# A form object used to collect the data for a new initiative committee
|
6
|
+
# member.
|
7
|
+
class CommitteeMemberForm < Form
|
8
|
+
mimic :initiatives_committee_member
|
9
|
+
|
10
|
+
attribute :initiative_id, Integer
|
11
|
+
attribute :user_id, Integer
|
12
|
+
attribute :state, String
|
13
|
+
|
14
|
+
validates :initiative_id, presence: true
|
15
|
+
validates :user_id, presence: true
|
16
|
+
validates :state, inclusion: { in: %w(requested rejected accepted) }, unless: :user_is_author?
|
17
|
+
validates :state, inclusion: { in: %w(rejected accepted) }, if: :user_is_author?
|
18
|
+
|
19
|
+
def user_is_author?
|
20
|
+
initiative&.decidim_author_id == user_id
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def initiative
|
26
|
+
@initiative ||= Decidim::Initiative.find_by(id: initiative_id)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -14,6 +14,7 @@ module Decidim
|
|
14
14
|
attribute :scope_id, Integer
|
15
15
|
attribute :decidim_user_group_id, Integer
|
16
16
|
attribute :signature_type, String
|
17
|
+
attribute :state, String
|
17
18
|
|
18
19
|
validates :title, :description, presence: true
|
19
20
|
validates :title, length: { maximum: 150 }
|
@@ -25,6 +26,10 @@ module Decidim
|
|
25
26
|
self.type_id = model.type.id
|
26
27
|
self.scope_id = model.scope.id
|
27
28
|
end
|
29
|
+
|
30
|
+
def signature_type_updatable?
|
31
|
+
state == "created" || state.nil?
|
32
|
+
end
|
28
33
|
end
|
29
34
|
end
|
30
35
|
end
|
@@ -0,0 +1,151 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "virtus/multiparams"
|
4
|
+
|
5
|
+
module Decidim
|
6
|
+
module Initiatives
|
7
|
+
# A form object used to collect the data for a new initiative.
|
8
|
+
class VoteForm < Form
|
9
|
+
include TranslatableAttributes
|
10
|
+
include Virtus::Multiparams
|
11
|
+
|
12
|
+
mimic :initiatives_vote
|
13
|
+
|
14
|
+
attribute :name_and_surname, String
|
15
|
+
attribute :document_number, String
|
16
|
+
attribute :date_of_birth, Date
|
17
|
+
|
18
|
+
attribute :postal_code, String
|
19
|
+
attribute :encrypted_metadata, String
|
20
|
+
attribute :hash_id, String
|
21
|
+
|
22
|
+
attribute :initiative_id, Integer
|
23
|
+
attribute :author_id, Integer
|
24
|
+
attribute :group_id, Integer
|
25
|
+
|
26
|
+
validates :name_and_surname, :document_number, :date_of_birth, :postal_code, presence: true, if: :required_personal_data?
|
27
|
+
validates :encrypted_metadata, presence: true, if: :required_personal_data?
|
28
|
+
validates :initiative_id, presence: true
|
29
|
+
validates :author_id, presence: true
|
30
|
+
|
31
|
+
validate :document_number_authorized, if: :required_personal_data?
|
32
|
+
validate :document_number_uniqueness, if: :required_personal_data?
|
33
|
+
validate :personal_data_consistent_with_metadata, if: :required_personal_data?
|
34
|
+
|
35
|
+
def initiative
|
36
|
+
@initiative ||= Decidim::Initiative.find_by(id: initiative_id)
|
37
|
+
end
|
38
|
+
|
39
|
+
delegate :scope, to: :initiative
|
40
|
+
|
41
|
+
def metadata
|
42
|
+
{ name_and_surname: name_and_surname,
|
43
|
+
document_number: document_number,
|
44
|
+
date_of_birth: date_of_birth,
|
45
|
+
postal_code: postal_code }
|
46
|
+
end
|
47
|
+
|
48
|
+
def encrypted_metadata
|
49
|
+
@encrypted_metadata ||= encrypt_metadata
|
50
|
+
end
|
51
|
+
|
52
|
+
def hash_id
|
53
|
+
Digest::MD5.hexdigest(
|
54
|
+
"#{initiative_id}-#{document_number || author_id}-#{Rails.application.secrets.secret_key_base}"
|
55
|
+
)
|
56
|
+
end
|
57
|
+
|
58
|
+
def decrypted_metadata
|
59
|
+
return unless encrypted_metadata
|
60
|
+
|
61
|
+
encryptor.decrypt(encrypted_metadata)
|
62
|
+
end
|
63
|
+
|
64
|
+
protected
|
65
|
+
|
66
|
+
def required_personal_data?
|
67
|
+
initiative_type&.collect_user_extra_fields?
|
68
|
+
end
|
69
|
+
|
70
|
+
def initiative_type
|
71
|
+
@initiative_type ||= initiative&.scoped_type&.type
|
72
|
+
end
|
73
|
+
|
74
|
+
def encryptor
|
75
|
+
@encryptor ||= DataEncryptor.new(secret: "personal user metadata")
|
76
|
+
end
|
77
|
+
|
78
|
+
def encrypt_metadata
|
79
|
+
return unless required_personal_data?
|
80
|
+
|
81
|
+
encryptor.encrypt(metadata)
|
82
|
+
end
|
83
|
+
|
84
|
+
def document_number_authorized
|
85
|
+
return if initiative.document_number_authorization_handler.blank?
|
86
|
+
|
87
|
+
errors.add(:document_number, :invalid) unless authorized? && authorization_handler && authorization.unique_id == authorization_handler.unique_id
|
88
|
+
end
|
89
|
+
|
90
|
+
def document_number_uniqueness
|
91
|
+
errors.add(:document_number, :taken) if initiative.votes.where(hash_id: hash_id).exists?
|
92
|
+
end
|
93
|
+
|
94
|
+
def personal_data_consistent_with_metadata
|
95
|
+
return if initiative.document_number_authorization_handler.blank?
|
96
|
+
|
97
|
+
errors.add(:base, :invalid) unless authorized? &&
|
98
|
+
authorization_handler &&
|
99
|
+
authorization_handler_metadata_variations.any? { |variation| authorization.metadata.symbolize_keys == variation.symbolize_keys }
|
100
|
+
end
|
101
|
+
|
102
|
+
def author
|
103
|
+
@author ||= current_organization.users.find_by(id: author_id)
|
104
|
+
end
|
105
|
+
|
106
|
+
def authorization
|
107
|
+
return unless author && handler_name
|
108
|
+
|
109
|
+
@authorization ||= Verifications::Authorizations.new(organization: author.organization, user: author, name: handler_name).first
|
110
|
+
end
|
111
|
+
|
112
|
+
def authorization_status
|
113
|
+
return unless authorization
|
114
|
+
|
115
|
+
Decidim::Verifications::Adapter.from_element(handler_name).authorize(authorization, {}, nil, nil)
|
116
|
+
end
|
117
|
+
|
118
|
+
def authorized?
|
119
|
+
authorization_status&.first == :ok
|
120
|
+
end
|
121
|
+
|
122
|
+
def handler_name
|
123
|
+
initiative.document_number_authorization_handler
|
124
|
+
end
|
125
|
+
|
126
|
+
def authorization_handler
|
127
|
+
return unless document_number && handler_name
|
128
|
+
|
129
|
+
@authorization_handler ||= Decidim::AuthorizationHandler.handler_for(handler_name,
|
130
|
+
document_number: document_number,
|
131
|
+
name_and_surname: name_and_surname,
|
132
|
+
date_of_birth: date_of_birth,
|
133
|
+
postal_code: postal_code,
|
134
|
+
scope_id: scope&.id)
|
135
|
+
end
|
136
|
+
|
137
|
+
def authorization_handler_metadata_variations
|
138
|
+
return [] unless authorization_handler && scope.present?
|
139
|
+
|
140
|
+
scope.children.map do |child_scope|
|
141
|
+
Decidim::AuthorizationHandler.handler_for(handler_name,
|
142
|
+
document_number: document_number,
|
143
|
+
name_and_surname: name_and_surname,
|
144
|
+
date_of_birth: date_of_birth,
|
145
|
+
postal_code: postal_code,
|
146
|
+
scope_id: child_scope&.id)
|
147
|
+
end.unshift(authorization_handler).map(&:metadata)
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|