decidim-initiatives 0.30.2 → 0.31.0.rc1

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.
Files changed (146) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +46 -9
  3. data/app/cells/decidim/initiatives/content_blocks/highlighted_initiatives_settings_form/show.erb +7 -2
  4. data/app/cells/decidim/initiatives/initiative_g_cell.rb +5 -1
  5. data/app/commands/decidim/initiatives/admin/publish_initiative.rb +1 -5
  6. data/app/commands/decidim/initiatives/admin/update_initiative.rb +1 -2
  7. data/app/commands/decidim/initiatives/create_initiative.rb +0 -1
  8. data/app/commands/decidim/initiatives/update_initiative.rb +1 -3
  9. data/app/commands/decidim/initiatives/vote_initiative.rb +1 -11
  10. data/app/controllers/concerns/decidim/initiatives/has_signature_workflow.rb +36 -0
  11. data/app/controllers/concerns/decidim/initiatives/needs_initiative.rb +1 -12
  12. data/app/controllers/decidim/initiatives/admin/initiatives_controller.rb +2 -2
  13. data/app/controllers/decidim/initiatives/admin/initiatives_settings_controller.rb +1 -1
  14. data/app/controllers/decidim/initiatives/admin/initiatives_type_scopes_controller.rb +2 -2
  15. data/app/controllers/decidim/initiatives/admin/initiatives_types_controller.rb +2 -2
  16. data/app/controllers/decidim/initiatives/committee_requests_controller.rb +10 -2
  17. data/app/controllers/decidim/initiatives/create_initiative_controller.rb +84 -18
  18. data/app/controllers/decidim/initiatives/initiative_signatures_controller.rb +133 -42
  19. data/app/controllers/decidim/initiatives/initiative_votes_controller.rb +3 -2
  20. data/app/controllers/decidim/initiatives/initiatives_controller.rb +21 -2
  21. data/app/forms/decidim/initiatives/admin/initiative_form.rb +0 -1
  22. data/app/forms/decidim/initiatives/initiative_form.rb +0 -3
  23. data/app/helpers/decidim/initiatives/application_helper.rb +2 -0
  24. data/app/helpers/decidim/initiatives/initiatives_helper.rb +0 -1
  25. data/app/models/decidim/initiative.rb +7 -31
  26. data/app/models/decidim/initiatives_committee_member.rb +1 -1
  27. data/app/models/decidim/initiatives_type.rb +5 -2
  28. data/app/models/decidim/initiatives_vote.rb +2 -2
  29. data/app/packs/entrypoints/decidim_initiatives.js +1 -1
  30. data/app/packs/entrypoints/decidim_initiatives_admin.scss +1 -1
  31. data/app/packs/src/decidim/initiatives/admin/initiatives_types.js +2 -11
  32. data/app/packs/src/decidim/initiatives/admin/invite_users.js +1 -1
  33. data/app/packs/src/decidim/initiatives/application.js +1 -1
  34. data/app/packs/src/decidim/initiatives/check_code.js +114 -0
  35. data/app/packs/src/decidim/initiatives/initiative_creation_wizard.js +16 -0
  36. data/app/packs/src/decidim/initiatives/scoped_type.js +1 -1
  37. data/app/packs/stylesheets/initiatives.scss +16 -2
  38. data/app/permissions/decidim/initiatives/admin/permissions.rb +1 -4
  39. data/app/permissions/decidim/initiatives/permissions.rb +26 -16
  40. data/app/presenters/decidim/initiative_presenter.rb +12 -6
  41. data/app/presenters/decidim/initiatives/admin_log/initiative_presenter.rb +1 -2
  42. data/app/queries/decidim/initiatives/initiatives_stats_followers_count.rb +14 -0
  43. data/app/queries/decidim/initiatives/initiatives_stats_participants_count.rb +14 -0
  44. data/app/serializers/decidim/initiatives/open_data_initiative_serializer.rb +0 -1
  45. data/app/services/decidim/initiatives/data_encryptor.rb +1 -1
  46. data/app/services/decidim/initiatives/legacy_signature_handler.rb +25 -0
  47. data/app/services/decidim/initiatives/progress_notifier.rb +1 -7
  48. data/app/services/decidim/initiatives/signature_handler.rb +248 -0
  49. data/app/services/decidim/initiatives/status_change_notifier.rb +1 -7
  50. data/app/views/decidim/initiatives/admin/committee_requests/index.html.erb +29 -11
  51. data/app/views/decidim/initiatives/admin/exports/_dropdown.html.erb +17 -20
  52. data/app/views/decidim/initiatives/admin/initiatives/_form.html.erb +7 -13
  53. data/app/views/decidim/initiatives/admin/initiatives/_initiative_attachments.erb +2 -2
  54. data/app/views/decidim/initiatives/admin/initiatives/index.html.erb +76 -47
  55. data/app/views/decidim/initiatives/admin/initiatives_types/_form.html.erb +13 -21
  56. data/app/views/decidim/initiatives/admin/initiatives_types/_initiative_type_scopes.html.erb +28 -12
  57. data/app/views/decidim/initiatives/admin/initiatives_types/index.html.erb +33 -15
  58. data/app/views/decidim/initiatives/create_initiative/_committee_member.html.erb +27 -0
  59. data/app/views/decidim/initiatives/create_initiative/_return_to_initiatives_button.html.erb +3 -0
  60. data/app/views/decidim/initiatives/create_initiative/_send_to_technical_validation_button.html.erb +10 -0
  61. data/app/views/decidim/initiatives/create_initiative/_share_committee_link.html.erb +5 -1
  62. data/app/views/decidim/initiatives/create_initiative/fill_data.html.erb +7 -11
  63. data/app/views/decidim/initiatives/create_initiative/finish.html.erb +16 -13
  64. data/app/views/decidim/initiatives/create_initiative/promotal_committee.html.erb +33 -6
  65. data/app/views/decidim/initiatives/create_initiative/select_initiative_type.html.erb +40 -26
  66. data/app/views/decidim/initiatives/initiative_signatures/_sms_code_form.html.erb +22 -0
  67. data/app/views/decidim/initiatives/initiative_signatures/_sms_phone_number_form.html.erb +13 -0
  68. data/app/views/decidim/initiatives/initiative_signatures/fill_personal_data.html.erb +23 -22
  69. data/app/views/decidim/initiatives/initiative_signatures/finish.html.erb +17 -5
  70. data/app/views/decidim/initiatives/initiative_signatures/sms_code.html.erb +6 -8
  71. data/app/views/decidim/initiatives/initiative_signatures/sms_phone_number.html.erb +3 -8
  72. data/app/views/decidim/initiatives/initiative_signatures/update_buttons_and_counters.js.erb +3 -14
  73. data/app/views/decidim/initiatives/initiative_votes/update_buttons_and_counters.js.erb +3 -14
  74. data/app/views/decidim/initiatives/initiatives/_committee_members.html.erb +1 -1
  75. data/app/views/decidim/initiatives/initiatives/_form.html.erb +1 -3
  76. data/app/views/decidim/initiatives/initiatives/_new_initiative_button.html.erb +10 -3
  77. data/app/views/decidim/initiatives/initiatives/_pending_initiatives.html.erb +5 -0
  78. data/app/views/decidim/initiatives/initiatives/index.html.erb +8 -0
  79. data/app/views/decidim/initiatives/initiatives/show.html.erb +2 -2
  80. data/app/views/layouts/decidim/_initiative_signature_creation_header.html.erb +20 -2
  81. data/app/views/layouts/decidim/admin/_manage_initiatives.html.erb +1 -1
  82. data/app/views/layouts/decidim/initiative_signature_creation.html.erb +3 -1
  83. data/config/assets.rb +2 -2
  84. data/config/locales/ar.yml +0 -45
  85. data/config/locales/bg.yml +0 -54
  86. data/config/locales/ca-IT.yml +99 -51
  87. data/config/locales/ca.yml +99 -51
  88. data/config/locales/cs.yml +93 -54
  89. data/config/locales/de.yml +99 -51
  90. data/config/locales/el.yml +0 -45
  91. data/config/locales/en.yml +99 -51
  92. data/config/locales/es-MX.yml +99 -51
  93. data/config/locales/es-PY.yml +99 -51
  94. data/config/locales/es.yml +99 -51
  95. data/config/locales/eu.yml +99 -51
  96. data/config/locales/fi-plain.yml +99 -51
  97. data/config/locales/fi.yml +99 -51
  98. data/config/locales/fr-CA.yml +44 -51
  99. data/config/locales/fr.yml +44 -51
  100. data/config/locales/ga-IE.yml +0 -17
  101. data/config/locales/gl.yml +0 -41
  102. data/config/locales/hu.yml +0 -54
  103. data/config/locales/id-ID.yml +0 -40
  104. data/config/locales/is-IS.yml +0 -22
  105. data/config/locales/it.yml +0 -53
  106. data/config/locales/ja.yml +98 -49
  107. data/config/locales/lb.yml +0 -50
  108. data/config/locales/lt.yml +0 -56
  109. data/config/locales/lv.yml +0 -46
  110. data/config/locales/nl.yml +0 -47
  111. data/config/locales/no.yml +0 -53
  112. data/config/locales/pl.yml +0 -56
  113. data/config/locales/pt-BR.yml +0 -53
  114. data/config/locales/pt.yml +0 -53
  115. data/config/locales/ro-RO.yml +92 -50
  116. data/config/locales/ru.yml +0 -25
  117. data/config/locales/sk.yml +0 -43
  118. data/config/locales/sl.yml +0 -1
  119. data/config/locales/sv.yml +10 -53
  120. data/config/locales/tr-TR.yml +0 -53
  121. data/config/locales/uk.yml +0 -25
  122. data/config/locales/zh-CN.yml +0 -45
  123. data/config/locales/zh-TW.yml +0 -53
  124. data/db/migrate/20250605104500_remove_hashtag_column_initiatives.rb +7 -0
  125. data/lib/decidim/api/initiative_api_type.rb +3 -0
  126. data/lib/decidim/api/initiative_type.rb +23 -4
  127. data/lib/decidim/exporters/initiative_votes_pdf.rb +1 -1
  128. data/lib/decidim/initiatives/default_signature_authorizer.rb +17 -0
  129. data/lib/decidim/initiatives/engine.rb +17 -14
  130. data/lib/decidim/initiatives/participatory_space.rb +15 -1
  131. data/lib/decidim/initiatives/seeds.rb +1 -2
  132. data/lib/decidim/initiatives/signature_workflow_manifest.rb +176 -0
  133. data/lib/decidim/initiatives/signatures.rb +12 -0
  134. data/lib/decidim/initiatives/test/factories.rb +7 -7
  135. data/lib/decidim/initiatives/test/initiatives_signatures_test_helpers.rb +19 -0
  136. data/lib/decidim/initiatives/validatable_authorizations.rb +83 -0
  137. data/lib/decidim/initiatives/version.rb +1 -1
  138. data/lib/decidim/initiatives.rb +23 -12
  139. metadata +33 -21
  140. data/app/events/decidim/initiatives/endorse_initiative_event.rb +0 -13
  141. data/app/forms/decidim/initiatives/vote_form.rb +0 -208
  142. data/app/packs/src/decidim/initiatives/identity_selector_dialog.js +0 -14
  143. data/app/services/decidim/initiatives/pdf_signature_example.rb +0 -110
  144. data/app/views/decidim/initiatives/initiative_signatures/_wizard_steps.html.erb +0 -15
  145. data/app/views/decidim/initiatives/initiatives/_interactions.html.erb +0 -10
  146. data/app/views/layouts/decidim/_initiative_header.html.erb +0 -27
