lato 3.25.0 → 3.26.0
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.
- checksums.yaml +4 -4
- data/README.md +49 -20
- data/app/assets/javascripts/lato/controllers/lato_account_webauthn_controller.js +8 -5
- data/app/assets/javascripts/lato/controllers/lato_action_controller.js +26 -15
- data/app/assets/javascripts/lato/controllers/lato_confirm_controller.js +25 -15
- data/app/assets/javascripts/lato/controllers/lato_copyclip_controller.js +28 -10
- data/app/assets/javascripts/lato/controllers/lato_form_controller.js +1 -0
- data/app/assets/javascripts/lato/controllers/lato_index_controller.js +4 -1
- data/app/controllers/concerns/lato/componentable.rb +20 -8
- data/app/controllers/concerns/lato/sessionable.rb +39 -10
- data/app/controllers/lato/account_controller.rb +10 -8
- data/app/controllers/lato/application_controller.rb +25 -1
- data/app/controllers/lato/authentication_controller.rb +74 -39
- data/app/helpers/lato/components_helper.rb +42 -16
- data/app/jobs/lato/active_storage_cleaner_job.rb +8 -7
- data/app/models/lato/invitation.rb +1 -1
- data/app/models/lato/operation.rb +6 -2
- data/app/models/lato/user.rb +39 -9
- data/app/views/lato/account/_alert-accepted-privacy-policy-version.html.erb +1 -1
- data/app/views/lato/account/_alert-accepted-terms-and-conditions-version.html.erb +1 -1
- data/app/views/lato/account/_form-authenticator.html.erb +7 -3
- data/app/views/lato/account/_form-password.html.erb +3 -2
- data/app/views/lato/account/_form-web3.html.erb +4 -3
- data/app/views/lato/account/_security-confirmation.html.erb +3 -3
- data/app/views/lato/authentication/_fields-registration.html.erb +8 -7
- data/app/views/lato/authentication/_form-authenticator.html.erb +2 -2
- data/app/views/lato/authentication/_form-signin.html.erb +2 -2
- data/app/views/lato/authentication/_form-verify-email.html.erb +3 -2
- data/app/views/lato/authentication/_form-web3-signin.html.erb +4 -3
- data/app/views/lato/components/_index.html.erb +25 -20
- data/app/views/lato/components/_navbar_nav_item.html.erb +1 -1
- data/app/views/lato/components/_operation.html.erb +4 -1
- data/app/views/lato/components/_page_head.html.erb +1 -1
- data/app/views/lato/components/_sidebar_nav_item.html.erb +2 -1
- data/app/views/lato/mailer/invitation/invite_mail.html.erb +1 -1
- data/app/views/lato/mailer/user/email_verification_mail.html.erb +1 -1
- data/app/views/lato/mailer/user/password_update_mail.html.erb +1 -1
- data/app/views/lato/mailer/user/signin_success_mail.html.erb +1 -1
- data/app/views/layouts/lato/_action.html.erb +2 -2
- data/app/views/layouts/lato/_aside-opener.html.erb +2 -2
- data/app/views/layouts/lato/_confirm.html.erb +6 -8
- data/app/views/layouts/lato/_feedbacks.html.erb +9 -8
- data/app/views/layouts/lato/_mailer-head_content.html.erb +1 -1
- data/app/views/layouts/lato/_network.html.erb +3 -3
- data/app/views/layouts/lato/application.html.erb +3 -2
- data/config/locales/en.yml +32 -0
- data/config/locales/fr.yml +29 -0
- data/config/locales/it.yml +29 -0
- data/config/locales/ro.yml +29 -0
- data/db/migrate/20261110000000_add_second_factor_counters_to_lato_users.rb +6 -0
- data/lib/lato/config.rb +4 -0
- data/lib/lato/engine.rb +5 -0
- data/lib/lato/version.rb +1 -1
- metadata +22 -15
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
<div class="mb-3" data-controller="lato-account-security" data-lato-account-security-challenge-url-value="<%= lato.account_security_challenge_path %>" data-lato-account-security-webauthn-value="<%= user.webauthn_enabled? && !user.authenticator_enabled? %>">
|
|
2
|
-
<%= form.label :current_password,
|
|
2
|
+
<%= form.label :current_password, I18n.t('lato.current_password'), class: "form-label" %>
|
|
3
3
|
<%= form.password_field :current_password, class: "form-control", autocomplete: "current-password" %>
|
|
4
4
|
<% if user.authenticator_enabled? %>
|
|
5
|
-
<%= form.label :current_authenticator_code,
|
|
5
|
+
<%= form.label :current_authenticator_code, I18n.t('lato.current_authenticator_code'), class: "form-label mt-2" %>
|
|
6
6
|
<%= form.text_field :current_authenticator_code, value: nil, class: "form-control", autocomplete: "one-time-code", inputmode: "numeric" %>
|
|
7
7
|
<% elsif user.webauthn_enabled? %>
|
|
8
8
|
<%= form.hidden_field :current_webauthn_credential, value: nil, data: { lato_account_security_target: "credential" } %>
|
|
9
|
-
<p class="text-muted mt-2"
|
|
9
|
+
<p class="text-muted mt-2"><%= I18n.t('lato.security_key_confirmation_help') %></p>
|
|
10
10
|
<% end %>
|
|
11
11
|
<p class="text-danger mt-2" data-lato-account-security-target="error" role="alert"></p>
|
|
12
12
|
</div>
|
|
@@ -7,31 +7,32 @@ email_readonly ||= false
|
|
|
7
7
|
<div class="row">
|
|
8
8
|
<div class="col col-12 col-md-6 mb-3">
|
|
9
9
|
<%= lato_form_item_label form, :first_name %>
|
|
10
|
-
<%= lato_form_item_input_text form, :first_name, required: true %>
|
|
10
|
+
<%= lato_form_item_input_text form, :first_name, required: true, autocomplete: 'given-name' %>
|
|
11
11
|
</div>
|
|
12
12
|
|
|
13
13
|
<div class="col col-12 col-md-6 mb-3">
|
|
14
14
|
<%= lato_form_item_label form, :last_name %>
|
|
15
|
-
<%= lato_form_item_input_text form, :last_name, required: true %>
|
|
15
|
+
<%= lato_form_item_input_text form, :last_name, required: true, autocomplete: 'family-name' %>
|
|
16
16
|
</div>
|
|
17
17
|
|
|
18
18
|
<div class="col col-12 mb-3">
|
|
19
19
|
<%= lato_form_item_label form, :email %>
|
|
20
|
-
<%= lato_form_item_input_email form, :email, required: true, readonly: email_readonly %>
|
|
20
|
+
<%= lato_form_item_input_email form, :email, required: true, readonly: email_readonly, autocomplete: 'email' %>
|
|
21
21
|
</div>
|
|
22
22
|
|
|
23
23
|
<div class="col col-12 col-md-6 mb-3">
|
|
24
24
|
<%= lato_form_item_label form, :password %>
|
|
25
|
-
<%= lato_form_item_input_password form, :password, required: true %>
|
|
25
|
+
<%= lato_form_item_input_password form, :password, required: true, autocomplete: 'new-password', minlength: Lato.config.auth_password_min_length, aria: { describedby: form.field_id(:password, :help) } %>
|
|
26
|
+
<div class="form-text" id="<%= form.field_id(:password, :help) %>"><%= I18n.t('lato.password_requirements', count: Lato.config.auth_password_min_length) %></div>
|
|
26
27
|
</div>
|
|
27
28
|
|
|
28
29
|
<div class="col col-12 col-md-6 mb-3">
|
|
29
30
|
<%= lato_form_item_label form, :password_confirmation %>
|
|
30
|
-
<%= lato_form_item_input_password form, :password_confirmation, required: true %>
|
|
31
|
+
<%= lato_form_item_input_password form, :password_confirmation, required: true, autocomplete: 'new-password' %>
|
|
31
32
|
</div>
|
|
32
33
|
</div>
|
|
33
34
|
|
|
34
35
|
<div class="mb-3 text-muted" style="font-size: 14px;">
|
|
35
|
-
<%= lato_form_item_input_check form, :accepted_privacy_policy_version, I18n.t('lato.privacy_policy_checkbox', link: link_to(I18n.t('lato.privacy_policy'), Lato.config.legal_privacy_policy_url, target: '_blank')), required: true %>
|
|
36
|
-
<%= lato_form_item_input_check form, :accepted_terms_and_conditions_version, I18n.t('lato.terms_and_conditions_checkbox', link: link_to(I18n.t('lato.terms_and_conditions'), Lato.config.legal_terms_and_conditions_url, target: '_blank')), required: true %>
|
|
36
|
+
<%= lato_form_item_input_check form, :accepted_privacy_policy_version, I18n.t('lato.privacy_policy_checkbox', link: link_to(I18n.t('lato.privacy_policy'), Lato.config.legal_privacy_policy_url, target: '_blank')).html_safe, required: true %>
|
|
37
|
+
<%= lato_form_item_input_check form, :accepted_terms_and_conditions_version, I18n.t('lato.terms_and_conditions_checkbox', link: link_to(I18n.t('lato.terms_and_conditions'), Lato.config.legal_terms_and_conditions_url, target: '_blank')).html_safe, required: true %>
|
|
37
38
|
</div>
|
|
@@ -10,11 +10,11 @@ user ||= Lato::User.new
|
|
|
10
10
|
<%= lato_form_errors user, class: %w[mb-3] %>
|
|
11
11
|
|
|
12
12
|
<div class="mb-3 text-center">
|
|
13
|
-
|
|
13
|
+
<%= form.label :authenticator_code, raw(I18n.t('lato.authenticator_code_help', email: h(user.email_protected))), class: 'form-label' %>
|
|
14
14
|
</div>
|
|
15
15
|
|
|
16
16
|
<div class="mb-3">
|
|
17
|
-
<%= lato_form_item_input_text form, :authenticator_code, required: true %>
|
|
17
|
+
<%= lato_form_item_input_text form, :authenticator_code, required: true, autocomplete: 'one-time-code', inputmode: 'numeric', pattern: '[0-9]{6}', maxlength: 6, autofocus: true %>
|
|
18
18
|
</div>
|
|
19
19
|
|
|
20
20
|
<div>
|
|
@@ -11,12 +11,12 @@ user ||= Lato::User.new
|
|
|
11
11
|
|
|
12
12
|
<div class="mb-3">
|
|
13
13
|
<%= lato_form_item_label form, :email %>
|
|
14
|
-
<%= lato_form_item_input_email form, :email, required: true %>
|
|
14
|
+
<%= lato_form_item_input_email form, :email, required: true, autocomplete: 'username' %>
|
|
15
15
|
</div>
|
|
16
16
|
|
|
17
17
|
<div class="mb-3">
|
|
18
18
|
<%= lato_form_item_label form, :password %>
|
|
19
|
-
<%= lato_form_item_input_password form, :password, required: true %>
|
|
19
|
+
<%= lato_form_item_input_password form, :password, required: true, autocomplete: 'current-password' %>
|
|
20
20
|
</div>
|
|
21
21
|
|
|
22
22
|
<div>
|
|
@@ -10,13 +10,14 @@ code ||= nil
|
|
|
10
10
|
<%= form_with model: user, url: lato.authentication_verify_email_action_path(id: user.id), class: 'text-center', data: { turbo_frame: '_self' } do |form| %>
|
|
11
11
|
<% if code %>
|
|
12
12
|
<%= form.hidden_field :code, value: code %>
|
|
13
|
-
|
|
13
|
+
<%# Visible fallback when JavaScript is disabled or blocked by CSP. %>
|
|
14
|
+
<button type="submit" class="btn btn-primary"><%= I18n.t('lato.verify_email') %></button>
|
|
14
15
|
<script>document.getElementById('authentication_form_verify_email').querySelector('button[type="submit"]').click()</script>
|
|
15
16
|
<% else %>
|
|
16
17
|
<%= lato_form_notices class: %w[mb-3], fixed: true %>
|
|
17
18
|
<%= lato_form_errors user, class: %w[mb-3], fixed: true %>
|
|
18
19
|
|
|
19
|
-
<%= link_to '
|
|
20
|
+
<%= link_to I18n.t('lato.back_to_account'), lato.account_path, class: %w[btn btn-primary] %>
|
|
20
21
|
<% end %>
|
|
21
22
|
<% end %>
|
|
22
23
|
<% end %>
|
|
@@ -12,7 +12,7 @@ user ||= Lato::User.new
|
|
|
12
12
|
<% if session[:web3_nonce] %>
|
|
13
13
|
|
|
14
14
|
<div class="alert alert-light mb-0 text-center">
|
|
15
|
-
<h4 class="alert-heading"
|
|
15
|
+
<h4 class="alert-heading" role="status"><%= I18n.t('lato.connecting') %></h4>
|
|
16
16
|
<div class="progress" role="progressbar" aria-valuenow="100" aria-valuemin="0" aria-valuemax="100">
|
|
17
17
|
<div class="progress-bar progress-bar-striped progress-bar-animated" style="width: 100%"></div>
|
|
18
18
|
</div>
|
|
@@ -21,13 +21,14 @@ user ||= Lato::User.new
|
|
|
21
21
|
<span id="account_form-web3__nonce" style="display: none;"><%= session[:web3_nonce] %></span>
|
|
22
22
|
<%= form.hidden_field :web3_address, id: 'account_form-web3__input-web3_address' %>
|
|
23
23
|
<%= form.hidden_field :web3_signed_nonce, id: 'account_form-web3__input-web3_signed_nonce' %>
|
|
24
|
-
<%= lato_form_submit form, '
|
|
24
|
+
<%= lato_form_submit form, I18n.t('lato.confirm'), class: %w[btn-primary d-none], id: 'account_form-web3__submit' %>
|
|
25
25
|
|
|
26
26
|
<script>
|
|
27
27
|
(async () => {
|
|
28
|
+
// Pinned exact version: dynamic import() can not carry an SRI hash (see README security checklist).
|
|
28
29
|
const ethers = await import('https://cdnjs.cloudflare.com/ajax/libs/ethers/5.7.2/ethers.esm.min.js')
|
|
29
30
|
|
|
30
|
-
const nonce = document.getElementById('account_form-web3__nonce').
|
|
31
|
+
const nonce = document.getElementById('account_form-web3__nonce').textContent
|
|
31
32
|
const inputAddress = document.getElementById('account_form-web3__input-web3_address')
|
|
32
33
|
const inputSignedNonce = document.getElementById('account_form-web3__input-web3_signed_nonce')
|
|
33
34
|
const submitButton = document.getElementById('account_form-web3__submit')
|
|
@@ -9,9 +9,10 @@ total_pages = collection.empty? ? params["#{key}_page"]&.to_i : 999_999_999 if s
|
|
|
9
9
|
|
|
10
10
|
<%= turbo_frame_tag "lato_index_#{key}_#{model_name_underscore}", class: "lato-index #{browser.device.mobile? ? 'lato-index-mob' : 'lato-index-desk'}" do %>
|
|
11
11
|
|
|
12
|
-
<%= form_tag request.path, method: :get, data: { controller: 'lato-form lato-index', turbo_frame: '_self' } do %>
|
|
12
|
+
<%= form_tag request.path, method: :get, data: { controller: 'lato-form lato-index', lato_index_key_value: key, turbo_frame: '_self' } do %>
|
|
13
13
|
<%= hidden_field_tag :key, key %>
|
|
14
|
-
<% params.each do |k, v| %>
|
|
14
|
+
<% params.except(:controller, :action, :key).each do |k, v| %>
|
|
15
|
+
<% next unless v.is_a?(String) %>
|
|
15
16
|
<%= hidden_field_tag k, v %>
|
|
16
17
|
<% end %>
|
|
17
18
|
|
|
@@ -33,7 +34,7 @@ total_pages = collection.empty? ? params["#{key}_page"]&.to_i : 999_999_999 if s
|
|
|
33
34
|
<% if custom_actions.any? %>
|
|
34
35
|
<div class="btn-group ms-2">
|
|
35
36
|
<% custom_actions.each do |action_key, action_params| %>
|
|
36
|
-
<%= link_to action_params[:path], { class: 'btn btn-primary' }.merge(action_params) do %>
|
|
37
|
+
<%= link_to action_params[:path], { class: 'btn btn-primary', 'aria-label': action_params[:title] }.merge(action_params) do %>
|
|
37
38
|
<i class="<%= action_params[:icon] %>"></i>
|
|
38
39
|
<% end %>
|
|
39
40
|
<% end %>
|
|
@@ -42,7 +43,7 @@ total_pages = collection.empty? ? params["#{key}_page"]&.to_i : 999_999_999 if s
|
|
|
42
43
|
|
|
43
44
|
<% if dropdown_actions %>
|
|
44
45
|
<div class="dropdown ms-2">
|
|
45
|
-
<%= content_tag :button, { class: 'btn btn-primary dropdown-toggle', 'data-bs-toggle': 'dropdown' }.merge(dropdown_actions) do %>
|
|
46
|
+
<%= content_tag :button, { class: 'btn btn-primary dropdown-toggle', 'data-bs-toggle': 'dropdown', 'aria-label': dropdown_actions[:title] }.merge(dropdown_actions) do %>
|
|
46
47
|
<i class="<%= dropdown_actions[:icon] %>"></i>
|
|
47
48
|
<% end %>
|
|
48
49
|
<ul class="dropdown-menu">
|
|
@@ -109,28 +110,32 @@ total_pages = collection.empty? ? params["#{key}_page"]&.to_i : 999_999_999 if s
|
|
|
109
110
|
<thead>
|
|
110
111
|
<tr class="align-middle">
|
|
111
112
|
<% columns.each do |column| %>
|
|
112
|
-
|
|
113
|
+
<% column_label = collection_test_istance.respond_to?(column) ? collection.model.human_attribute_name(column) : lato_index_dynamic_label({
|
|
114
|
+
key: key,
|
|
115
|
+
model_name: model_name,
|
|
116
|
+
column: column
|
|
117
|
+
}) %>
|
|
118
|
+
<% sort_direction = sortable_columns.include?(column) ? { "#{column}|ASC" => 'ascending', "#{column}|DESC" => 'descending' }[params["#{key}_sort_by"]] : nil %>
|
|
119
|
+
<%= content_tag :th, scope: 'col', class: "lato-index-desk-col-label lato-index-desk-col-label-#{column}", 'aria-sort': sort_direction do %>
|
|
113
120
|
<div class="d-flex align-items-center justify-content-between">
|
|
114
|
-
<span><%=
|
|
115
|
-
key: key,
|
|
116
|
-
model_name: model_name,
|
|
117
|
-
column: column
|
|
118
|
-
}) %></span>
|
|
121
|
+
<span><%= column_label %></span>
|
|
119
122
|
<% if sortable_columns.include?(column) %>
|
|
120
123
|
<div class="btn-group ms-3 lato-index-col-sort">
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
<
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
124
|
+
<% [['ASC', 'ascending', 'bi-sort-up'], ['DESC', 'descending', 'bi-sort-down']].each do |direction, direction_name, icon| %>
|
|
125
|
+
<% active = params["#{key}_sort_by"] == "#{column}|#{direction}" %>
|
|
126
|
+
<button
|
|
127
|
+
type="submit"
|
|
128
|
+
name="<%= "#{key}_sort_by" %>"
|
|
129
|
+
value="<%= column %>|<%= direction %>"
|
|
130
|
+
class="btn btn-sm <%= active ? 'btn-primary' : 'btn-outline-primary' %>"
|
|
131
|
+
aria-label="<%= I18n.t("lato.sort_#{direction_name}", column: strip_tags(column_label.to_s)) %>"
|
|
132
|
+
aria-pressed="<%= active %>"
|
|
133
|
+
><i class="bi <%= icon %>" aria-hidden="true"></i></button>
|
|
134
|
+
<% end %>
|
|
130
135
|
</div>
|
|
131
136
|
<% end %>
|
|
132
137
|
</div>
|
|
133
|
-
|
|
138
|
+
<% end %>
|
|
134
139
|
<% end %>
|
|
135
140
|
</tr>
|
|
136
141
|
</thead>
|
|
@@ -47,7 +47,10 @@
|
|
|
47
47
|
|
|
48
48
|
<% if operation.output_action? %>
|
|
49
49
|
<div class="mt-3 d-flex justify-content-center">
|
|
50
|
-
|
|
50
|
+
<%# Only http(s) and relative links: a javascript: href built from job input would run on click. %>
|
|
51
|
+
<% output_href = operation.output_action['href'].to_s %>
|
|
52
|
+
<% output_href = '#' unless output_href.start_with?('/', 'http://', 'https://') && !output_href.start_with?('//') %>
|
|
53
|
+
<%= link_to operation.output_action['label'], output_href, { class: 'btn btn-primary' }.merge(operation.output_action['attributes'] || {}) %>
|
|
51
54
|
</div>
|
|
52
55
|
<% end %>
|
|
53
56
|
</div>
|
|
@@ -9,6 +9,7 @@ unless has_children
|
|
|
9
9
|
class: ['nav-link position-relative', (active ? 'active' : 'link-dark')].join(' '),
|
|
10
10
|
}
|
|
11
11
|
link_to_options[:target] = '_blank' if external
|
|
12
|
+
link_to_options[:aria] = { current: 'page' } if active
|
|
12
13
|
end
|
|
13
14
|
|
|
14
15
|
%>
|
|
@@ -28,7 +29,7 @@ end
|
|
|
28
29
|
<ul class="list-unstyled mt-2 border-start border-1 ps-2">
|
|
29
30
|
<% children.each do |child| %>
|
|
30
31
|
<li class="py-1">
|
|
31
|
-
<%= link_to child[:path], class: ['nav-link py-1', (active == child[:key] ? 'active' : 'link-dark')].join(' ') do %>
|
|
32
|
+
<%= link_to child[:path], class: ['nav-link py-1', (active == child[:key] ? 'active' : 'link-dark')].join(' '), aria: { current: ('page' if active == child[:key]) } do %>
|
|
32
33
|
<%= child[:label] %>
|
|
33
34
|
<% end %>
|
|
34
35
|
</li>
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
<% 10.times do %>
|
|
2
2
|
|
|
3
|
-
<div class="modal fade" data-lato-action-target="modal" tabindex="-1" aria-hidden="true" data-bs-backdrop="static"
|
|
3
|
+
<div class="modal fade" data-lato-action-target="modal" tabindex="-1" aria-hidden="true" data-bs-backdrop="static">
|
|
4
4
|
<div class="modal-dialog modal-dialog-centered modal-dialog-scrollable" data-lato-action-target="modalDialog">
|
|
5
5
|
<div class="modal-content">
|
|
6
6
|
<div class="modal-header">
|
|
7
7
|
<h2 class="mb-0 mt-0 fs-4" data-lato-action-target="modalTitle"></h2>
|
|
8
|
-
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="
|
|
8
|
+
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="<%= I18n.t('lato.close') %>"></button>
|
|
9
9
|
</div>
|
|
10
10
|
<div class="modal-body" data-lato-action-target="modalBody">
|
|
11
11
|
...
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
class="aside-opener shadow"
|
|
3
3
|
data-controller="lato-aside-opener"
|
|
4
4
|
data-action="click->lato-aside-opener#onClickToggle"
|
|
5
|
-
aria-label="
|
|
5
|
+
aria-label="<%= I18n.t('lato.toggle_sidebar') %>"
|
|
6
6
|
>
|
|
7
|
-
<i class="bi bi-layout-text-sidebar-reverse"></i>
|
|
7
|
+
<i class="bi bi-layout-text-sidebar-reverse" aria-hidden="true"></i>
|
|
8
8
|
</button>
|
|
@@ -1,26 +1,24 @@
|
|
|
1
|
-
<div class="lato-confirm modal fade" data-controller="lato-confirm" tabindex="-1" aria-hidden="true"
|
|
1
|
+
<div class="lato-confirm modal fade" data-controller="lato-confirm" tabindex="-1" aria-hidden="true" aria-labelledby="latoConfirmTitle">
|
|
2
2
|
<div class="modal-dialog modal-dialog-centered modal-dialog-scrollable">
|
|
3
3
|
<div class="modal-content">
|
|
4
4
|
<div class="modal-header">
|
|
5
|
-
<h2 class="mb-0 mt-0 fs-4"><%= I18n.t('lato.confirm_title') %></h2>
|
|
6
|
-
<button type="button" class="btn-close" data-action="click->lato-confirm#confirmCancel" aria-label="
|
|
5
|
+
<h2 class="mb-0 mt-0 fs-4" id="latoConfirmTitle"><%= I18n.t('lato.confirm_title') %></h2>
|
|
6
|
+
<button type="button" class="btn-close" data-action="click->lato-confirm#confirmCancel" aria-label="<%= I18n.t('lato.close') %>"></button>
|
|
7
7
|
</div>
|
|
8
8
|
<div class="modal-body" data-lato-confirm-target="modalBody">
|
|
9
9
|
</div>
|
|
10
10
|
<div class="modal-footer">
|
|
11
11
|
<button
|
|
12
12
|
type="button"
|
|
13
|
-
class="btn btn-
|
|
13
|
+
class="btn btn-secondary"
|
|
14
14
|
data-action="click->lato-confirm#confirmCancel"
|
|
15
|
-
aria-label="<%= I18n.t('lato.confirm_no') %>"
|
|
16
15
|
><%= I18n.t('lato.confirm_no') %></button>
|
|
17
16
|
<button
|
|
18
17
|
type="button"
|
|
19
|
-
class="btn btn-
|
|
18
|
+
class="btn btn-primary"
|
|
20
19
|
data-action="click->lato-confirm#confirmSuccess"
|
|
21
|
-
aria-label="<%= I18n.t('lato.confirm_yes') %>"
|
|
22
20
|
><%= I18n.t('lato.confirm_yes') %></button>
|
|
23
21
|
</div>
|
|
24
22
|
</div>
|
|
25
23
|
</div>
|
|
26
|
-
</div>
|
|
24
|
+
</div>
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
<div class="toast-container">
|
|
2
2
|
<% if notice %>
|
|
3
3
|
<div class="position-fixed end-0 bottom-0 p-3" style="z-index: 11;">
|
|
4
|
-
<div data-controller="lato-feedback" id="feedbackNotice" class="toast hide" role="
|
|
4
|
+
<div data-controller="lato-feedback" id="feedbackNotice" class="toast hide" role="status" aria-live="polite" aria-atomic="true">
|
|
5
5
|
<div class="toast-header">
|
|
6
|
-
<i class="bi bi-check-circle-fill text-success me-2"></i>
|
|
7
|
-
<strong class="me-auto"><%= I18n.t('lato.
|
|
8
|
-
<button type="button" class="btn-close" data-bs-dismiss="toast" aria-label="
|
|
6
|
+
<i class="bi bi-check-circle-fill text-success me-2" aria-hidden="true"></i>
|
|
7
|
+
<strong class="me-auto"><%= I18n.t('lato.notice_title') %></strong>
|
|
8
|
+
<button type="button" class="btn-close" data-bs-dismiss="toast" aria-label="<%= I18n.t('lato.close') %>"></button>
|
|
9
9
|
</div>
|
|
10
10
|
<div class="toast-body">
|
|
11
11
|
<%= notice %>
|
|
@@ -16,11 +16,12 @@
|
|
|
16
16
|
|
|
17
17
|
<% if alert %>
|
|
18
18
|
<div class="position-fixed end-0 bottom-0 p-3" style="z-index: 11;">
|
|
19
|
-
|
|
19
|
+
<%# Errors stay visible until dismissed: auto hiding gives no time to read them. %>
|
|
20
|
+
<div data-controller="lato-feedback" id="feedbackAlert" class="toast hide" role="alert" aria-live="assertive" aria-atomic="true" data-bs-autohide="false">
|
|
20
21
|
<div class="toast-header">
|
|
21
|
-
<i class="bi bi-
|
|
22
|
-
<strong class="me-auto"><%= I18n.t('lato.
|
|
23
|
-
<button type="button" class="btn-close" data-bs-dismiss="toast" aria-label="
|
|
22
|
+
<i class="bi bi-exclamation-triangle-fill text-danger me-2" aria-hidden="true"></i>
|
|
23
|
+
<strong class="me-auto"><%= I18n.t('lato.alert_title') %></strong>
|
|
24
|
+
<button type="button" class="btn-close" data-bs-dismiss="toast" aria-label="<%= I18n.t('lato.close') %>"></button>
|
|
24
25
|
</div>
|
|
25
26
|
<div class="toast-body">
|
|
26
27
|
<%= alert %>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<div class="fixed-bottom p-3 d-none" data-controller="lato-network">
|
|
2
|
-
<span class="badge rounded-pill text-bg-danger fs-6 d-none" data-lato-network-target="offline"
|
|
3
|
-
<span class="badge rounded-pill text-bg-success fs-6 d-none" data-lato-network-target="online"
|
|
1
|
+
<div class="fixed-bottom p-3 d-none" data-controller="lato-network" role="status" aria-live="polite">
|
|
2
|
+
<span class="badge rounded-pill text-bg-danger fs-6 d-none" data-lato-network-target="offline"><%= I18n.t('lato.network_offline') %></span>
|
|
3
|
+
<span class="badge rounded-pill text-bg-success fs-6 d-none" data-lato-network-target="online"><%= I18n.t('lato.network_online') %></span>
|
|
4
4
|
</div>
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="utf-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no,interactive-widget=resizes-content">
|
|
6
|
-
<title><%= @layout_page_title %></title>
|
|
6
|
+
<title><%= @layout_page_title.presence || Lato.config.application_title %></title>
|
|
7
7
|
<%= csrf_meta_tags %>
|
|
8
8
|
<%= csp_meta_tag %>
|
|
9
9
|
|
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
class="controller-<%= controller_name %> action-<%= action_name %> <%= @layout_body_classes.join(' ') %> <%= cookies[:lato_aside_hidden] == 'true' ? 'aside-hidden' : '' %>"
|
|
28
28
|
data-controller="lato-action lato-guide"
|
|
29
29
|
>
|
|
30
|
+
<a class="visually-hidden-focusable position-absolute top-0 start-0 p-2 bg-white" style="z-index: 2100;" href="#main"><%= I18n.t('lato.skip_to_content') %></a>
|
|
30
31
|
<header>
|
|
31
32
|
<%= render 'layouts/lato/navbar' %>
|
|
32
33
|
</header>
|
|
@@ -39,7 +40,7 @@
|
|
|
39
40
|
</aside>
|
|
40
41
|
<% end %>
|
|
41
42
|
|
|
42
|
-
<main class="<%= @layout_sidebar ? 'layout_sidebar' : '' %>">
|
|
43
|
+
<main id="main" tabindex="-1" class="<%= @layout_sidebar ? 'layout_sidebar' : '' %>">
|
|
43
44
|
<%= render 'layouts/lato/content' %>
|
|
44
45
|
</main>
|
|
45
46
|
</div>
|
data/config/locales/en.yml
CHANGED
|
@@ -1,5 +1,28 @@
|
|
|
1
1
|
en:
|
|
2
2
|
lato:
|
|
3
|
+
close: "Close"
|
|
4
|
+
notice_title: "Notification"
|
|
5
|
+
alert_title: "Error"
|
|
6
|
+
sort_ascending: "Sort by %{column} ascending"
|
|
7
|
+
sort_descending: "Sort by %{column} descending"
|
|
8
|
+
password_requirements: "At least %{count} characters."
|
|
9
|
+
too_many_requests: "Too many requests, please wait %{minutes} minutes to retry."
|
|
10
|
+
hcaptcha_missing: "Please complete the captcha verification."
|
|
11
|
+
hcaptcha_failed: "Captcha verification failed, please retry."
|
|
12
|
+
security_confirmation_required: "Confirm your current password and enabled second factor to continue."
|
|
13
|
+
current_password: "Current password"
|
|
14
|
+
current_authenticator_code: "Current authenticator code"
|
|
15
|
+
security_key_confirmation_help: "Confirm with your registered security key when submitting."
|
|
16
|
+
confirm_authenticator_code: "Confirm authenticator code"
|
|
17
|
+
authenticator_qr_code_alt: "Authenticator enrollment QR code"
|
|
18
|
+
authenticator_manual_key: "Or enter this key manually in your authenticator app"
|
|
19
|
+
connecting: "Connecting..."
|
|
20
|
+
back_to_account: "Back to your account"
|
|
21
|
+
network_offline: "You are offline"
|
|
22
|
+
network_online: "You are online"
|
|
23
|
+
toggle_sidebar: "Toggle sidebar"
|
|
24
|
+
skip_to_content: "Skip to main content"
|
|
25
|
+
copied: "Copied to clipboard"
|
|
3
26
|
account: Account
|
|
4
27
|
signout: Logout
|
|
5
28
|
signin: Login
|
|
@@ -144,6 +167,7 @@ en:
|
|
|
144
167
|
credentials_not_correct: Email or password not correct
|
|
145
168
|
privacy_policy_invalid: To accept the privacy policy you must select the confirmation checkbox
|
|
146
169
|
terms_and_conditions_invalid: To accept the terms and conditions you must select the confirmation checkbox
|
|
170
|
+
invitation_invalid: Invalid invitation
|
|
147
171
|
web3_address_invalid: The address you send is not corretly signed
|
|
148
172
|
web3_connection_error: Impossible to connect the wallet
|
|
149
173
|
authenticator_code_invalid: The code you inserted is not correct
|
|
@@ -153,6 +177,8 @@ en:
|
|
|
153
177
|
webauthn_authentication_failed: Passkey authentication failed. Please try again or use another method.
|
|
154
178
|
password:
|
|
155
179
|
not_correct: not correct
|
|
180
|
+
password_confirmation:
|
|
181
|
+
confirmation: does not match the password
|
|
156
182
|
email:
|
|
157
183
|
taken: already used with another account
|
|
158
184
|
not_registered: not registered
|
|
@@ -163,8 +189,14 @@ en:
|
|
|
163
189
|
inclusion: not accepted
|
|
164
190
|
web3_address:
|
|
165
191
|
not_correct: not correct
|
|
192
|
+
lato/operation:
|
|
193
|
+
attributes:
|
|
194
|
+
base:
|
|
195
|
+
start_error: "Unable to start the job"
|
|
166
196
|
lato/invitation:
|
|
167
197
|
attributes:
|
|
198
|
+
email:
|
|
199
|
+
taken: "already used by another user"
|
|
168
200
|
base:
|
|
169
201
|
already_accepted: Invitation already accepted
|
|
170
202
|
email_sending_error: Unable to send mail
|
data/config/locales/fr.yml
CHANGED
|
@@ -2,6 +2,29 @@
|
|
|
2
2
|
|
|
3
3
|
fr:
|
|
4
4
|
lato:
|
|
5
|
+
close: "Fermer"
|
|
6
|
+
notice_title: "Notification"
|
|
7
|
+
alert_title: "Erreur"
|
|
8
|
+
sort_ascending: "Trier par %{column} croissant"
|
|
9
|
+
sort_descending: "Trier par %{column} d\u00E9croissant"
|
|
10
|
+
password_requirements: "Au moins %{count} caract\u00E8res."
|
|
11
|
+
too_many_requests: "Trop de requ\u00EAtes, veuillez attendre %{minutes} minutes avant de r\u00E9essayer."
|
|
12
|
+
hcaptcha_missing: "Veuillez compl\u00E9ter la v\u00E9rification captcha."
|
|
13
|
+
hcaptcha_failed: "La v\u00E9rification captcha a \u00E9chou\u00E9, veuillez r\u00E9essayer."
|
|
14
|
+
security_confirmation_required: "Confirmez votre mot de passe actuel et le second facteur activ\u00E9 pour continuer."
|
|
15
|
+
current_password: "Mot de passe actuel"
|
|
16
|
+
current_authenticator_code: "Code authenticator actuel"
|
|
17
|
+
security_key_confirmation_help: "Confirmez avec votre cl\u00E9 de s\u00E9curit\u00E9 enregistr\u00E9e lors de l'envoi."
|
|
18
|
+
confirm_authenticator_code: "Confirmer le code authenticator"
|
|
19
|
+
authenticator_qr_code_alt: "QR code de configuration de l'authenticator"
|
|
20
|
+
authenticator_manual_key: "Ou saisissez cette cl\u00E9 manuellement dans votre application authenticator"
|
|
21
|
+
connecting: "Connexion en cours..."
|
|
22
|
+
back_to_account: "Retour \u00E0 votre compte"
|
|
23
|
+
network_offline: "Vous \u00EAtes hors ligne"
|
|
24
|
+
network_online: "Vous \u00EAtes en ligne"
|
|
25
|
+
toggle_sidebar: "Afficher/masquer la barre lat\u00E9rale"
|
|
26
|
+
skip_to_content: "Aller au contenu principal"
|
|
27
|
+
copied: "Copi\u00E9 dans le presse-papiers"
|
|
5
28
|
account: Compte
|
|
6
29
|
signout: Déconnexion
|
|
7
30
|
signin: Connexion
|
|
@@ -173,8 +196,14 @@ fr:
|
|
|
173
196
|
inclusion: non acceptées
|
|
174
197
|
web3_address:
|
|
175
198
|
not_correct: incorrect
|
|
199
|
+
lato/operation:
|
|
200
|
+
attributes:
|
|
201
|
+
base:
|
|
202
|
+
start_error: "Impossible de lancer le job"
|
|
176
203
|
lato/invitation:
|
|
177
204
|
attributes:
|
|
205
|
+
email:
|
|
206
|
+
taken: "d\u00E9j\u00E0 utilis\u00E9e par un autre utilisateur"
|
|
178
207
|
base:
|
|
179
208
|
already_accepted: Invitation déjà acceptée par l'utilisateur
|
|
180
209
|
email_sending_error: Impossible d'envoyer l'e-mail
|
data/config/locales/it.yml
CHANGED
|
@@ -2,6 +2,29 @@
|
|
|
2
2
|
|
|
3
3
|
it:
|
|
4
4
|
lato:
|
|
5
|
+
close: "Chiudi"
|
|
6
|
+
notice_title: "Notifica"
|
|
7
|
+
alert_title: "Errore"
|
|
8
|
+
sort_ascending: "Ordina per %{column} crescente"
|
|
9
|
+
sort_descending: "Ordina per %{column} decrescente"
|
|
10
|
+
password_requirements: "Almeno %{count} caratteri."
|
|
11
|
+
too_many_requests: "Troppe richieste, attendi %{minutes} minuti prima di riprovare."
|
|
12
|
+
hcaptcha_missing: "Completa la verifica captcha."
|
|
13
|
+
hcaptcha_failed: "Verifica captcha non riuscita, riprova."
|
|
14
|
+
security_confirmation_required: "Conferma la password attuale e il secondo fattore attivo per continuare."
|
|
15
|
+
current_password: "Password attuale"
|
|
16
|
+
current_authenticator_code: "Codice authenticator attuale"
|
|
17
|
+
security_key_confirmation_help: "Conferma con la tua chiave di sicurezza registrata al momento dell'invio."
|
|
18
|
+
confirm_authenticator_code: "Conferma codice authenticator"
|
|
19
|
+
authenticator_qr_code_alt: "Codice QR per configurare l'authenticator"
|
|
20
|
+
authenticator_manual_key: "Oppure inserisci manualmente questa chiave nella tua app authenticator"
|
|
21
|
+
connecting: "Connessione in corso..."
|
|
22
|
+
back_to_account: "Torna al tuo account"
|
|
23
|
+
network_offline: "Sei offline"
|
|
24
|
+
network_online: "Sei online"
|
|
25
|
+
toggle_sidebar: "Mostra/nascondi barra laterale"
|
|
26
|
+
skip_to_content: "Vai al contenuto principale"
|
|
27
|
+
copied: "Copiato negli appunti"
|
|
5
28
|
account: Account
|
|
6
29
|
signout: Esci
|
|
7
30
|
signin: Accedi
|
|
@@ -173,8 +196,14 @@ it:
|
|
|
173
196
|
inclusion: non accettati
|
|
174
197
|
web3_address:
|
|
175
198
|
not_correct: non corretto
|
|
199
|
+
lato/operation:
|
|
200
|
+
attributes:
|
|
201
|
+
base:
|
|
202
|
+
start_error: "Impossibile eseguire il job"
|
|
176
203
|
lato/invitation:
|
|
177
204
|
attributes:
|
|
205
|
+
email:
|
|
206
|
+
taken: "gi\u00E0 utilizzata da un altro utente"
|
|
178
207
|
base:
|
|
179
208
|
already_accepted: Invito già accettato dall'utente
|
|
180
209
|
email_sending_error: Impossibile inviare mail
|