decidim-verifications 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +101 -0
- data/Rakefile +3 -0
- data/app/commands/decidim/verifications/authorize_user.rb +61 -0
- data/app/commands/decidim/verifications/confirm_user_authorization.rb +49 -0
- data/app/commands/decidim/verifications/perform_authorization_step.rb +48 -0
- data/app/controllers/decidim/verifications/authorizations_controller.rb +91 -0
- data/app/controllers/decidim/verifications/id_documents/admin/confirmations_controller.rb +48 -0
- data/app/controllers/decidim/verifications/id_documents/admin/pending_authorizations_controller.rb +28 -0
- data/app/controllers/decidim/verifications/id_documents/admin/rejections_controller.rb +37 -0
- data/app/controllers/decidim/verifications/id_documents/authorizations_controller.rb +82 -0
- data/app/controllers/decidim/verifications/postal_letter/admin/pending_authorizations_controller.rb +27 -0
- data/app/controllers/decidim/verifications/postal_letter/admin/postages_controller.rb +41 -0
- data/app/controllers/decidim/verifications/postal_letter/authorizations_controller.rb +74 -0
- data/app/forms/decidim/verifications/id_documents/information_form.rb +50 -0
- data/app/forms/decidim/verifications/id_documents/information_rejection_form.rb +15 -0
- data/app/forms/decidim/verifications/id_documents/upload_form.rb +20 -0
- data/app/forms/decidim/verifications/postal_letter/address_form.rb +28 -0
- data/app/forms/decidim/verifications/postal_letter/confirmation_form.rb +19 -0
- data/app/forms/decidim/verifications/postal_letter/postage_form.rb +34 -0
- data/app/presenters/decidim/verifications/id_documents/authorization_presenter.rb +20 -0
- data/app/presenters/decidim/verifications/postal_letter/authorization_presenter.rb +59 -0
- data/app/queries/decidim/verifications/authorizations.rb +41 -0
- data/app/services/decidim/authorization_handler.rb +112 -0
- data/app/services/decidim/dummy_authorization_handler.rb +27 -0
- data/app/uploaders/decidim/verifications/attachment_uploader.rb +16 -0
- data/app/views/decidim/verifications/authorizations/first_login.html.erb +22 -0
- data/app/views/decidim/verifications/authorizations/index.html.erb +78 -0
- data/app/views/decidim/verifications/authorizations/new.html.erb +33 -0
- data/app/views/decidim/verifications/id_documents/admin/confirmations/new.html.erb +26 -0
- data/app/views/decidim/verifications/id_documents/admin/pending_authorizations/index.html.erb +32 -0
- data/app/views/decidim/verifications/id_documents/authorizations/edit.html.erb +53 -0
- data/app/views/decidim/verifications/id_documents/authorizations/new.html.erb +35 -0
- data/app/views/decidim/verifications/postal_letter/admin/pending_authorizations/index.html.erb +50 -0
- data/app/views/decidim/verifications/postal_letter/authorizations/edit.html.erb +37 -0
- data/app/views/decidim/verifications/postal_letter/authorizations/new.html.erb +27 -0
- data/config/locales/ca.yml +126 -0
- data/config/locales/en.yml +126 -0
- data/config/locales/es.yml +126 -0
- data/config/locales/eu.yml +125 -0
- data/config/locales/fi.yml +125 -0
- data/config/locales/fr.yml +125 -0
- data/config/locales/it.yml +125 -0
- data/config/locales/nl.yml +125 -0
- data/config/locales/pl.yml +125 -0
- data/config/locales/pt.yml +125 -0
- data/config/locales/ru.yml +5 -0
- data/config/locales/uk.yml +41 -0
- data/db/migrate/20171030133426_move_authorizations_to_new_api.rb +66 -0
- data/lib/decidim/verifications.rb +26 -0
- data/lib/decidim/verifications/adapter.rb +88 -0
- data/lib/decidim/verifications/dummy.rb +5 -0
- data/lib/decidim/verifications/engine.rb +24 -0
- data/lib/decidim/verifications/id_documents.rb +6 -0
- data/lib/decidim/verifications/id_documents/admin.rb +12 -0
- data/lib/decidim/verifications/id_documents/admin_engine.rb +21 -0
- data/lib/decidim/verifications/id_documents/engine.rb +18 -0
- data/lib/decidim/verifications/id_documents/workflow.rb +6 -0
- data/lib/decidim/verifications/postal_letter.rb +6 -0
- data/lib/decidim/verifications/postal_letter/admin.rb +12 -0
- data/lib/decidim/verifications/postal_letter/admin_engine.rb +21 -0
- data/lib/decidim/verifications/postal_letter/engine.rb +18 -0
- data/lib/decidim/verifications/postal_letter/workflow.rb +6 -0
- data/lib/decidim/verifications/registry.rb +39 -0
- data/lib/decidim/verifications/version.rb +10 -0
- data/lib/decidim/verifications/workflow_manifest.rb +56 -0
- data/lib/decidim/verifications/workflows.rb +58 -0
- metadata +152 -0
@@ -0,0 +1,59 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Verifications
|
5
|
+
module PostalLetter
|
6
|
+
#
|
7
|
+
# Decorator for postal letter authorizations.
|
8
|
+
#
|
9
|
+
class AuthorizationPresenter < SimpleDelegator
|
10
|
+
def self.for_collection(authorizations)
|
11
|
+
authorizations.map { |authorization| new(authorization) }
|
12
|
+
end
|
13
|
+
|
14
|
+
#
|
15
|
+
# The address where the verification code will be sent
|
16
|
+
#
|
17
|
+
def verification_address
|
18
|
+
verification_metadata["address"]
|
19
|
+
end
|
20
|
+
|
21
|
+
#
|
22
|
+
# The verification code to be sent. It's kept in a different metadata
|
23
|
+
# key according to whether it has already been sent or not
|
24
|
+
#
|
25
|
+
def verification_code
|
26
|
+
if letter_sent?
|
27
|
+
verification_metadata["verification_code"]
|
28
|
+
else
|
29
|
+
verification_metadata["pending_verification_code"]
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
#
|
34
|
+
# Whether the letter with the verification code has already been sent or
|
35
|
+
# not
|
36
|
+
#
|
37
|
+
def letter_sent?
|
38
|
+
verification_metadata["verification_code"].present?
|
39
|
+
end
|
40
|
+
|
41
|
+
#
|
42
|
+
# Formatted time when the postal letter was sent. Or an informational
|
43
|
+
# string if not yet sent
|
44
|
+
#
|
45
|
+
def letter_sent_at
|
46
|
+
unless letter_sent?
|
47
|
+
return I18n.t("pending_authorizations.index.not_yet_sent",
|
48
|
+
scope: "decidim.verifications.postal_letter.admin")
|
49
|
+
end
|
50
|
+
|
51
|
+
I18n.l(
|
52
|
+
Time.zone.parse(verification_metadata["letter_sent_at"]),
|
53
|
+
format: :short
|
54
|
+
)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Verifications
|
5
|
+
# Finds authorizations by different criteria
|
6
|
+
class Authorizations < Rectify::Query
|
7
|
+
# Initializes the class.
|
8
|
+
#
|
9
|
+
# @param name [String] The name of an authorization method
|
10
|
+
# @param user [User] A user to find authorizations for
|
11
|
+
# @param granted [Boolean] Whether the authorization is granted or not
|
12
|
+
def initialize(user: nil, name: nil, granted: nil)
|
13
|
+
@user = user
|
14
|
+
@name = name
|
15
|
+
@granted = granted
|
16
|
+
end
|
17
|
+
|
18
|
+
# Finds the Authorizations for the given method
|
19
|
+
#
|
20
|
+
# Returns an ActiveRecord::Relation.
|
21
|
+
def query
|
22
|
+
scope = Decidim::Authorization.where(nil)
|
23
|
+
|
24
|
+
scope = scope.where(name: name) unless name.nil?
|
25
|
+
scope = scope.where(user: user) unless user.nil?
|
26
|
+
|
27
|
+
if granted == true
|
28
|
+
scope = scope.where.not(granted_at: nil)
|
29
|
+
elsif granted == false
|
30
|
+
scope = scope.where(granted_at: nil)
|
31
|
+
end
|
32
|
+
|
33
|
+
scope
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
attr_reader :user, :name, :granted
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
# This is the base class for authorization handlers, all implementations
|
5
|
+
# should inherit from it.
|
6
|
+
# Each AuthorizationHandler is a form that will be used to check if the
|
7
|
+
# authorization is valid or not. When it is valid a new authorization will
|
8
|
+
# be created for the user.
|
9
|
+
#
|
10
|
+
# Feel free to use validations to assert fields against a remote API,
|
11
|
+
# local database, or whatever.
|
12
|
+
#
|
13
|
+
# It also sets two default attributes, `user` and `handler_name`.
|
14
|
+
class AuthorizationHandler < Form
|
15
|
+
# The user that is trying to authorize, it's initialized with the
|
16
|
+
# `current_user` from the controller.
|
17
|
+
attribute :user, Decidim::User
|
18
|
+
|
19
|
+
# The String name of the handler, should not be modified since it's used to
|
20
|
+
# infer the class name of the authorization handler.
|
21
|
+
attribute :handler_name, String
|
22
|
+
|
23
|
+
# A unique ID to be implemented by the authorization handler that ensures
|
24
|
+
# no duplicates are created. This uniqueness check will be skipped if
|
25
|
+
# unique_id returns nil.
|
26
|
+
def unique_id
|
27
|
+
nil
|
28
|
+
end
|
29
|
+
|
30
|
+
# THe attributes of the handler that should be exposed as form input when
|
31
|
+
# rendering the handler in a form.
|
32
|
+
#
|
33
|
+
# Returns an Array of Strings.
|
34
|
+
def form_attributes
|
35
|
+
attributes.except(:id, :user).keys
|
36
|
+
end
|
37
|
+
|
38
|
+
# The String partial path so Rails can render the handler as a form. This
|
39
|
+
# is useful if you want to have a custom view to render the form instead of
|
40
|
+
# the default view.
|
41
|
+
#
|
42
|
+
# Example:
|
43
|
+
#
|
44
|
+
# A handler named Decidim::CensusHandler would look for its partial in:
|
45
|
+
# decidim/census/form
|
46
|
+
#
|
47
|
+
# Returns a String.
|
48
|
+
def to_partial_path
|
49
|
+
handler_name.sub!(/_handler$/, "") + "/form"
|
50
|
+
end
|
51
|
+
|
52
|
+
# Any data that the developer would like to inject to the `metadata` field
|
53
|
+
# of an authorization when it's created. Can be useful if some of the
|
54
|
+
# params the user sent with the authorization form want to be persisted for
|
55
|
+
# future use.
|
56
|
+
#
|
57
|
+
# Returns a Hash.
|
58
|
+
def metadata
|
59
|
+
{}
|
60
|
+
end
|
61
|
+
|
62
|
+
#
|
63
|
+
# Any data to be injected in the `verification_metadata` field of an
|
64
|
+
# authorization when it's created. This data will be used for multi-step
|
65
|
+
# verificaton workflows in order to confirm the authorization.
|
66
|
+
#
|
67
|
+
# Returns a Hash.
|
68
|
+
def verification_metadata
|
69
|
+
{}
|
70
|
+
end
|
71
|
+
|
72
|
+
#
|
73
|
+
# An optional attachment to help out with verification.
|
74
|
+
#
|
75
|
+
def verification_attachment
|
76
|
+
nil
|
77
|
+
end
|
78
|
+
|
79
|
+
# A serialized version of the handler's name.
|
80
|
+
#
|
81
|
+
# Returns a String.
|
82
|
+
def self.handler_name
|
83
|
+
name.demodulize.underscore
|
84
|
+
end
|
85
|
+
|
86
|
+
# Same as the class method but accessible from the instance.
|
87
|
+
#
|
88
|
+
# Returns a String.
|
89
|
+
def handler_name
|
90
|
+
self.class.handler_name
|
91
|
+
end
|
92
|
+
|
93
|
+
# Finds a handler class from a String. This is necessary when processing
|
94
|
+
# the form data. It will only look for valid handlers that have also been
|
95
|
+
# configured in `Decidim.authorization_handlers`.
|
96
|
+
#
|
97
|
+
# name - The String name of the class to find, usually in the same shape as
|
98
|
+
# the one returned by `handler_name`.
|
99
|
+
# params - An optional Hash with params to initialize the handler.
|
100
|
+
#
|
101
|
+
# Returns an AuthorizationHandler descendant.
|
102
|
+
# Returns nil when no handlers could be found.
|
103
|
+
def self.handler_for(name, params = {})
|
104
|
+
return unless name
|
105
|
+
|
106
|
+
manifest = Decidim.authorization_handlers.find { |m| m.name == name }
|
107
|
+
return unless manifest
|
108
|
+
|
109
|
+
manifest.form.constantize.from_params(params || {})
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
# An example implementation of an AuthorizationHandler to be used in tests.
|
5
|
+
class DummyAuthorizationHandler < AuthorizationHandler
|
6
|
+
attribute :document_number, String
|
7
|
+
attribute :postal_code, String
|
8
|
+
attribute :birthday, Date
|
9
|
+
|
10
|
+
validates :document_number, presence: true
|
11
|
+
validate :valid_document_number
|
12
|
+
|
13
|
+
def metadata
|
14
|
+
super.merge(document_number: document_number)
|
15
|
+
end
|
16
|
+
|
17
|
+
def unique_id
|
18
|
+
document_number
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def valid_document_number
|
24
|
+
errors.add(:document_number, :invalid) unless document_number.to_s.end_with?("X")
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Verifications
|
5
|
+
# This class deals with uploading identity documents.
|
6
|
+
class AttachmentUploader < ImageUploader
|
7
|
+
version :thumbnail do
|
8
|
+
process resize_to_limit: [90, nil]
|
9
|
+
end
|
10
|
+
|
11
|
+
version :big do
|
12
|
+
process resize_to_limit: [600, nil]
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
<main class="wrapper">
|
2
|
+
<div class="row collapse">
|
3
|
+
<div class="row collapse">
|
4
|
+
<div class="columns large-8 large-centered text-center">
|
5
|
+
<h1 class="heading1 page-title"><%= t(".title") %></h1>
|
6
|
+
<p class="heading5"><%= t(".verify_with_these_options") %></p>
|
7
|
+
</div>
|
8
|
+
</div>
|
9
|
+
<div class="row">
|
10
|
+
<div class="columns medium-7 large-5 medium-centered">
|
11
|
+
<div class="card">
|
12
|
+
<div class="card__content">
|
13
|
+
<% unauthorized_methods.each do |unauthorized_method| %>
|
14
|
+
<%= link_to t(".actions.#{unauthorized_method.key}"), unauthorized_method.root_path, class: "button expanded" %>
|
15
|
+
<% end %>
|
16
|
+
<p class="text-center skip"><%= t("decidim.verifications.authorizations.skip_verification", link: link_to(t("decidim.verifications.authorizations.start_exploring"), cta_button_path).html_safe).html_safe %>.</p>
|
17
|
+
</div>
|
18
|
+
</div>
|
19
|
+
</div>
|
20
|
+
</div>
|
21
|
+
</div>
|
22
|
+
</main>
|
@@ -0,0 +1,78 @@
|
|
1
|
+
<div class="row column authorizations-list">
|
2
|
+
<section class="section">
|
3
|
+
<% if @granted_authorizations.any? %>
|
4
|
+
<div class="card card--list">
|
5
|
+
<% @granted_authorizations.each do |authorization| %>
|
6
|
+
<div class="card--list__item">
|
7
|
+
<div class="card--list__text">
|
8
|
+
<%= icon "lock-unlocked", class: "card--list__icon" %>
|
9
|
+
<div>
|
10
|
+
<h5 class="card--list__heading">
|
11
|
+
<%= t("#{authorization.name}.name", scope: "decidim.authorization_handlers") %>
|
12
|
+
</h5>
|
13
|
+
<span class="text-small">
|
14
|
+
<%= t("granted_at", scope: "decidim.authorization_handlers", timestamp: l(authorization.granted_at, format: :long)) %>
|
15
|
+
</span>
|
16
|
+
</div>
|
17
|
+
</div>
|
18
|
+
</div>
|
19
|
+
<% end %>
|
20
|
+
</div>
|
21
|
+
<% end %>
|
22
|
+
|
23
|
+
<% if @pending_authorizations.any? %>
|
24
|
+
<div class="card card--list">
|
25
|
+
<% @pending_authorizations.each do |authorization| %>
|
26
|
+
<% authorization_method = Decidim::Verifications::Adapter.from_element(authorization.name) %>
|
27
|
+
<%= link_to authorization_method.resume_authorization_path do %>
|
28
|
+
<div class="card--list__item">
|
29
|
+
<div class="card--list__text">
|
30
|
+
<%= icon "reload", class: "card--list__icon" %>
|
31
|
+
<div>
|
32
|
+
<h5 class="card--list__heading">
|
33
|
+
<%= t("#{authorization.name}.name", scope: "decidim.authorization_handlers") %>
|
34
|
+
</h5>
|
35
|
+
<span class="text-small">
|
36
|
+
<%= t("started_at", scope: "decidim.authorization_handlers", timestamp: l(authorization.updated_at, format: :long)) %>
|
37
|
+
</span>
|
38
|
+
</div>
|
39
|
+
</div>
|
40
|
+
<div class="card--list__data">
|
41
|
+
<span class="card--list__data__icon">
|
42
|
+
<%= icon "chevron-right" %>
|
43
|
+
</span>
|
44
|
+
</div>
|
45
|
+
</div>
|
46
|
+
<% end %>
|
47
|
+
<% end %>
|
48
|
+
</div>
|
49
|
+
<% end %>
|
50
|
+
|
51
|
+
<% if unauthorized_methods.any? %>
|
52
|
+
<div class="card card--list">
|
53
|
+
<% unauthorized_methods.each do |unauthorized_method| %>
|
54
|
+
<%= link_to unauthorized_method.root_path do %>
|
55
|
+
<div class="card--list__item">
|
56
|
+
<div class="card--list__text">
|
57
|
+
<%= icon "lock-locked", class: "card--list__icon" %>
|
58
|
+
<div>
|
59
|
+
<h5 class="card--list__heading">
|
60
|
+
<%= t("#{unauthorized_method.key}.name", scope: "decidim.authorization_handlers") %>
|
61
|
+
</h5>
|
62
|
+
<span class="text-small">
|
63
|
+
<%= t("#{unauthorized_method.key}.explanation", scope: "decidim.authorization_handlers") %>
|
64
|
+
</span>
|
65
|
+
</div>
|
66
|
+
</div>
|
67
|
+
<div class="card--list__data">
|
68
|
+
<span class="card--list__data__icon">
|
69
|
+
<%= icon "chevron-right" %>
|
70
|
+
</span>
|
71
|
+
</div>
|
72
|
+
</div>
|
73
|
+
<% end %>
|
74
|
+
<% end %>
|
75
|
+
</div>
|
76
|
+
<% end %>
|
77
|
+
</section>
|
78
|
+
</div>
|
@@ -0,0 +1,33 @@
|
|
1
|
+
<main class="wrapper">
|
2
|
+
<div class="row collapse">
|
3
|
+
<div class="row collapse">
|
4
|
+
<div class="columns large-8 large-centered text-center page-title">
|
5
|
+
<h1><%= t(".authorize_with", authorizer: t("#{params[:handler]}.name", scope: "decidim.authorization_handlers")) %></h1>
|
6
|
+
</div>
|
7
|
+
</div>
|
8
|
+
|
9
|
+
<div class="row">
|
10
|
+
<div class="columns large-6 medium-centered">
|
11
|
+
<div class="card">
|
12
|
+
<div class="card__content">
|
13
|
+
<%= authorization_form_for(handler, url: authorizations_path(redirect_url: params[:redirect_url])) do |form| %>
|
14
|
+
<%= form.error_for(:base) %>
|
15
|
+
|
16
|
+
<% if lookup_context.exists?(handler.to_partial_path, [], true) %>
|
17
|
+
<%= render partial: handler.to_partial_path, locals: { handler: handler, form: form } %>
|
18
|
+
<% else %>
|
19
|
+
<%= form.all_fields %>
|
20
|
+
<div class="actions">
|
21
|
+
<%= form.submit t(".authorize"), class: "button expanded" %>
|
22
|
+
</div>
|
23
|
+
<% end %>
|
24
|
+
<% end %>
|
25
|
+
<p class="text-center skip">
|
26
|
+
<%= t("decidim.verifications.authorizations.skip_verification", link: link_to(t("decidim.verifications.authorizations.start_exploring"), cta_button_path).html_safe).html_safe %>.
|
27
|
+
</p>
|
28
|
+
</div>
|
29
|
+
</div>
|
30
|
+
</div>
|
31
|
+
</div>
|
32
|
+
</div>
|
33
|
+
</main>
|
@@ -0,0 +1,26 @@
|
|
1
|
+
<%= decidim_form_for(@form, url: pending_authorization_confirmation_path(@pending_authorization),
|
2
|
+
html: { class: "form" }) do |form| %>
|
3
|
+
<div class="card">
|
4
|
+
<div class="card-divider">
|
5
|
+
<h2 class="card-title">
|
6
|
+
<%= t(".introduce_user_data") %>
|
7
|
+
<%= link_to t(".reject"), pending_authorization_rejection_path(@pending_authorization), method: :post, class: "button alert tiny button--title destroy" %>
|
8
|
+
</h2>
|
9
|
+
</div>
|
10
|
+
|
11
|
+
<div class="card-section">
|
12
|
+
<div class="row column">
|
13
|
+
<%= image_tag @pending_authorization.verification_attachment.url(:big), class: "thumbnail" %>
|
14
|
+
</div>
|
15
|
+
|
16
|
+
<div class="row column">
|
17
|
+
<%= form.select :document_type, @form.document_types_for_select, prompt: true, selected: nil %>
|
18
|
+
<%= form.text_field :document_number, value: nil %>
|
19
|
+
</div>
|
20
|
+
</div>
|
21
|
+
</div>
|
22
|
+
|
23
|
+
<div class="button--double form-general-submit">
|
24
|
+
<%= form.submit t(".verify") %>
|
25
|
+
</div>
|
26
|
+
<% end %>
|
@@ -0,0 +1,32 @@
|
|
1
|
+
<div class="card">
|
2
|
+
<div class="card-divider">
|
3
|
+
<h2 class="card-title">
|
4
|
+
<%= t(".title") %>
|
5
|
+
</h2>
|
6
|
+
</div>
|
7
|
+
<div class="card-section">
|
8
|
+
<table class="table-list">
|
9
|
+
<thead>
|
10
|
+
<tr>
|
11
|
+
<th><%= t("activemodel.attributes.id") %></th>
|
12
|
+
<th><%= t("activemodel.attributes.id_document_upload.verification_attachment") %></th>
|
13
|
+
</tr>
|
14
|
+
</thead>
|
15
|
+
<tbody>
|
16
|
+
<tr>
|
17
|
+
<% @pending_authorizations.each do |authorization| %>
|
18
|
+
<td>
|
19
|
+
<%= link_to t(".verification_number", n: authorization.id),
|
20
|
+
new_pending_authorization_confirmation_path(authorization.id) %>
|
21
|
+
</td>
|
22
|
+
<td>
|
23
|
+
<%= link_to new_pending_authorization_confirmation_path(authorization.id) do %>
|
24
|
+
<%= image_tag authorization.verification_attachment.url(:thumbnail), class: "thumbnail-list" %>
|
25
|
+
<% end %>
|
26
|
+
</td>
|
27
|
+
<% end %>
|
28
|
+
</tr>
|
29
|
+
</tbody>
|
30
|
+
</table>
|
31
|
+
</div>
|
32
|
+
</div>
|