@@ -22,6 +22,7 @@ module Decidim
22
22
 
23
23
  resources :create_initiative do
24
24
  collection do
25
+ get :load_initiative_draft
25
26
  get :select_initiative_type
26
27
  put :select_initiative_type, to: "create_initiative#store_initiative_type"
27
28
  get :fill_data
@@ -60,6 +61,7 @@ module Decidim
60
61
  get :authorization_create_modal, to: "authorization_create_modals#show"
61
62
  get :print, to: "initiatives#print", as: "print"
62
63
  get :send_to_technical_validation, to: "initiatives#send_to_technical_validation"
64
+ delete :discard, to: "initiatives#discard"
63
65
  end
64
66
 
65
67
  resource :initiative_vote, only: [:create, :destroy]
@@ -99,6 +101,15 @@ module Decidim
99
101
  Decidim.icons.register(name: "forbid-line", icon: "forbid-line", category: "system", description: "", engine: :initiatives)
100
102
  end
101
103
 
104
+ initializer "decidim_initiatives.stats" do
105
+ Decidim.stats.register :initiatives_count,
106
+ priority: StatsRegistry::HIGH_PRIORITY,
107
+ icon_name: "lightbulb-flash-line",
108
+ tooltip_key: "initiatives_count_tooltip" do |organization, _start_at, _end_at|
109
+ Decidim::Initiative.where(organization:).public_spaces.count
110
+ end
111
+ end
112
+
102
113
  initializer "decidim_initiatives.content_blocks" do
