decidim-verifications 0.30.1 → 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 (78) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +5 -5
  3. data/app/commands/decidim/verifications/csv_census/admin/create_census_data.rb +5 -4
  4. data/app/commands/decidim/verifications/csv_census/admin/create_census_record.rb +23 -0
  5. data/app/commands/decidim/verifications/csv_census/admin/update_census_record.rb +15 -0
  6. data/app/controllers/concerns/decidim/verifications/admin/filterable.rb +26 -0
  7. data/app/controllers/decidim/verifications/authorizations_controller.rb +1 -2
  8. data/app/controllers/decidim/verifications/csv_census/admin/census_controller.rb +58 -14
  9. data/app/controllers/decidim/verifications/csv_census/admin/census_records_controller.rb +63 -0
  10. data/app/controllers/decidim/verifications/id_documents/admin/config_controller.rb +1 -1
  11. data/app/controllers/decidim/verifications/id_documents/admin/confirmations_controller.rb +1 -1
  12. data/app/controllers/decidim/verifications/id_documents/admin/offline_confirmations_controller.rb +1 -1
  13. data/app/controllers/decidim/verifications/id_documents/authorizations_controller.rb +2 -2
  14. data/app/controllers/decidim/verifications/postal_letter/authorizations_controller.rb +2 -2
  15. data/app/controllers/decidim/verifications/sms/authorizations_controller.rb +2 -2
  16. data/app/forms/decidim/verifications/csv_census/admin/census_data_form.rb +29 -3
  17. data/app/forms/decidim/verifications/csv_census/admin/census_form.rb +29 -0
  18. data/app/forms/decidim/verifications/sms/mobile_phone_form.rb +2 -2
  19. data/app/jobs/decidim/verifications/csv_census/process_census_data_job.rb +49 -0
  20. data/app/models/decidim/verifications/csv_census/data.rb +13 -1
  21. data/app/models/decidim/verifications/csv_datum.rb +21 -0
  22. data/app/packs/entrypoints/decidim_verifications.js +3 -0
  23. data/app/packs/src/csv_census.js +47 -0
  24. data/app/presenters/decidim/verifications/admin_log/csv_datum_presenter.rb +45 -0
  25. data/app/services/decidim/authorization_handler.rb +1 -1
  26. data/app/views/decidim/verifications/authorizations/new.html.erb +1 -1
  27. data/app/views/decidim/verifications/authorizations/onboarding_pending.html.erb +1 -1
  28. data/app/views/decidim/verifications/csv_census/admin/census/_csv_census_drawer.html.erb +5 -0
  29. data/app/views/decidim/verifications/csv_census/admin/census/index.html.erb +73 -51
  30. data/app/views/decidim/verifications/csv_census/admin/census/new_import.html.erb +27 -0
  31. data/app/views/decidim/verifications/csv_census/admin/census_records/_form.html.erb +9 -0
  32. data/app/views/decidim/verifications/csv_census/admin/census_records/edit_record.html.erb +14 -0
  33. data/app/views/decidim/verifications/csv_census/admin/census_records/new_record.html.erb +14 -0
  34. data/app/views/decidim/verifications/postal_letter/admin/pending_authorizations/index.html.erb +33 -17
  35. data/config/assets.rb +2 -2
  36. data/config/locales/ar.yml +0 -5
  37. data/config/locales/bg.yml +0 -12
  38. data/config/locales/ca-IT.yml +62 -17
  39. data/config/locales/ca.yml +62 -17
  40. data/config/locales/cs.yml +42 -16
  41. data/config/locales/de.yml +61 -17
  42. data/config/locales/el.yml +0 -5
  43. data/config/locales/en.yml +62 -17
  44. data/config/locales/es-MX.yml +62 -17
  45. data/config/locales/es-PY.yml +62 -17
  46. data/config/locales/es.yml +62 -17
  47. data/config/locales/eu.yml +74 -29
  48. data/config/locales/fi-plain.yml +63 -11
  49. data/config/locales/fi.yml +63 -11
  50. data/config/locales/fr-CA.yml +5 -12
  51. data/config/locales/fr.yml +5 -12
  52. data/config/locales/gl.yml +0 -5
  53. data/config/locales/hu.yml +0 -12
  54. data/config/locales/id-ID.yml +0 -5
  55. data/config/locales/it.yml +0 -5
  56. data/config/locales/ja.yml +62 -17
  57. data/config/locales/lt.yml +0 -12
  58. data/config/locales/lv.yml +0 -5
  59. data/config/locales/nl.yml +0 -5
  60. data/config/locales/no.yml +0 -5
  61. data/config/locales/pl.yml +0 -12
  62. data/config/locales/pt-BR.yml +0 -5
  63. data/config/locales/pt.yml +0 -5
  64. data/config/locales/ro-RO.yml +40 -8
  65. data/config/locales/sk.yml +0 -5
  66. data/config/locales/sv.yml +61 -17
  67. data/config/locales/tr-TR.yml +0 -5
  68. data/config/locales/zh-CN.yml +0 -5
  69. data/config/locales/zh-TW.yml +0 -12
  70. data/lib/decidim/verifications/csv_census/admin_engine.rb +10 -2
  71. data/lib/decidim/verifications/engine.rb +1 -1
  72. data/lib/decidim/verifications/version.rb +1 -1
  73. data/lib/decidim/verifications/workflows.rb +3 -58
  74. data/lib/decidim/verifications.rb +1 -1
  75. data/lib/tasks/revoke.rake +16 -0
  76. metadata +22 -10
  77. data/app/jobs/decidim/verifications/csv_census/remove_duplicates_job.rb +0 -30
  78. data/lib/decidim/verifications/registry.rb +0 -43
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 90a284f93c186bca0e06fb5409f3554ae7e0dbd728ae45d2e02014e8884ef8c4
4
- data.tar.gz: 5a2c6b6544f731e6365d6ba62148b7d15f286ea60f091c74706a1927ac3095ff
3
+ metadata.gz: 7f5e445b04ef228652e17ed056aa508198045f6f2a8709a06e6f918ae32e31a2
4
+ data.tar.gz: e1ef5356582bcab1d2eadb41a71fde9c3980751ee08495c54e175410f10c83ec
5
5
  SHA512:
6
- metadata.gz: 70b77457cbb0f8459ef82e146628eff2526c364c599ca1c9611354b663a7bed04c3b5888d1f40dbf1dfaa2ec6f6f2005ab3059ce9e6046064e5fd53889a51653
7
- data.tar.gz: d5e08ed95e169338ed5098e0c2f489fee7100549058017b114588e41e543ccd25e90060805dfdec262129b2a7d3a04a15f5fcf13e134378dac84c48295d80856
6
+ metadata.gz: 1a46cbde1f71b5818775a8ef8b6f741945fd2b7bc5c3368ae849aa93e66cccf2e963cec21e7c7118616f294cd5f9b028d9c853433870fa942a8e8d2995f7976d
7
+ data.tar.gz: 78f7b084de6123dfd783f868d9ef674b25e9f35a0f2beba5150f18153fc635ca25dcf6edaa06379ad8bcbda46ba5ecdf7d4fa3e50e6cede99ce01b3ebca7da7c
data/README.md CHANGED
@@ -59,7 +59,7 @@ Decidim implements two type of authorization methods:
59
59
  To register your handler, use
60
60
 
61
61
  ```ruby
62
- # config/initializers/decidim.rb
62
+ # config/initializers/decidim_verifications.rb
63
63
 
64
64
  Decidim::Verifications.register_workflow(:census) do |workflow|
65
65
  workflow.form = "<myAuthorizationHandlerClass>"
@@ -74,7 +74,7 @@ Decidim implements two type of authorization methods:
74
74
  For example:
75
75
 
76
76
  ```ruby
77
- # config/initializers/decidim.rb
77
+ # config/initializers/decidim_verifications.rb
78
78
 
79
79
  Decidim::Verifications.register_workflow(:my_verification) do |workflow|
80
80
  workflow.engine = Decidim::Verifications::MyVerification::Engine
@@ -103,7 +103,7 @@ Decidim implements two type of authorization methods:
103
103
  Optionally to change the renew modal content part of the data stored, you can set a new value for the cell used to render the metadata.
104
104
 
