decidim-direct_verifications 0.17.8 → 1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +43 -2
  3. data/app/assets/config/direct_verifications_admin_manifest.css +3 -0
  4. data/app/assets/stylesheets/decidim/direct_verifications/authorizations.scss +17 -0
  5. data/app/commands/decidim/direct_verifications/verification/create_import.rb +50 -0
  6. data/app/controllers/decidim/direct_verifications/verification/admin/authorizations_controller.rb +40 -0
  7. data/app/controllers/decidim/direct_verifications/verification/admin/direct_verifications_controller.rb +71 -46
  8. data/app/controllers/decidim/direct_verifications/verification/admin/imports_controller.rb +55 -0
  9. data/app/forms/decidim/direct_verifications/registration_form.rb +8 -0
  10. data/app/forms/decidim/direct_verifications/verification/create_import_form.rb +43 -0
  11. data/app/jobs/decidim/direct_verifications/authorize_users_job.rb +34 -0
  12. data/app/jobs/decidim/direct_verifications/base_import_job.rb +38 -0
  13. data/app/jobs/decidim/direct_verifications/register_users_job.rb +19 -0
  14. data/app/jobs/decidim/direct_verifications/revoke_users_job.rb +17 -0
  15. data/app/mailers/decidim/direct_verifications/import_mailer.rb +27 -0
  16. data/app/mailers/decidim/direct_verifications/stats.rb +23 -0
  17. data/app/views/decidim/direct_verifications/import_mailer/finished_processing.html.erb +7 -0
  18. data/app/views/decidim/direct_verifications/import_mailer/finished_processing.text.erb +7 -0
  19. data/app/views/decidim/direct_verifications/verification/admin/authorizations/index.html.erb +42 -0
  20. data/app/views/decidim/direct_verifications/verification/admin/direct_verifications/index.html.erb +18 -17
  21. data/app/views/decidim/direct_verifications/verification/admin/imports/new.html.erb +50 -0
  22. data/app/views/decidim/direct_verifications/verification/admin/stats/index.html.erb +3 -3
  23. data/config/initializers/mail_previews.rb +5 -0
  24. data/config/locales/ca.yml +25 -20
  25. data/config/locales/cs.yml +69 -0
  26. data/config/locales/en.yml +34 -2
  27. data/config/locales/es.yml +25 -20
  28. data/config/locales/fr.yml +69 -0
  29. data/lib/decidim/direct_verifications.rb +24 -1
  30. data/lib/decidim/direct_verifications/authorize_user.rb +64 -0
  31. data/lib/decidim/direct_verifications/instrumenter.rb +58 -0
  32. data/lib/decidim/direct_verifications/parsers.rb +11 -0
  33. data/lib/decidim/direct_verifications/parsers/base_parser.rb +37 -0
  34. data/lib/decidim/direct_verifications/parsers/metadata_parser.rb +54 -0
  35. data/lib/decidim/direct_verifications/parsers/name_parser.rb +40 -0
  36. data/lib/decidim/direct_verifications/register_user.rb +54 -0
  37. data/lib/decidim/direct_verifications/revoke_user.rb +45 -0
  38. data/lib/decidim/direct_verifications/tests/factories.rb +11 -0
  39. data/lib/decidim/direct_verifications/tests/verification_controller_examples.rb +83 -0
  40. data/lib/decidim/direct_verifications/user_processor.rb +18 -98
  41. data/lib/decidim/direct_verifications/user_stats.rb +9 -6
  42. data/lib/decidim/direct_verifications/verification/admin_engine.rb +6 -1
  43. data/lib/decidim/direct_verifications/version.rb +3 -2
  44. metadata +38 -9
  45. data/lib/decidim/direct_verifications/config.rb +0 -23
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e4928cec1e6b6d4f5e09fd2235e38b4e7a0fe411dc4be792a25d5bff2a8ef5b6
4
- data.tar.gz: 978ba2b8a55151a524643df8268e2f0435f329edc46acad48c3fe463f5382150
3
+ metadata.gz: 46d0f12ed4661879a4cdec82f1482875823fe9c5f4fba39c129e2a020d3c7f19
4
+ data.tar.gz: 5cf039fb0a1b3474082dbf7acc28013d8d0034c6d959c7c7ad589aca85c1fdf8
5
5
  SHA512:
6
- metadata.gz: f9435b6e219c4c49c2d19899fe7482524ca101545f9099dd2b09a7a7c33a0d662ce0b60dace84582912b1e91c8969843b2263404e4730c31043908da455f345a
7
- data.tar.gz: 42dc907a3fd1bfc59194a31007c6ed083a5b5610790bde8f32a50ef83ee5fed8ed8e4a215adb8586bb7e1e096a9244e5aa48c0a0973b0d1afce1f409ddc9cd3d
6
+ metadata.gz: 4f960edacf1528f9801d6cfd82aa6898a5bffbf9233a2386a57cdb0c55c0a3d5ef45ddf501d1775db1ab325dbcd61925b012dcc3ab8bf83bedbd4630abbf3b03
7
+ data.tar.gz: 909e8c8be3368861bc6fe75c1290f820a4d697e20bf4ded85d9e91ec9a5b7a27d3aa65953d749460d3fe0efbfe132fd8aa586ac27e032782127463be98476a61
data/README.md CHANGED
@@ -1,6 +1,10 @@
1
1
  # Decidim::DirectVerifications
2
2
 
3
- [![Build Status](https://travis-ci.org/Platoniq/decidim-verifications-direct_verifications.svg?branch=master)](https://travis-ci.org/Platoniq/decidim-verifications-direct_verifications)
3
+ [![Test](https://github.com/Platoniq/decidim-verifications-direct_verifications/actions/workflows/test.yml/badge.svg)](https://github.com/Platoniq/decidim-verifications-direct_verifications/actions/workflows/test.yml)
4
+ [![Maintainability](https://api.codeclimate.com/v1/badges/2195deb4de6c6354a6bc/maintainability)](https://codeclimate.com/github/Platoniq/decidim-verifications-direct_verifications/maintainability)
5
+ [![codecov](https://codecov.io/gh/Platoniq/decidim-verifications-direct_verifications/branch/main/graph/badge.svg?token=FR1zkV71S2)](https://codecov.io/gh/Platoniq/decidim-verifications-direct_verifications)
6
+
7
+
4
8
 
5
9
  A [Decidim](https://github.com/decidim/decidim) that provides a verification method called `Direct verification`. Works only on the admin side, final users do not intervene in the verification process.
6
10
 
@@ -34,6 +38,23 @@ With the detected list of emails admin have different options available:
34
38
  3. Revoke the authorization for the list of users using any verification method available.
35
39
  4. Check the status of the users in order to know if they are verified or registered.
36
40
 
41
+ ### Metadata mode
42
+
43
+ This mode provides extra capabilities over the default processing:
44
+
45
+ * Reads CSV format with header (copy and paste it from your spreadsheet)
46
+ * Stores all columns except the email as authorization metadata
47
+
48
+ This enables querying the authorization metadata however fits you best.
49
+
50
+ To enable it create a new initializer, for instance `config/initializers/decidim_direct_verifications.rb` with the following contents
51
+
52
+ ```ruby
53
+ Decidim::DirectVerifications.configure do |config|
54
+ config.input_parser = :metadata_parser
55
+ end
56
+ ```
57
+
37
58
  ## Installation
38
59
 
39
60
  Add this line to your application's Gemfile:
@@ -48,6 +69,13 @@ And then execute:
48
69
  bundle
49
70
  ```
50
71
 
72
+ Depending on your Decidim version, you might want to specify the version to ensure compatibility:
73
+
74
+ | Direct Verifications version | Compatible Decidim versions |
75
+ |---|---|
76
+ | 1.0 | >= 0.23.x |
77
+ | 0.22.x | 0.22.x |
78
+
51
79
  ## Using additional verification methods
52
80
 
53
81
  You can manage other verification methods (or workflow) a part from `Direct verification`. You need to configure it in a new file in the `config/initializers` folder.
@@ -68,7 +96,10 @@ end
68
96
 
69
97
  # We need to tell the plugin to handle this method in addition to the default "Direct verification". Any registered workflow is valid.
70
98
  Decidim::DirectVerifications.configure do |config|
71
- config.manage_workflows = %w(direct_verifications_managers)
99
+ config.manage_workflows = %w(direct_verifications direct_verifications_managers)
100
+
101
+ # change the to the metadata_parser if you want it
102
+ # config.input_parser = :metadata_parser
72
103
  end
73
104
 
74
105
  ```
@@ -84,6 +115,11 @@ en:
84
115
  direct_verifications_managers:
85
116
  name: Organization managers
86
117
  explanation: Direct Verifications Subgroup explanation
118
+ verifications:
119
+ authorizations:
120
+ first_login:
121
+ actions:
122
+ direct_verifications_managers: Organization managers
87
123
  ```
88
124
 
89
125
  Similarly, you can also overwrite the default title "Direct verification" by creating the key again in your locales:
@@ -95,6 +131,11 @@ en:
95
131
  direct_verifications:
96
132
  name: Generic organization members
97
133
  explanation: Direct Verifications Subgroup explanation
134
+ verifications:
135
+ authorizations:
136
+ first_login:
137
+ actions:
138
+ direct_verifications: Generic organization members
98
139
  ```
99
140
 
100
141
 
@@ -0,0 +1,3 @@
1
+ /*
2
+ *= link decidim/direct_verifications/authorizations.css
3
+ */
@@ -0,0 +1,17 @@
1
+ @import "decidim/admin/utils/settings";
2
+
3
+ $code-color: $black;
4
+ $code-font-family: $font-family-monospace;
5
+ $code-font-weight: $global-weight-normal;
6
+ $code-background: $light-gray;
7
+ $code-border: 1px solid $medium-gray;
8
+ $code-padding: rem-calc(2 5 1);
9
+
10
+ .code {
11
+ background: $code-background;
12
+ color: $code-color;
13
+ font-family: $code-font-family;
14
+ font-weight: $code-font-weight;
15
+ border: $code-border;
16
+ padding: $code-padding;
17
+ }
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module DirectVerifications
5
+ module Verification
6
+ class CreateImport < Rectify::Command
7
+ def initialize(form)
8
+ @form = form
9
+ @file = form.file
10
+ @organization = form.organization
11
+ @user = form.user
12
+ @action = form.action
13
+ end
14
+
15
+ def call
16
+ return broadcast(:invalid) unless form.valid?
17
+
18
+ case action
19
+ when :register
20
+ register_users_async
21
+ when :register_and_authorize
22
+ register_users_async
23
+ file.rewind
24
+ authorize_users_async
25
+ when :revoke
26
+ revoke_users_async
27
+ end
28
+
29
+ broadcast(:ok)
30
+ end
31
+
32
+ private
33
+
34
+ attr_reader :form, :file, :organization, :user, :action
35
+
36
+ def register_users_async
37
+ RegisterUsersJob.perform_later(file.read, organization, user, form.authorization_handler)
38
+ end
39
+
40
+ def revoke_users_async
41
+ RevokeUsersJob.perform_later(file.read, organization, user, form.authorization_handler)
42
+ end
43
+
44
+ def authorize_users_async
45
+ AuthorizeUsersJob.perform_later(file.read, organization, user, form.authorization_handler)
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module DirectVerifications
5
+ module Verification
6
+ module Admin
7
+ class AuthorizationsController < Decidim::Admin::ApplicationController
8
+ layout "decidim/admin/users"
9
+
10
+ def index
11
+ enforce_permission_to :index, :authorization
12
+ @authorizations = collection.includes(:user)
13
+ end
14
+
15
+ def destroy
16
+ if authorization.destroy
17
+ flash[:notice] = "successfully"
18
+ redirect_to authorizations_path
19
+ end
20
+ end
21
+
22
+ private
23
+
24
+ def collection
25
+ # Decidim::Verifications::Authorizations Query
26
+ Decidim::Verifications::Authorizations.new(
27
+ organization: current_organization,
28
+ name: "direct_verifications",
29
+ granted: true
30
+ ).query
31
+ end
32
+
33
+ def authorization
34
+ @authorization ||= collection.find_by(id: params[:id])
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -6,75 +6,100 @@ module Decidim
6
6
  module Admin
7
7
  class DirectVerificationsController < Decidim::Admin::ApplicationController
8
8
  include NeedsPermission
9
+ helper_method :workflows, :current_authorization_handler
9
10
 
10
11
  layout "decidim/admin/users"
11
12
 
12
13
  def index
13
14
  enforce_permission_to :index, :authorization
14
- @authorization_handler = :direct_verifications
15
- @workflows = workflows
16
15
  end
17
16
 
18
17
  def create
19
18
  enforce_permission_to :create, :authorization
20
19
 
21
- @userlist = params[:userlist]
22
- @workflows = workflows
23
- processor = UserProcessor.new(current_organization, current_user)
24
- processor.emails = extract_emails_to_hash @userlist
25
- processor.authorization_handler = authorization_handler(params[:authorization_handler])
26
- stats = UserStats.new(current_organization)
27
- stats.authorization_handler = processor.authorization_handler
28
- if params[:register]
29
- processor.register_users
30
- flash[:warning] = t(".registered", count: processor.emails.count,
31
- registered: processor.processed[:registered].count,
32
- errors: processor.errors[:registered].count)
33
- end
34
- if params[:authorize] == "in"
35
- processor.authorize_users
36
- flash[:notice] = t(".authorized", handler: t("#{processor.authorization_handler}.name", scope: "decidim.authorization_handlers"),
37
- count: processor.emails.count,
38
- authorized: processor.processed[:authorized].count,
39
- errors: processor.errors[:authorized].count)
40
- elsif params[:authorize] == "out"
41
- processor.revoke_users
42
- flash[:notice] = t(".revoked", handler: t("#{processor.authorization_handler}.name", scope: "decidim.authorization_handlers"),
43
- count: processor.emails.count,
44
- revoked: processor.processed[:revoked].count,
45
- errors: processor.errors[:revoked].count)
46
- else
47
- stats.emails = processor.emails.keys
48
- flash[:info] = t(".info", handler: t("#{processor.authorization_handler}.name", scope: "decidim.authorization_handlers"),
49
- count: processor.emails.count,
50
- authorized: stats.authorized,
51
- unconfirmed: stats.unconfirmed,
52
- registered: stats.registered)
53
- render(action: :index) && return
54
- end
20
+ @userslist = params[:userslist]
21
+
22
+ @processor = UserProcessor.new(current_organization, current_user, session, instrumenter)
23
+ @processor.emails = parser_class.new(@userslist).to_h
24
+ @processor.authorization_handler = current_authorization_handler
25
+
26
+ @stats = UserStats.new(current_organization)
27
+ @stats.authorization_handler = @processor.authorization_handler
28
+
29
+ register_users
30
+ authorize_users
31
+ revoke_users
32
+
33
+ render(action: :index) && return if show_users_info
34
+
35
+ redirect_to direct_verifications_path
36
+ rescue InputParserError => e
37
+ flash[:error] = e.message
55
38
  redirect_to direct_verifications_path
56
39
  end
57
40
 
58
41
  private
59
42
 
60
- def extract_emails_to_hash(txt)
61
- reg = /([A-Z0-9+._-]+@[A-Z0-9._-]+\.[A-Z0-9_-]+)\b/i
62
- emails = {}
63
- txt.split(/[\r\n;,]/).each do |line|
64
- reg.match line do |m|
65
- n = line.split(m[0]).first
66
- emails[m[0]] = (n.presence || "").gsub(/[^[:print:]]|[\"\$\<\>\|\\]/, "").strip
67
- end
68
- end
69
- emails
43
+ def instrumenter
44
+ @instrumenter ||= Instrumenter.new(current_user)
45
+ end
46
+
47
+ def register_users
48
+ return unless params[:register]
49
+
50
+ @processor.register_users
51
+ flash[:warning] = t(".registered", count: @processor.emails.count,
52
+ registered: instrumenter.processed_count(:registered),
53
+ errors: instrumenter.errors_count(:registered))
54
+ end
55
+
56
+ def authorize_users
57
+ return unless params[:authorize] == "in"
58
+
59
+ @processor.authorize_users
60
+ flash[:notice] = t(".authorized", handler: t("#{@processor.authorization_handler}.name", scope: "decidim.authorization_handlers"),
61
+ count: @processor.emails.count,
62
+ authorized: instrumenter.processed_count(:authorized),
63
+ errors: instrumenter.errors_count(:authorized))
64
+ end
65
+
66
+ def revoke_users
67
+ return unless params[:authorize] == "out"
68
+
69
+ @processor.revoke_users
70
+ flash[:notice] = t(".revoked", handler: t("#{@processor.authorization_handler}.name", scope: "decidim.authorization_handlers"),
71
+ count: @processor.emails.count,
72
+ revoked: instrumenter.processed_count(:revoked),
73
+ errors: instrumenter.errors_count(:revoked))
74
+ end
75
+
76
+ def show_users_info
77
+ return if params[:authorize].in? %w(in out)
78
+
79
+ @stats.emails = @processor.emails.keys
80
+ flash.now[:info] = t(".info", handler: t("#{@processor.authorization_handler}.name", scope: "decidim.authorization_handlers"),
81
+ count: @processor.emails.count,
82
+ authorized: @stats.authorized,
83
+ unconfirmed: @stats.unconfirmed,
84
+ registered: @stats.registered)
85
+ true
86
+ end
87
+
88
+ def parser_class
89
+ Decidim::DirectVerifications.find_parser_class(Decidim::DirectVerifications.input_parser)
70
90
  end
71
91
 
72
92
  def authorization_handler(authorization_handler)
73
93
  @authorization_handler = authorization_handler.presence || :direct_verifications
74
94
  end
75
95
 
96
+ def current_authorization_handler
97
+ authorization_handler(params[:authorization_handler])
98
+ end
99
+
76
100
  def configured_workflows
77
101
  return Decidim::DirectVerifications.config.manage_workflows if Decidim::DirectVerifications.config
102
+
78
103
  ["direct_verifications"]
79
104
  end
80
105
 
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module DirectVerifications
5
+ module Verification
6
+ module Admin
7
+ class ImportsController < Decidim::Admin::ApplicationController
8
+ layout "decidim/admin/users"
9
+ helper_method :workflows, :current_authorization_handler
10
+
11
+ def new
12
+ enforce_permission_to :create, :authorization
13
+ @form = form(CreateImportForm).instance
14
+ end
15
+
16
+ def create
17
+ enforce_permission_to :create, :authorization
18
+
19
+ defaults = { organization: current_organization, user: current_user }
20
+ form = form(CreateImportForm).from_params(params.merge(defaults))
21
+
22
+ CreateImport.call(form) do
23
+ on(:ok) do
24
+ flash[:notice] = t(".success")
25
+ end
26
+
27
+ on(:invalid) do
28
+ flash[:alert] = t(".error")
29
+ end
30
+ end
31
+
32
+ redirect_to new_import_path
33
+ end
34
+
35
+ def workflows
36
+ workflows = configured_workflows & current_organization.available_authorizations.map.to_a
37
+ workflows.map do |workflow|
38
+ [t("#{workflow}.name", scope: "decidim.authorization_handlers"), workflow]
39
+ end
40
+ end
41
+
42
+ def configured_workflows
43
+ return Decidim::DirectVerifications.config.manage_workflows if Decidim::DirectVerifications.config
44
+
45
+ ["direct_verifications"]
46
+ end
47
+
48
+ def current_authorization_handler
49
+ params[:authorization_handler]
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module DirectVerifications
5
+ class RegistrationForm < OpenStruct
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module DirectVerifications
5
+ module Verification
6
+ class CreateImportForm < Form
7
+ ACTIONS = {
8
+ "in" => :authorize,
9
+ "out" => :revoke,
10
+ "check" => :check
11
+ }.freeze
12
+
13
+ attribute :file
14
+ attribute :organization, Decidim::Organization
15
+ attribute :user, Decidim::User
16
+ attribute :authorize, String
17
+ attribute :register, Boolean
18
+ attribute :authorization_handler, String
19
+
20
+ validates :file, :organization, :user, :authorize, :authorization_handler, presence: true
21
+ validates :authorize, inclusion: { in: ACTIONS.keys }
22
+
23
+ validate :available_authorization_handler
24
+
25
+ def available_authorization_handler
26
+ return if authorization_handler.in?(organization.available_authorizations)
27
+
28
+ errors.add(:authorization_handler, :inclusion)
29
+ end
30
+
31
+ def action
32
+ if register && authorize == "in"
33
+ :register_and_authorize
34
+ elsif register
35
+ :register
36
+ else
37
+ ACTIONS[authorize]
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end