103
114
  Decidim::Initiatives::ContentBlocks::RegistryManager.register!
104
115
  end
@@ -118,20 +129,12 @@ module Decidim
118
129
  Decidim::Gamification.register_badge(:initiatives) do |badge|
119
130
  badge.levels = [1, 5, 15, 30, 50]
120
131
 
121
- badge.valid_for = [:user, :user_group]
132
+ badge.valid_for = [:user]
122
133
 
123
134
  badge.reset = lambda { |model|
124
- case model
125
- when User
126
- Decidim::Initiative.where(
127
- author: model,
128
- user_group: nil
129
- ).published.count
130
- when UserGroup
131
- Decidim::Initiative.where(
132
- user_group: model
133
- ).published.count
134
- end
135
+ Decidim::Initiative.where(
136
+ author: model
137
+ ).published.count
135
138
  }
136
139
  end
137
140
  end
@@ -140,7 +143,7 @@ module Decidim
140
143
  Decidim::Api::QueryType.include QueryExtensions
141
144
  end
142
145
 
143
- initializer "decidim_initiatives.webpacker.assets_path" do
146
+ initializer "decidim_initiatives.shakapacker.assets_path" do
144
147
  Decidim.register_assets_path File.expand_path("app/packs", root)
145
148
  end
146
149
 