105
105
  ```ruby
106
- # config/initializers/decidim.rb
106
+ # config/initializers/decidim_verifications.rb
107
107
 
108
108
  Decidim::Verifications.register_workflow(:census) do |workflow|
109
109
  workflow.form = "myAuthorizationHandlerClass"
@@ -146,7 +146,7 @@ SMS code using your preferred provider.
146
146
  In order to setup Decidim with SMS verification you need to:
147
147
 
148
148
  1. Create a class that accepts two parameters when initializing it (mobile phone and code) and a method named `deliver_code` that will send an SMS and return a truthy or falsey value if the delivery was OK or not.
149
- 1. Set the `sms_gateway_service` configuration variable to the name of the class that you just created (use a String, not the actual class) at `config/initializers/decidim.rb`.
149
+ 1. Set the `sms_gateway_service` configuration variable to the name of the class that you just created (use a String, not the actual class) using the `DECIDIM_SMS_GATEWAY_SERVICE` environment variable
150
150
 
151
151
  Keep in mind that Decidim will not store a free text version of the mobile phone, only a hashed
152
152
  version so we can avoid duplicates and guarantee the users' privacy.
@@ -217,7 +217,7 @@ To be used by the verification method, this class should be referenced by name i
217
217
  its workflow manifest:
218
218
 
219
219
  ```ruby
220
- # config/initializers/decidim.rb
220
+ # config/initializers/decidim_verifications.rb
221
221
 
222
222
  Decidim::Verifications.register_workflow(:my_verification) do |workflow|
223
223
  workflow.engine = Decidim::Verifications::MyVerification::Engine
@@ -7,9 +7,10 @@ module Decidim
7
7
  # A command with the business logic to create census data for a
8
8
  # organization.
9
9
  class CreateCensusData < Decidim::Command
10
- def initialize(form, organization)
10
+ def initialize(form, current_user)
11
11
  @form = form
12
- @organization = organization
12
+ @current_user = current_user
13
+ @organization = current_user.organization
13
14
  end
14
15
 
15
16
  # Executes the command. Broadcast this events:
@@ -21,13 +22,13 @@ module Decidim
21
22
  return broadcast(:invalid) unless @form.file
22
23
 
23
24
  data = @form.data
24
- return broadcast(:invalid) unless data
25
+ return broadcast(:invalid) if data.blank? || data.values.empty?
25
26
 
26
27
  # rubocop:disable Rails/SkipsModelValidations
27
28
  CsvDatum.insert_all(@organization, data.values)
28
29
  # rubocop:enable Rails/SkipsModelValidations
29
- RemoveDuplicatesJob.perform_later(@organization)
30
30
 
31
+ ProcessCensusDataJob.perform_later(data.values, @current_user)
31
32
  broadcast(:ok)
32
33
  end
33
34
  end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Verifications
5
+ module CsvCensus
6
+ module Admin
7
+ # A command with the business logic to create census data for a
8
+ # organization.
9
+ class CreateCensusRecord < Decidim::Commands::CreateResource
10
+ fetch_form_attributes :email, :organization
11
+
12
+ private
13
+
14
+ def resource_class = Decidim::Verifications::CsvDatum
15
+
16
+ def run_after_hooks
17
+ @resource.authorize!
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Verifications
5
+ module CsvCensus
6
+ module Admin
7
+ # A command with the business logic to create census data for a
8
+ # organization.
9
+ class UpdateCensusRecord < Decidim::Commands::UpdateResource
10
+ fetch_form_attributes :email
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/concern"
4
+
5
+ module Decidim
6
+ module Verifications
7
+ module Admin
8
+ module Filterable
9
+ extend ActiveSupport::Concern
10
+
11
+ included do
12
+ include Decidim::Admin::Filterable
13
+
14
+ private
15
+
16
+ def base_query
17
+ CsvDatum
18
+ .where(organization: current_organization)
19
+ .page(params[:page])
20
+ .per(15)
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -15,7 +15,6 @@ module Decidim
15
15
  include Decidim::HtmlSafeFlash
16
16
  include Decidim::Verifications::Renewable
17
17
  helper Decidim::DecidimFormHelper