@@ -149,7 +152,7 @@ module Decidim
149
152
  # We need to make sure we call `Preview.all` before requiring our
150
153
  # previews, otherwise any previews the app attempts to add need to be
151
154
  # manually required.
152
- if Rails.env.development? || Rails.env.test?
155
+ if Rails.env.local?
153
156
  ActionMailer::Preview.all
154
157
 
155
158
  Dir[root.join("spec/mailers/previews/**/*_preview.rb")].each do |file|
@@ -32,7 +32,21 @@ Decidim.register_participatory_space(:initiatives) do |participatory_space|
32
32
 
33
33
  participatory_space.register_resource(:initiatives_type) do |resource|
34
34
  resource.model_class_name = "Decidim::InitiativesType"
35
- resource.actions = %w(vote create)
35
+ resource.actions = %w(create)
36
+ end
37
+
38
+ participatory_space.register_stat :followers_count,
39
+ priority: Decidim::StatsRegistry::MEDIUM_PRIORITY,
40
+ icon_name: "user-follow-line",
41
+ tooltip_key: "followers_count_tooltip" do
42
+ Decidim::Initiatives::InitiativesStatsFollowersCount.for(participatory_space)
43
+ end
44
+
45
+ participatory_space.register_stat :participants_count,
46
+ priority: Decidim::StatsRegistry::MEDIUM_PRIORITY,
47
+ icon_name: "user-line",
48
+ tooltip_key: "participants_count_tooltip" do
49
+ Decidim::Initiatives::InitiativesStatsParticipantsCount.for(participatory_space)
36
50
  end
37
51
 
38
52
  participatory_space.model_class_name = "Decidim::Initiative"
@@ -9,7 +9,7 @@ module Decidim
9
9
  def call
10
10
  create_content_block!
11
11
 
12
- 3.times do |_n|
12
+ number_of_records.times do |_n|
13
13
  type = create_initiative_type!
14
14
 
15
15
  organization.top_scopes.each do |scope|
@@ -19,7 +19,6 @@ module Decidim
19
19
 
20
20
  Decidim::Initiative.states.keys.each do |state|
21
21
  Decidim::Initiative.skip_callback(:save, :after, :notify_state_change, raise: false)
22
- Decidim::Initiative.skip_callback(:create, :after, :notify_creation, raise: false)
23
22
 
24
23
  initiative = create_initiative!(state:)
25
24
 
@@ -0,0 +1,176 @@
1
+ # frozen_string_literal: true
2
+
3
+ # require "decidim/settings_manifest"
4
+ require "decidim/initiatives/default_signature_authorizer"
5
+
6
+ module Decidim
7
+ module Initiatives
8
+ # This class serves as a DSL to declaratively specify a signature method.
9
+ #
10
+ # To define a direct signature method, you need to specify the `form`
11
+ # attribute as a `Decidim::Form` that will be valid if the signature
12
+ # process is valid. If no form is provided the application assumes that the
13
+ # form class is `Decidim::Initiatives::SignatureHandler`
14
+ #
15
+ # All the attributes except the name are optional. If no attributes are set
16
+ # there are default values for them and the signature flow will be direct
17
+ # without steps and metadata stored in the initiatives_vote
18
+ #
19
+ # Signature workflow attributes:
20
+ # - :form (String) (optional) The name of the form object class responsible
21
+ # for collecting and validating the data to create the initiative votes.
22
+ # This class should be inherited from
23
+ # Decidim::Initiatives::SignatureHandler and can define a set of
24
+ # attributes to collect user personal data. If extra attributes are
25
+ # defined a collect personal data step will be enabled. This class also
26
+ # can define an unique_id to avoid duplicated votes based on the
27
+ # personal data and a metadata hash that will be encrypted in the vote
28
+ # creation. The class can also collect a hash of metadata that will be
29
+ # passed to an authorization handler if defined in the workflow. The
30
+ # class is also responsible to define the scopes associated with the
31
+ # votes. The main class by default detects the scopes candidates and
32
+ # the inherited class can define a signature_scope_id based on the data
33
+ # provided by the user.
34
+ # (default: "Decidim::Initiatives::SignatureHandler")
35
+ # - :authorization_handler_form (String) (optional) If this authorization
36
+ # handler class name is set the previous form will create and validate
37
+ # an instance of it from the params defined in it (by default the
38
+ # collected metadata and the user). If the authorization is invalid the
39
+ # signature is blocked. (default: nil)
40
+ # - :save_authorizations (Boolean) (optional) This option allows the
41
+ # workflow to save or update if already exist the authorization
42
+ # associated to the previous authorization handler. Note that if this
43
+ # option is set to false the user will be required to have been
44
+ # previously authorized with the method associated to the authorization
45
+ # handler. The authorization is searched by the organization, the user
46
+ # and the authorization handler name. If this class is not defined,
47
+ # class Decidim::Initiatives::DefaultSignatureAuthorizer is used,
48
+ # which inherits from Decidim::Verifications::DefaultActionAuthorizer
49
+ # and checks the authorization status. (default: true)
50
+ # - :ephemeral (Boolean) (optional) This option enables the possibility for
51
+ # users to sign without prior registration through an ephemeral session.
52
+ # To allow ephemeral sessions to be recovered o transferred to regular
53
+ # users authorizations must be stored in the process so an
54
+ # authorization_handler_form must be defined and the save_authorizations
55
+ # option must not be set to false. If those settings are not properly
56
+ # configured this option will be ignored and the workflow will not
57
+ # allow ephemeral sessions. (default: false)
58
+ # - :promote_authorization_validation_errors (Boolean) (optional) If set
59
+ # to true, errors in the personal data passed to the authorization
60
+ # handler form will be displayed next to the corresponding fields
61
+ # in the collection of personal data. Note that this option may provide
62
+ # information about personal data to the user. (default: false)
63
+ # - :action_authorizer (String) This is the name of the action authorizer
64
+ # class responsible for checking the status of the authorization
65
+ # associated to the signature. If this class is not defined, the
66
+ # authorization status will be ignored. You can define a handler
67
+ # inherited from Decidim::Initiatives::DefaultSignatureAuthorizer which
68
+ # inherits from Decidim::Verifications::DefaultActionAuthorizer and
69
+ # checks the authorization status with an instance initialized only
70
+ # with the authorization (without a component or a resource).
71
+ # (default: nil)
72
+ # - :sms_verification (Boolean) (optional) This option enables an
73
+ # additional SMS verification step. It uses by default the sms
74
+ # verification flow defined in decidim_verifications and expects the
75
+ # user to have previously created an SMS authorization validating
76
+ # their phone number. This behaviour can be changed defining the
77
+ # following attributes. (default: false)
78
+ # - :sms_mobile_phone_form_class (String) (optional) The name of the class
79
+ # responsible for starting the users phone verification. It uses the
80
+ # default form defined in decidim_initiatives.
81
+ # (default: "Decidim::Verifications::Sms::MobilePhoneForm")
82
+ # - :sms_mobile_phone_validator (String) (optional) The name of the command
83
+ # class responsible for checking if the mobile phone provided by the
84
+ # user has an authorization associated to the sms_mobile_form_class
85
+ # and delivering the sms code
86
+ # (default: "Decidim::Initiatives::ValidateMobilePhone")
87
+ # - :sms_code_validator_class (String) (optional) The name of the command
88
+ # class responsible for checking if the SMS code provided by the user
89
+ # is valid. (default: "Decidim::Initiatives::ValidateSmsCode")
90
+ #
91
+ # An example of declaration of a workflow:
92
+ #
93
+ # Decidim::Initiatives::Signatures.register_workflow(:dummy_signature_handler) do |workflow|
94
+ # workflow.form = "DummySignatureHandler"
95
+ # workflow.authorization_handler_form = "DummyAuthorizationHandler"
96
+ # workflow.action_authorizer = "DummySignatureHandler::DummySignatureActionAuthorizer"
97
+ # workflow.promote_authorization_validation_errors = true
98
+ # workflow.sms_verification = true
99
+ # end
100
+ #
101
+ class SignatureWorkflowManifest
102
+ include ActiveModel::Model
103
+ include Decidim::AttributeObject::Model
104
+
105
+ attribute :name, String
106
+ attribute :form, String
107
+ attribute :authorization_handler_form, String, default: nil
108
+ attribute :action_authorizer, String
109
+ attribute :save_authorizations, Boolean, default: true
110
+ attribute :promote_authorization_validation_errors, Boolean, default: false
111
+ attribute :ephemeral, Boolean, default: false
112
+ attribute :sms_verification, Boolean, default: false
113
+ attribute :sms_mobile_phone_form, String, default: nil
114
+ attribute :sms_mobile_phone_validator, String, default: nil
115
+ attribute :sms_code_validator, String, default: nil
116
+
117
+ validates :name, presence: true
118
+ validate :ephemeral_configuration
119
+
120
+ alias key name
121
+
122
+ def fullname
123
+ I18n.t("#{key}.name", scope: "decidim.signature_workflows", default: name.humanize)
124
+ end
125
+
126
+ def signature_form_class
127
+ form&.safe_constantize || Decidim::Initiatives::SignatureHandler
128
+ end
129
+
130
+ def action_authorizer_class
131
+ return if action_authorizer.blank?
132
+
133
+ action_authorizer.safe_constantize || DefaultSignatureAuthorizer
134
+ end
135
+
136
+ def authorization_handler_form_class
137
+ authorization_handler_form&.safe_constantize
138
+ end
139
+
140
+ # If no authorization handler is set or the save_authorizations option
141
+ # is disabled the workflow will not be able to save the user verification
142
+ # attributes necessary to recover the user session or transfer their
143
+ # activities
144
+ def ephemeral?
145
+ return if authorization_handler_form_class.blank? || !save_authorizations
146
+
147
+ ephemeral
148
+ end
149
+
150
+ def sms_mobile_phone_form_class
151
+ return unless sms_verification
152
+
153
+ sms_mobile_phone_form&.safe_constantize || Decidim::Verifications::Sms::MobilePhoneForm
154
+ end
155
+
156
+ def sms_mobile_phone_validator_class
157
+ return unless sms_verification
158
+
159
+ sms_mobile_phone_validator&.safe_constantize || Decidim::Initiatives::ValidateMobilePhone
160
+ end
161
+
162
+ def sms_code_validator_class
163
+ return unless sms_verification
164
+
165
+ sms_code_validator&.safe_constantize || Decidim::Initiatives::ValidateSmsCode
166
+ end
167
+
168
+ def ephemeral_configuration
169
+ return unless Rails.env.development?
170
+ return unless ephemeral
171
+
172
+ raise StandardError, "Wrong configuration of ephemeral signature workflow #{fullname}" if !save_authorizations || authorization_handler_form.blank?
173
+ end
174
+ end
175
+ end
176
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Initiatives
5
+ module Signatures
6
+ autoload :SignatureWorkflowManifest, "decidim/initiatives/signature_workflow_manifest"
7
+ include Decidim::HasWorkflows
8
+
9
+ def self.workflow_manifest_class = SignatureWorkflowManifest
10
+ end
11
+ end
12
+ end
@@ -84,13 +84,17 @@ FactoryBot.define do
84
84
  end