18
- helper Decidim::CtaButtonHelper
19
18
  helper Decidim::AuthorizationFormHelper
20
19
  helper Decidim::TranslationsHelper
21
20
 
@@ -87,7 +86,7 @@ module Decidim
87
86
 
88
87
  on(:invalid) do
89
88
  flash[:alert] = t("authorizations.create.error", scope: "decidim.verifications")
90
- render action: :new
89
+ render action: :new, status: :unprocessable_entity
91
90
  end
92
91
  end
93
92
  end
@@ -10,41 +10,69 @@ module Decidim
10
10
  before_action :show_instructions,
11
11
  unless: :csv_census_active?
12
12
 
13
+ include Decidim::Verifications::Admin::Filterable
13
14
  include Decidim::Admin::WorkflowsBreadcrumb
15
+ include Decidim::Paginable
14
16
 
15
17
  add_breadcrumb_item_from_menu :workflows_menu
16
18
 
17
- def index
18
- enforce_permission_to :index, :authorization
19
- @form = form(CensusDataForm).instance
19
+ helper_method :csv_census_data, :last_login
20
+
21
+ def index; end
22
+
23
+ def destroy
24
+ Decidim::Commands::DestroyResource.call(census_data, current_user) do
25
+ on(:ok) do
26
+ flash[:notice] = I18n.t("census.destroy.success", scope: "decidim.verifications.csv_census.admin")
27
+ redirect_to census_logs_path
28
+ end
29
+ end
30
+ end
31
+
32
+ def new_import
33
+ @form = form(CensusDataForm).from_params(params)
20
34
  @status = Status.new(current_organization)
21
35
  end
22
36
 
23
- def create
37
+ def create_import
24
38
  enforce_permission_to :create, :authorization
25
39
  @form = form(CensusDataForm).from_params(params)
26
40
  @status = Status.new(current_organization)
27
- CreateCensusData.call(@form, current_organization) do
41
+
42
+ @form.validate_csv
43
+
44
+ if @form.errors.any?
45
+ error_messages = @form.errors.full_messages.map { |msg| "<li>#{msg}</li>" }.join
46
+ flash[:alert] = "<ul>#{error_messages}</ul>"
47
+ redirect_to(census_logs_path) && return
48
+ end
49
+
50
+ CreateCensusData.call(@form, current_user) do
28
51
  on(:ok) do
29
- flash[:notice] = t(".success", count: @form.data.values.count, errors: @form.data.errors.count)
30
- redirect_to census_path
52
+ flash[:notice] = I18n.t("census.create_import.success", scope: "decidim.verifications.csv_census.admin", count: @form.data.values.count)
53
+ redirect_to census_logs_path
31
54
  end
32
55
 
33
56
  on(:invalid) do
34
- flash[:alert] = t(".error")
35
- render :index
57
+ flash[:alert] = I18n.t("census.create_import.error", scope: "decidim.verifications.csv_census.admin")
58
+ redirect_to census_logs_path
36
59
  end
37
60
  end
38
61
  end
39
62
 
40
- def destroy_all
41
- enforce_permission_to :destroy, :authorization
42
- CsvDatum.clear(current_organization)
63
+ private
43
64
 
44
- redirect_to census_path, notice: t(".success")
65
+ def census_data
66
+ @census_data ||= CsvDatum.where(organization: current_organization).find(params[:id])
45
67
  end
46
68
 
47
- private
69
+ def csv_census_data
70
+ @csv_census_data ||= filtered_collection
71
+ end
72
+
73
+ def collection
74
+ @collection ||= CsvDatum.where(organization: current_organization)
75
+ end
48
76
 
49
77
  def show_instructions
50
78
  enforce_permission_to :index, :authorization
@@ -54,6 +82,22 @@ module Decidim
54
82
  def csv_census_active?
55
83
  current_organization.available_authorizations.include?("csv_census")
56
84
  end
85
+
86
+ def last_login(data)
87
+ user = current_organization.users.available.find_by(email: data.email)
88
+
89
+ return { icon: nil, text: t(".no_user"), last_sign_in: nil } unless user
90
+
91
+ authorized = Decidim::Authorization.where(name: "csv_census", user:)
92
+ .where.not(granted_at: nil)
93
+ .exists?
94
+
95
+ icon = authorized ? "checkbox-circle-line" : "close-circle-line"
96
+ text = authorized ? t("index.authorized", scope: "decidim.verifications.csv_census.admin") : t("index.no_authorized", scope: "decidim.verifications.csv_census.admin")
97
+ last_sign_in = user.last_sign_in_at ? l(user.last_sign_in_at, format: :decidim_short) : t(".no_sign_in")
98
+
99
+ { icon:, text:, last_sign_in: }
100
+ end
57
101
  end
58
102
  end
59
103
  end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Verifications
5
+ module CsvCensus
6
+ module Admin
7
+ class CensusRecordsController < Decidim::Admin::ApplicationController
8
+ layout false
9
+
10
+ helper_method :csv_census_data
11
+
12
+ def new_record
13
+ @form = form(Admin::CensusForm).instance
14
+ end
15
+
16
+ def create_record
17
+ @form = form(Admin::CensusForm).from_params(params)
18
+ Admin::CreateCensusRecord.call(@form) do
19
+ on(:ok) do
20
+ flash[:notice] = I18n.t("census_records.create_record.success", scope: "decidim.verifications.csv_census.admin")
21
+ render json: { redirect_url: census_logs_path }, status: :ok
22
+ end
23
+
24
+ on(:invalid) do
25
+ render :new_record, status: :unprocessable_entity
26
+ end
27
+ end
28
+ end
29
+
30
+ def edit_record
31
+ @form = form(Admin::CensusForm).from_model(census_data)
32
+ end
33
+
34
+ def update_record
35
+ @form = form(Admin::CensusForm).from_params(params)
36
+
37
+ Admin::UpdateCensusRecord.call(@form, census_data) do
38
+ on(:ok) do
39
+ flash[:notice] = I18n.t("census_records.update_record.success", scope: "decidim.verifications.csv_census.admin")
40
+ render json: { redirect_url: census_logs_path }, status: :ok
41
+ end
42
+
43
+ on(:invalid) do
44
+ flash.now[:alert] = I18n.t("census_records.update_record.invalid", scope: "decidim.verifications.csv_census.admin")
45
+ render action: "edit_record", status: :unprocessable_entity
46
+ end
47
+ end
48
+ end
49
+
50
+ private
51
+
52
+ def census_data
53
+ @census_data ||= CsvDatum.where(organization: current_organization).find_by(id: params[:id])
54
+ end
55
+
56
+ def csv_census_data
57
+ @csv_census_data ||= CsvDatum.where(organization: current_organization)
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -33,7 +33,7 @@ module Decidim
33
33
 
34
34
  on(:invalid) do
35
35
  flash.now[:alert] = t("config.update.error", scope: "decidim.verifications.id_documents.admin")
36
- render action: :edit
36
+ render action: :edit, status: :unprocessable_entity
37
37
  end
38
38
  end
39
39
  end
@@ -35,7 +35,7 @@ module Decidim
35
35
 
36
36
  on(:invalid) do
37
37
  flash.now[:alert] = t("confirmations.create.error", scope: "decidim.verifications.id_documents.admin")
38
- render action: :new
38
+ render action: :new, status: :unprocessable_entity
39
39
  end
40
40
  end
41
41
  end
@@ -33,7 +33,7 @@ module Decidim
33
33
 
34
34
  on(:invalid) do
35
35
  flash.now[:alert] = t("offline_confirmations.create.error", scope: "decidim.verifications.id_documents.admin")
36
- render action: :new
36
+ render action: :new, status: :unprocessable_entity
37
37
  end
38
38
  end
39
39
  end
@@ -42,7 +42,7 @@ module Decidim
42
42
 
43
43
  on(:invalid) do
44
44
  flash.now[:alert] = t("authorizations.create.error", scope: "decidim.verifications.id_documents")
45
- render action: :new
45
+ render action: :new, status: :unprocessable_entity
46
46
  end
47
47
  end
48
48
  end
@@ -72,7 +72,7 @@ module Decidim
72
72
 
73
73
  on(:invalid) do