85
85
 
86
86
  trait :with_user_extra_fields_collection do
87
- collect_user_extra_fields { true }
88
87
  extra_fields_legal_information { generate_localized_description(:initiatives_type_extra_fields_legal_information, skip_injection:) }
89
- document_number_authorization_handler { "dummy_authorization_handler" }
88
+ document_number_authorization_handler { "dummy_signature_with_personal_data_handler" }
90
89
  end
91
90
 
92
91
  trait :with_sms_code_validation do
93
- validate_sms_code_on_votes { true }
92
+ document_number_authorization_handler { "dummy_signature_with_sms_handler" }
93
+ end
94
+
95
+ trait :with_sms_code_validation_and_user_extra_fields_collection do
96
+ extra_fields_legal_information { generate_localized_description(:initiatives_type_extra_fields_legal_information, skip_injection:) }
97
+ document_number_authorization_handler { "dummy_signature_handler" }
94
98
  end
95
99
 
96
100
  trait :child_scope_threshold_enabled do
@@ -268,10 +272,6 @@ FactoryBot.define do
268
272
  end
269
273
  initiative { create(:initiative, skip_injection:) }
270
274
  author { create(:user, :confirmed, organization: initiative.organization, skip_injection:) }
271
- decidim_user_group_id { create(:user_group, skip_injection:).id }
272
- after(:create) do |support, evaluator|
273
- create(:user_group_membership, user: support.author, user_group: Decidim::UserGroup.find(support.decidim_user_group_id), skip_injection: evaluator.skip_injection)
274
- end
275
275
  end