74
74
  flash.now[:alert] = t("authorizations.update.error", scope: "decidim.verifications.id_documents")
75
- render action: :edit
75
+ render action: :edit, status: :unprocessable_entity
76
76
  end
77
77
  end
78
78
  end
@@ -29,7 +29,7 @@ module Decidim
29
29
 
30
30
  on(:invalid) do
31
31
  flash.now[:alert] = t("authorizations.create.error", scope: "decidim.verifications.postal_letter")
32
- render :new
32
+ render :new, status: :unprocessable_entity
33
33
  end
34
34
  end
35
35
  end
@@ -53,7 +53,7 @@ module Decidim
53
53
 
54
54
  on(:invalid) do
55
55
  flash.now[:alert] = t("authorizations.update.error", scope: "decidim.verifications.postal_letter")
56
- render :edit
56
+ render :edit, status: :unprocessable_entity
57
57
  end
58
58
  end
59
59
  end
@@ -27,7 +27,7 @@ module Decidim
27
27
  end
28
28
  on(:invalid) do
29
29
  flash.now[:alert] = t("authorizations.create.error", scope: "decidim.verifications.sms")
30
- render :new
30
+ render :new, status: :unprocessable_entity
31
31
  end
32
32
  end
33
33
  end
@@ -52,7 +52,7 @@ module Decidim
52
52
 
53
53
  on(:invalid) do
54
54
  flash.now[:alert] = t("authorizations.update.error", scope: "decidim.verifications.sms")
55
- render :edit
55
+ render :edit, status: :unprocessable_entity
56
56
  end
57
57
  end
58
58
  end
@@ -6,16 +6,42 @@ module Decidim
6
6
  module Admin
7
7
  # A form to temporarily upload csv census data
8
8
  class CensusDataForm < Form
9
+ include Decidim::HasUploadValidations
10
+ include Decidim::ProcessesFileLocally
9
11
  mimic :census_data
10
12
 
11
- attribute :file
13
+ attribute :file, Decidim::Attributes::Blob
14
+
15
+ validates :file, presence: true, file_content_type: { allow: ["text/csv"] }
12
16
 
13
17
  def data
14
- CsvCensus::Data.new(file.path)
18
+ @data ||= process_data
19
+ end
20
+
21
+ def process_data
22
+ process_file_locally(file) do |file_path|
23
+ CsvCensus::Data.new(file_path)
24
+ end
15
25
  rescue CSV::MalformedCSVError
16
26
  errors.add(:file, :malformed)
27
+ end
28
+
29
+ def validate_csv
30
+ return unless data
31
+
32
+ errors.add(:base, I18n.t("decidim.verifications.errors.wrong_number_columns", expected: 1, actual: data.count)) if data.count != 1
33
+
34
+ errors.add(:base, I18n.t("decidim.verifications.errors.no_emails")) if data.values.empty?
35
+
36
+ data.values.each do |value|
37
+ errors.add(:base, I18n.t("decidim.verifications.errors.invalid_emails", invalid_emails: value)) unless valid_email?(value)
38
+ end
39
+ end
40
+
41
+ private
17
42
 
18
- nil
43
+ def valid_email?(email)
44
+ URI::MailTo::EMAIL_REGEXP.match?(email)
19
45
  end
20
46
  end
21
47
  end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Verifications
5
+ module CsvCensus
6
+ module Admin
7
+ # A form to temporarily upload csv census data
8
+ class CensusForm < Form
9
+ attribute :email
10
+
11
+ validates :email, presence: true, "valid_email_2/email": { disposable: true }
12
+ validate :unique_email
13
+
14
+ private
15
+
16
+ def unique_email
17
+ return true if CsvDatum.where(
18
+ organization: context.current_organization,
19
+ email:
20
+ ).empty?
21
+
22
+ errors.add :email, :taken
23
+ false
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -17,7 +17,7 @@ module Decidim
17
17
 
18
18
  # A mobile phone can only be verified once but it should be private.
19
19
  def unique_id
20
- Digest::MD5.hexdigest(
20
+ Digest::SHA256.hexdigest(
21
21
  "#{mobile_phone_number}-#{Rails.application.secret_key_base}"
22
22
  )
23
23
  end
@@ -57,7 +57,7 @@ module Decidim
57
57
  end
58
58
 
59
59
  def generated_code
60
- @generated_code ||= SecureRandom.random_number(1_000_000).to_s
60
+ @generated_code ||= format("%06d", SecureRandom.random_number(1_000_000))
61
61
  end
62
62
  end
63
63
  end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Verifications
5
+ module CsvCensus
6
+ class ProcessCensusDataJob < ApplicationJob
7
+ queue_as :default
8
+ attr_reader :imported_records, :failed, :user
9
+
10
+ def perform(data, user)
11
+ @user = user
12
+ @imported_records = []
13
+ @failed = []
14
+
15
+ data.each do |email|
16
+ record = CsvDatum.find_or_create_by(email:, organization: user.organization)
17
+ if record && record.valid?
18
+ @imported_records << record
19
+ else
20
+ @failed << email
21
+ Rails.logger.warn(I18n.t("census.new_import.errors.email_exists", scope: "decidim.verifications.csv_census.admin", email:, organization: user.organization.id))
22
+ end
23
+
24
+ record.authorize!
25
+ end
26
+
27
+ log_import_action
28
+ end
29
+
30
+ private
31
+
32
+ def log_import_action
33
+ return if imported_records.blank?
34
+
35
+ Decidim::ActionLogger.log(
36
+ "import",
37
+ user,
38
+ imported_records.first,
39
+ nil,
40
+ extra: {
41
+ imported_records:,
42
+ failed_count: failed.count
43
+ }
44
+ )
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -14,20 +14,32 @@ module Decidim
14
14
  #
15
15
  # Returns nothing
16
16
  class Data
17
- attr_reader :errors, :values
17
+ attr_reader :errors, :values, :file
18
18
 
19
19
  def initialize(file)
20
20
  @file = file
21
21
  @values = []
22
22
  @errors = []
23
+ @column_count = nil
23
24
 
24
25
  CSV.foreach(@file, encoding: "BOM|UTF-8") do |row|
25
26
  process_row(row)
27
+ @column_count ||= row.size
26
28
  end
29
+
30
+ @errors << I18n.t("decidim.verifications.errors.wrong_number_columns", expected: 1, actual: @column_count) if @column_count && @column_count > 1
31
+ end
32
+
33
+ def count
34
+ @column_count || 0
27
35
  end
28
36
 
29
37
  private
30
38
 
39
+ def valid_email?(email)
40
+ URI::MailTo::EMAIL_REGEXP.match?(email)
41
+ end
42
+
31
43
  def process_row(row)
32
44
  user_mail = row.first
33
45
  if user_mail.present? && user_mail.match?(::Devise.email_regexp)
@@ -3,10 +3,14 @@
3
3
  module Decidim
4
4
  module Verifications
5
5
  class CsvDatum < ApplicationRecord
6
+ include Decidim::Traceable
7
+
6
8
  belongs_to :organization, foreign_key: :decidim_organization_id,
7
9
  class_name: "Decidim::Organization"
8
10
 
9
11
  validates :email, format: { with: ::Devise.email_regexp }
12
+ validates :email, presence: true
13
+ validates :email, uniqueness: { scope: :decidim_organization_id }
10
14
 
11
15
  def self.inside(organization)
12
16
  where(organization:)
@@ -26,6 +30,23 @@ module Decidim
26
30
  def self.clear(organization)
27
31
  inside(organization).delete_all
28
32
  end
33
+
34
+ def self.log_presenter_class_for(_log)
35
+ Decidim::Verifications::AdminLog::CsvDatumPresenter
36
+ end
37
+
38
+ def authorize!
39
+ user = organization.users.available.find_by(email:)
40
+
41
+ return unless user
42
+
43
+ authorization = Decidim::Authorization.find_or_initialize_by(
44
+ user:,
45
+ name: "csv_census"
46
+ )
47
+
48
+ authorization.grant! unless authorization.granted?
49
+ end
29
50
  end
30
51
  end
31
52
  end
@@ -1,2 +1,5 @@
1
1
  // CSS
2
2
  import "stylesheets/verifications.scss"
3
+
4
+ // JS
5
+ import "src/csv_census"