276
276
 
277
277
  factory :initiatives_committee_member, class: "Decidim::InitiativesCommitteeMember" do
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Initiatives
5
+ module InitiativesSignaturesTestHelpers
6
+ def fill_signature_date(date)
7
+ [date.year, date.month, date.day].each_with_index do |value, i|
8
+ within "select[name='dummy_signature_handler[date_of_birth(#{i + 1}i)]']" do
9
+ find("option[value='#{value}']").select_option
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
16
+
17
+ RSpec.configure do |config|
18
+ config.include Decidim::Initiatives::InitiativesSignaturesTestHelpers, type: :system
19
+ end
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Initiatives
5
+ # Concern to search for authorizations or try to create them and
6
+ # validate their status. If the authorization cannot be created and the
7
+ # workflow sets save_authorizations to false a new invalid authorization is
8
+ # used. This validation is not performed if no action_authorizer class name
9
+ # is set in the workflow.
10
+ module ValidatableAuthorizations
11
+ extend ActiveSupport::Concern
12
+
13
+ included do
14
+ validate :valid_authorization
15
+
16
+ delegate :action_authorizer, :save_authorizations, :action_authorizer_class, to: :workflow_manifest
17
+
18
+ def authorization_status
19
+ return unless authorization && action_authorizer_class
20
+
21
+ @authorization_status ||= action_authorizer_class.new(authorization, {}).authorize
22
+ end
23
+
24
+ def authorization
25
+ return unless user && authorization_handler_form_class
26
+
27
+ persisted_authorization = authorization_query.first
28
+ @authorization ||= if persisted_authorization.present? && persisted_authorization.unique_id == authorization_handler.unique_id
29
+ persisted_authorization
30
+ elsif save_authorizations
31
+ create_authorization
32
+ else
33
+ new_authorization
34
+ end
35
+ end
36
+
37
+ private
38
+
39
+ def valid_authorization
40
+ return if authorization_status.blank?
41
+ return if authorization_status.first == :ok
42
+
43
+ errors.add(:base, I18n.t("invalid_authorization", scope: "decidim.initiatives.initiative_signatures.fill_personal_data"))
44
+ end
45
+
46
+ def create_authorization
47
+ Decidim::Verifications::AuthorizeUser.call(authorization_handler, initiative.organization) do
48
+ on(:transferred) do |transfer|
49
+ self.transfer_status = transfer
50
+ end
51
+
52
+ on(:transfer_user) do |authorized_user|
53
+ self.user = authorized_user
54
+ self.transfer_status = :transfer_user
55
+ end
56
+
57
+ on(:invalid) do
58
+ return Decidim::Authorization.new
59
+ end
60
+ end
61
+
62
+ authorization_query.first
63
+ end
64
+
65
+ def authorization_query
66
+ Verifications::Authorizations.new(**authorization_params)
67
+ end
68
+
69
+ def new_authorization
70
+ Decidim::Authorization.new(created_at: Time.current, **authorization_params)
71
+ end
72
+
73
+ def authorization_params
74
+ {
75
+ organization: initiative.organization,
76
+ user:,
77
+ name: authorization_handler_form_class.handler_name
78
+ }
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
@@ -4,7 +4,7 @@ module Decidim
4
4
  # This holds the decidim-initiatives version.
5
5
  module Initiatives
6
6
  def self.version
7
- "0.30.2"
7
+ "0.31.0.rc1"
8
8
  end
9
9
  end
10
10
  end
@@ -5,6 +5,7 @@ require "decidim/initiatives/api"
5
5
  require "decidim/initiatives/engine"
6
6
  require "decidim/initiatives/admin_engine"
7
7
  require "decidim/initiatives/participatory_space"
8
+ require "decidim/initiatives/signatures"
8
9
 
9
10
  module Decidim
10
11
  module Exporters
@@ -14,79 +15,89 @@ module Decidim
14
15
  # Base module for the initiatives engine.
15
16
  module Initiatives
16
17
  autoload :ApplicationFormPDF, "decidim/initiatives/application_form_pdf"
18
+ autoload :ValidatableAuthorizations, "decidim/initiatives/validatable_authorizations"
17
19
 
18
20
  include ActiveSupport::Configurable
19
21
 
20
22
  # Public setting that defines whether creation is allowed to any validated
21
23
  # user or not. Defaults to true.
22
24
  config_accessor :creation_enabled do
23
- true
25
+ Decidim::Env.new("INITIATIVES_CREATION_ENABLED", "auto").present?
24
26
  end
25
27
 
26
28
  # Minimum number of committee members required to pass the initiative to
27
29
  # technical validation phase. Only applies to initiatives created by
28
30
  # individuals.
29
31
  config_accessor :minimum_committee_members do
30
- 2
32
+ Decidim::Env.new("INITIATIVES_MINIMUM_COMMITTEE_MEMBERS", 2).to_i
31
33
  end
32
34
 
33
35
  # Number of days available to collect supports after an initiative has been
34
36
  # published.
35
37
  config_accessor :default_signature_time_period_length do
36
- 120
38
+ Decidim::Env.new("INITIATIVES_DEFAULT_SIGNATURE_TIME_PERIOD_LENGTH", 120).to_i
37
39
  end
38
40
 
39
41
  # Components enabled for a new initiative
40
42
  config_accessor :default_components do
41
- [:pages, :meetings, :blogs]
43
+ Decidim::Env.new("INITIATIVES_DEFAULT_COMPONENTS", "pages, meetings, blogs").to_array
42
44
  end
43
45
 
44
46
  # Notifies when the given percentage of supports is reached for an
45
47
  # initiative.
46
48
  config_accessor :first_notification_percentage do
47
- 33
49
+ Decidim::Env.new("INITIATIVES_FIRST_NOTIFICATION_PERCENTAGE", 33).to_i
48
50
  end
49
51
 
50
52
  # Notifies when the given percentage of supports is reached for an
51
53
  # initiative.
52
54
  config_accessor :second_notification_percentage do
53
- 66
55
+ Decidim::Env.new("INITIATIVES_SECOND_NOTIFICATION_PERCENTAGE", 66).to_i
54
56
  end
55
57
 
56
58
  # Sets the expiration time for the statistic data.
57
59
  config_accessor :stats_cache_expiration_time do
58
- 5.minutes
60
+ Decidim::Env.new("INITIATIVES_STATS_CACHE_EXPIRATION_TIME", 5).to_i.minutes
59
61
  end
60
62
 
61
63
  # Maximum amount of time in validating state.
62
64
  # After this time the initiative will be moved to
63
65
  # discarded state.
64
66
  config_accessor :max_time_in_validating_state do
65
- 60.days
67
+ Decidim::Env.new("INITIATIVES_MAX_TIME_IN_VALIDATING_STATE", 60).to_i.days
66
68
  end
67
69
 
68
70
  # Print functionality enabled. Allows the user to get
69
71
  # a printed version of the initiative from the administration
70
72
  # panel.
71
73
  config_accessor :print_enabled do
72
- false
74
+ Decidim::Env.new("INITIATIVES_PRINT_ENABLED", "auto").to_s == "true"
73
75
  end
74
76
 
75
77
  # Set a service to generate a timestamp on each vote. The
76
78
  # attribute is the name of a class whose instances are
77
79
  # initialized with a string containing the data to be
78
80
  # timestamped and respond to a timestamp method
79
- config_accessor :timestamp_service
81
+ config_accessor :timestamp_service do
82
+ Decidim::Env.new("DECIDIM_TIMESTAMP_SERVICE", nil).value
83
+ end
80
84
 
81
85
  # Set a service to add a signature to pdf of signatures.
82
86
  # The attribute is the name of a class whose instances are
83
87
  # initialized with the document to be signed and respond to a
84
88
  # signed_pdf method with the signature added
85
- config_accessor :pdf_signature_service
89
+ config_accessor :pdf_signature_service do
90
+ Decidim::Env.new("DECIDIM_PDF_SIGNATURE_SERVICE", nil).value
91
+ end
86
92
 
87
93
  # This flag allows creating authorizations to unauthorized users.
88
94
  config_accessor :do_not_require_authorization do
89
- false
95
+ Decidim::Env.new("INITIATIVES_DO_NOT_REQUIRE_AUTHORIZATION").present?
96
+ end
97
+
98
+ # Encryption secret to use with signatures metadata
99
+ config_accessor :signature_handler_encryption_secret do
100
+ Decidim::Env.new("INITIATIVES_SIGNATURE_HANDLER_ENCRYPTION_SECRET", "personal user metadata").to_s
90
101
  end
91
102
  end
92
103
  end