decidim-core 0.3.2 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/assets/stylesheets/decidim/extras/_add_comments.scss +9 -0
- data/app/assets/stylesheets/decidim/extras/_label-required.scss +3 -0
- data/app/assets/stylesheets/decidim/extras/_reference.scss +3 -0
- data/app/assets/stylesheets/decidim/extras/_social_icons_mini.scss +10 -0
- data/app/commands/decidim/invite_user.rb +1 -1
- data/app/commands/decidim/update_account.rb +2 -6
- data/app/constraints/decidim/current_feature.rb +11 -18
- data/app/constraints/decidim/current_participatory_process.rb +35 -0
- data/app/controllers/concerns/decidim/devise_controllers.rb +1 -0
- data/app/controllers/concerns/decidim/locale_switcher.rb +2 -2
- data/app/controllers/concerns/decidim/needs_authorization.rb +1 -2
- data/app/controllers/concerns/decidim/needs_organization.rb +1 -2
- data/app/controllers/concerns/decidim/needs_participatory_process.rb +19 -10
- data/app/controllers/decidim/account_controller.rb +13 -9
- data/app/controllers/decidim/application_controller.rb +3 -8
- data/app/controllers/decidim/devise/registrations_controller.rb +0 -1
- data/app/controllers/decidim/devise/sessions_controller.rb +0 -1
- data/app/controllers/decidim/errors_controller.rb +15 -0
- data/app/controllers/decidim/features/base_controller.rb +1 -0
- data/app/controllers/decidim/participatory_process_groups_controller.rb +6 -2
- data/app/controllers/decidim/participatory_process_steps_controller.rb +1 -0
- data/app/controllers/decidim/participatory_processes_controller.rb +3 -3
- data/app/helpers/decidim/application_helper.rb +3 -0
- data/app/helpers/decidim/feature_path_helper.rb +35 -0
- data/app/helpers/decidim/feature_reference_helper.rb +4 -2
- data/app/helpers/decidim/icon_helper.rb +20 -0
- data/app/helpers/decidim/menu_helper.rb +1 -1
- data/app/helpers/decidim/participatory_process_helper.rb +0 -14
- data/app/models/decidim/feature.rb +9 -1
- data/app/models/decidim/static_page.rb +1 -0
- data/app/models/decidim/user.rb +2 -0
- data/app/queries/decidim/highlighted_participatory_processes.rb +1 -1
- data/app/views/decidim/account/show.html.erb +1 -3
- data/app/views/decidim/devise/shared/_omniauth_buttons.html.erb +1 -1
- data/app/views/decidim/devise/shared/_omniauth_buttons_mini.html.erb +20 -0
- data/app/views/{pages/500.html.erb → decidim/errors/internal_server_error.html.erb} +0 -0
- data/app/views/{pages/404.html.erb → decidim/errors/not_found.html.erb} +1 -1
- data/app/views/decidim/shared/_login_modal.html.erb +4 -0
- data/app/views/decidim/shared/_share_modal.html.erb +1 -1
- data/app/views/layouts/decidim/_process_header.html.erb +2 -2
- data/config/i18n-tasks.yml +2 -0
- data/config/initializers/invisible_captcha.rb +2 -2
- data/config/locales/ca.yml +14 -8
- data/config/locales/en.yml +13 -7
- data/config/locales/es.yml +13 -7
- data/config/locales/eu.yml +0 -7
- data/config/locales/fi.yml +0 -7
- data/config/locales/fr.yml +4 -11
- data/config/locales/it.yml +0 -7
- data/config/locales/nl.yml +0 -7
- data/config/routes.rb +2 -8
- data/db/migrate/20160817115213_devise_create_decidim_users.rb +6 -6
- data/db/migrate/20160920140207_devise_invitable_add_to_decidim_users.rb +9 -9
- data/db/seeds.rb +2 -2
- data/lib/decidim/core.rb +25 -1
- data/lib/decidim/core/engine.rb +0 -1
- data/lib/decidim/core/test.rb +1 -0
- data/lib/decidim/core/test/factories.rb +11 -6
- data/lib/decidim/core/test/shared_examples/comments_examples.rb +69 -66
- data/lib/decidim/core/test/shared_examples/errors.rb +21 -0
- data/lib/decidim/core/test/shared_examples/has_reference.rb +18 -2
- data/lib/decidim/core/test/shared_examples/manage_moderations_examples.rb +2 -2
- data/lib/decidim/core/version.rb +1 -1
- data/lib/decidim/devise_failure_app.rb +1 -1
- data/lib/decidim/form_builder.rb +89 -5
- data/lib/decidim/has_reference.rb +3 -15
- data/lib/decidim/notifiable.rb +22 -0
- data/lib/devise/models/decidim_validatable.rb +4 -4
- metadata +41 -16
@@ -3,6 +3,8 @@
|
|
3
3
|
module Decidim
|
4
4
|
# Main module to add application-wide helpers.
|
5
5
|
module ApplicationHelper
|
6
|
+
include Decidim::OmniauthHelper
|
7
|
+
|
6
8
|
# Truncates a given text respecting its HTML tags.
|
7
9
|
#
|
8
10
|
# text - The String text to be truncated.
|
@@ -17,6 +19,7 @@ module Decidim
|
|
17
19
|
options[:tail] = options.delete(:separator) || options[:tail] || "..."
|
18
20
|
options[:count_tags] ||= false
|
19
21
|
options[:count_tail] ||= false
|
22
|
+
options[:tail_before_final_tag] ||= true
|
20
23
|
|
21
24
|
Truncato.truncate(text, options)
|
22
25
|
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
# A helper to get the root path for a feature.
|
5
|
+
module FeaturePathHelper
|
6
|
+
# Returns the defined root path for a given feature.
|
7
|
+
#
|
8
|
+
# feature - the Feature we want to find the root path for.
|
9
|
+
#
|
10
|
+
# Returns a url.
|
11
|
+
def feature_path(feature)
|
12
|
+
feature_root_path_for(feature.manifest.engine, feature)
|
13
|
+
end
|
14
|
+
|
15
|
+
# Returns the defined admin root path for a given feature.
|
16
|
+
#
|
17
|
+
# feature - the Feature we want to find the root path for.
|
18
|
+
#
|
19
|
+
# Returns a url.
|
20
|
+
def manage_feature_path(feature)
|
21
|
+
feature_root_path_for(feature.manifest.admin_engine, feature)
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def feature_root_path_for(engine, feature)
|
27
|
+
url_params = {
|
28
|
+
feature_id: feature.id,
|
29
|
+
participatory_process_id: feature.participatory_process.id
|
30
|
+
}
|
31
|
+
|
32
|
+
engine.routes.url_helpers.root_path(url_params)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -6,12 +6,14 @@ module Decidim
|
|
6
6
|
# Displays the localized reference for the given feature.
|
7
7
|
#
|
8
8
|
# feature - the Feature that has the reference to display.
|
9
|
+
# options - An optional hash of options
|
10
|
+
# * class: A string of extra css classes
|
9
11
|
#
|
10
12
|
# Returns a String.
|
11
|
-
def feature_reference(feature)
|
13
|
+
def feature_reference(feature, options = {})
|
12
14
|
return unless feature.reference
|
13
15
|
@reference = feature.reference
|
14
|
-
"<div class='reference'>#{localized_reference}</div>".html_safe
|
16
|
+
"<div class='reference #{options[:class]}'>#{localized_reference}</div>".html_safe
|
15
17
|
end
|
16
18
|
|
17
19
|
private
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
# Helpers related to icons
|
5
|
+
module IconHelper
|
6
|
+
# Public: Returns an icon given an instance of a Feature. It defaults to
|
7
|
+
# a question mark when no icon is found.
|
8
|
+
#
|
9
|
+
# feature - The feature to generate the icon for.
|
10
|
+
#
|
11
|
+
# Returns an HTML tag with the icon.
|
12
|
+
def feature_icon(feature)
|
13
|
+
if feature.manifest.icon
|
14
|
+
external_icon feature.manifest.icon
|
15
|
+
else
|
16
|
+
icon "question-mark"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -13,19 +13,5 @@ module Decidim
|
|
13
13
|
dates = [participatory_process_step.start_date, participatory_process_step.end_date]
|
14
14
|
dates.map { |date| date ? localize(date.to_date, format: :default) : "?" }.join(" - ")
|
15
15
|
end
|
16
|
-
|
17
|
-
# Public: Returns an icon given an instance of a Feature. It defaults to
|
18
|
-
# a question mark when no icon is found.
|
19
|
-
#
|
20
|
-
# feature - The feature to generate the icon for.
|
21
|
-
#
|
22
|
-
# Returns an HTML tag with the icon.
|
23
|
-
def feature_icon(feature)
|
24
|
-
if feature.manifest.icon
|
25
|
-
external_icon feature.manifest.icon
|
26
|
-
else
|
27
|
-
icon "question-mark"
|
28
|
-
end
|
29
|
-
end
|
30
16
|
end
|
31
17
|
end
|
@@ -61,6 +61,14 @@ module Decidim
|
|
61
61
|
self[:settings]["global"] = serialize_settings(settings_schema(:global), data)
|
62
62
|
end
|
63
63
|
|
64
|
+
def default_step_settings
|
65
|
+
settings_schema(:step).new(self[:settings]["default_step"])
|
66
|
+
end
|
67
|
+
|
68
|
+
def default_step_settings=(data)
|
69
|
+
self[:settings]["default_step"] = serialize_settings(settings_schema(:step), data)
|
70
|
+
end
|
71
|
+
|
64
72
|
def step_settings
|
65
73
|
participatory_process.steps.each_with_object({}) do |step, result|
|
66
74
|
result[step.id.to_s] = settings_schema(:step).new(self[:settings].dig("steps", step.id.to_s))
|
@@ -75,7 +83,7 @@ module Decidim
|
|
75
83
|
|
76
84
|
def active_step_settings
|
77
85
|
active_step = participatory_process.active_step
|
78
|
-
return
|
86
|
+
return default_step_settings unless active_step
|
79
87
|
|
80
88
|
step_settings.fetch(active_step.id.to_s)
|
81
89
|
end
|
@@ -11,6 +11,7 @@ module Decidim
|
|
11
11
|
belongs_to :organization, foreign_key: "decidim_organization_id", class_name: "Decidim::Organization", inverse_of: :static_pages
|
12
12
|
|
13
13
|
validates :slug, presence: true, uniqueness: { scope: :organization }
|
14
|
+
validates :slug, format: { with: /\A[a-z0-9-]+/ }
|
14
15
|
|
15
16
|
# These pages will be created by default when registering an organization
|
16
17
|
# and cannot be deleted.
|
data/app/models/decidim/user.rb
CHANGED
@@ -28,6 +28,8 @@ module Decidim
|
|
28
28
|
validate :all_roles_are_valid
|
29
29
|
mount_uploader :avatar, Decidim::AvatarUploader
|
30
30
|
|
31
|
+
scope :not_deleted, -> { where(deleted_at: nil) }
|
32
|
+
|
31
33
|
# Public: Allows customizing the invitation instruction email content when
|
32
34
|
# inviting a user.
|
33
35
|
#
|
@@ -5,7 +5,7 @@ module Decidim
|
|
5
5
|
# public view.
|
6
6
|
class HighlightedParticipatoryProcesses < Rectify::Query
|
7
7
|
def query
|
8
|
-
|
8
|
+
PrioritizedParticipatoryProcesses.new.query.limit(8)
|
9
9
|
end
|
10
10
|
end
|
11
11
|
end
|
@@ -1,9 +1,7 @@
|
|
1
1
|
<div class="row">
|
2
2
|
<%= decidim_form_for(@account, url: account_path, method: :put, class: "user-form") do |f| %>
|
3
3
|
<div class="columns large-4">
|
4
|
-
<%=
|
5
|
-
<%= f.file_field :avatar, label: false %>
|
6
|
-
<%= f.check_box :remove_avatar %>
|
4
|
+
<%= f.upload :avatar %>
|
7
5
|
</div>
|
8
6
|
|
9
7
|
<div class="columns large-8 end">
|
@@ -0,0 +1,20 @@
|
|
1
|
+
<% if Devise.mappings[:user].omniauthable? && any_social_provider_enabled? %>
|
2
|
+
<div class="row">
|
3
|
+
<div class="columns medium-8 medium-centered">
|
4
|
+
<span class="register__separator">
|
5
|
+
<span class="register__separator__text"><%= t('or', scope: "decidim.devise.shared.omniauth_buttons") %></span>
|
6
|
+
</span>
|
7
|
+
<div class="text-center">
|
8
|
+
<%- Decidim::User.omniauth_providers.each do |provider| %>
|
9
|
+
<% if social_provider_enabled? provider %>
|
10
|
+
<%= link_to decidim.send("user_#{provider}_omniauth_authorize_path"), class: "button button--social button--#{normalize_provider_name(provider)} button--social--mini" do %>
|
11
|
+
<span class="button--social__icon">
|
12
|
+
<%= icon normalize_provider_name(provider) %>
|
13
|
+
</span>
|
14
|
+
<% end %>
|
15
|
+
<% end %>
|
16
|
+
<% end %>
|
17
|
+
</div>
|
18
|
+
</div>
|
19
|
+
</div>
|
20
|
+
<% end %>
|
File without changes
|
@@ -24,6 +24,10 @@
|
|
24
24
|
<p class="text-center">
|
25
25
|
<%= link_to t('.sign_up'), decidim.new_user_registration_path, class: "sign-up-link" %>
|
26
26
|
</p>
|
27
|
+
<p class="text-center">
|
28
|
+
<%= link_to t("devise.shared.links.forgot_your_password"), new_password_path(:user) %>
|
29
|
+
</p>
|
27
30
|
</div>
|
31
|
+
<%= render "decidim/devise/shared/omniauth_buttons_mini" %>
|
28
32
|
</div>
|
29
33
|
</div>
|
@@ -48,8 +48,8 @@
|
|
48
48
|
|
49
49
|
<% current_participatory_process.features.each do |feature| %>
|
50
50
|
<% if feature.published? || feature == self.try(:current_feature) %>
|
51
|
-
<li class="<%= "is-active" if is_active_link?(
|
52
|
-
<%= active_link_to
|
51
|
+
<li class="<%= "is-active" if is_active_link?(feature_path(feature), :inclusive) %>">
|
52
|
+
<%= active_link_to feature_path(feature), class: "process-nav__link", active: :inclusive, class_active: "is-active" do %>
|
53
53
|
<%= feature_icon(feature) %>
|
54
54
|
|
55
55
|
<%= translated_attribute(feature.name) %>
|
data/config/i18n-tasks.yml
CHANGED
@@ -105,6 +105,8 @@ ignore_unused:
|
|
105
105
|
- decidim.participatory_processes.scopes.global
|
106
106
|
- decidim.participatory_processes.participatory_process_groups.none
|
107
107
|
- pages.home.statistics.*
|
108
|
+
- forms.*
|
109
|
+
|
108
110
|
# - 'activerecord.attributes.*'
|
109
111
|
# - '{devise,kaminari,will_paginate}.*'
|
110
112
|
# - 'simple_form.{yes,no}'
|
@@ -2,9 +2,9 @@
|
|
2
2
|
|
3
3
|
InvisibleCaptcha.setup do |config|
|
4
4
|
config.honeypots << "another_fake_attribute"
|
5
|
-
config.visual_honeypots
|
5
|
+
config.visual_honeypots = false
|
6
6
|
config.timestamp_threshold = 4
|
7
|
-
config.timestamp_enabled
|
7
|
+
config.timestamp_enabled = false
|
8
8
|
# Leave these unset if you want to use I18n (see below)
|
9
9
|
# config.sentence_for_humans = 'If you are a human, ignore this field'
|
10
10
|
# config.timestamp_error_message = 'Sorry, that was too quick! Please resubmit.'
|
data/config/locales/ca.yml
CHANGED
@@ -118,6 +118,14 @@ ca:
|
|
118
118
|
shared:
|
119
119
|
omniauth_buttons:
|
120
120
|
or: O
|
121
|
+
errors:
|
122
|
+
internal_server_error:
|
123
|
+
title: Hi ha hagut un problema amb el nostre servidor
|
124
|
+
try_later: Si us plau, torna-ho a intentar més tard.
|
125
|
+
not_found:
|
126
|
+
back_home: Tornar a l'inici
|
127
|
+
content_doesnt_exist: Aquesta adreça és incorrecta o ha estat eliminada.
|
128
|
+
title: No s'ha trobat la pàgina que busques
|
121
129
|
export_mailer:
|
122
130
|
export:
|
123
131
|
ready: S'adjunta una versió comprimida de l'exportació.
|
@@ -145,6 +153,9 @@ ca:
|
|
145
153
|
proposal: propostes
|
146
154
|
result: Resultats
|
147
155
|
forms:
|
156
|
+
current_file: Fitxer actual
|
157
|
+
current_image: Imatge actual
|
158
|
+
default_image: Imatge predeterminada
|
148
159
|
errors:
|
149
160
|
error: Hi ha un error en aquest camp.
|
150
161
|
menu:
|
@@ -284,6 +295,8 @@ ca:
|
|
284
295
|
too_many_marks: Està utilitzant massa símbols d'exclamació o interrogació
|
285
296
|
too_much_caps: Està utilitzant massa majúscules
|
286
297
|
too_short: És massa curt
|
298
|
+
forms:
|
299
|
+
required: Obligatori
|
287
300
|
invisible_captcha:
|
288
301
|
sentence_for_humans: Si ets un humà, pots ignorar aquest camp
|
289
302
|
timestamp_error_message: Ho sentim, has estat massa ràpid! Torna a enviar-ho.
|
@@ -345,13 +358,6 @@ ca:
|
|
345
358
|
locale:
|
346
359
|
name: Català
|
347
360
|
pages:
|
348
|
-
'404':
|
349
|
-
back_home: Tornar a l'inici
|
350
|
-
content_doesnt_exist: Aquesta adreça és incorrecta o ha estat eliminada.
|
351
|
-
title: No s'ha trobat la pàgina que busques!
|
352
|
-
'500':
|
353
|
-
title: Hi ha hagut un problema amb el nostre servidor
|
354
|
-
try_later: Si us plau, torna-ho a intentar més tard.
|
355
361
|
home:
|
356
362
|
extended:
|
357
363
|
debates: Debats
|
@@ -364,7 +370,7 @@ ca:
|
|
364
370
|
proposals_explanation: Espai obert de propostes dels ciutadans sobre quin tipus de ciutat volem viure.
|
365
371
|
footer_sub_hero:
|
366
372
|
footer_sub_hero_body: Construïm una societat més oberta, transparent i col·laborativa.<br />Uneix-te, participa i decideix.
|
367
|
-
footer_sub_hero_headline:
|
373
|
+
footer_sub_hero_headline: Bienvenida y bienvenido a la plataforma participativa %{organization}.
|
368
374
|
register: Registra't
|
369
375
|
hero:
|
370
376
|
participate: Participa
|
data/config/locales/en.yml
CHANGED
@@ -119,6 +119,14 @@ en:
|
|
119
119
|
shared:
|
120
120
|
omniauth_buttons:
|
121
121
|
or: Or
|
122
|
+
errors:
|
123
|
+
internal_server_error:
|
124
|
+
title: There was a problem with our server
|
125
|
+
try_later: Please try again later.
|
126
|
+
not_found:
|
127
|
+
back_home: Back home
|
128
|
+
content_doesnt_exist: This address is incorrect or has been removed.
|
129
|
+
title: The page you're looking for can't be found
|
122
130
|
export_mailer:
|
123
131
|
export:
|
124
132
|
ready: Please find attached a zipped version of your export.
|
@@ -146,6 +154,9 @@ en:
|
|
146
154
|
proposal: Proposals
|
147
155
|
result: Results
|
148
156
|
forms:
|
157
|
+
current_file: Current file
|
158
|
+
current_image: Current image
|
159
|
+
default_image: Default image
|
149
160
|
errors:
|
150
161
|
error: There's an error in this field.
|
151
162
|
menu:
|
@@ -285,6 +296,8 @@ en:
|
|
285
296
|
too_many_marks: Is using too many marks
|
286
297
|
too_much_caps: Is using too much caps
|
287
298
|
too_short: Is too short
|
299
|
+
forms:
|
300
|
+
required: Required
|
288
301
|
invisible_captcha:
|
289
302
|
sentence_for_humans: If you are human, ignore this field
|
290
303
|
timestamp_error_message: Sorry, that was too quick! Please resubmit.
|
@@ -346,13 +359,6 @@ en:
|
|
346
359
|
locale:
|
347
360
|
name: English
|
348
361
|
pages:
|
349
|
-
'404':
|
350
|
-
back_home: Back home
|
351
|
-
content_doesnt_exist: This address is incorrect or has been removed.
|
352
|
-
title: The page you're looking for can't be found
|
353
|
-
'500':
|
354
|
-
title: There was a problem with our server
|
355
|
-
try_later: Please try again later.
|
356
362
|
home:
|
357
363
|
extended:
|
358
364
|
debates: Debates
|
data/config/locales/es.yml
CHANGED
@@ -118,6 +118,14 @@ es:
|
|
118
118
|
shared:
|
119
119
|
omniauth_buttons:
|
120
120
|
or: O
|
121
|
+
errors:
|
122
|
+
internal_server_error:
|
123
|
+
title: Ha habido un problema con nuestro servidor
|
124
|
+
try_later: Por favor, vuelve a intentarlo más tarde.
|
125
|
+
not_found:
|
126
|
+
back_home: Volver al inicio
|
127
|
+
content_doesnt_exist: Esta dirección es incorrecta o ha sido eliminada.
|
128
|
+
title: No se ha encontrado la página que buscas
|
121
129
|
export_mailer:
|
122
130
|
export:
|
123
131
|
ready: Adjunto encontrará una versión comprimida de su exportación.
|
@@ -145,6 +153,9 @@ es:
|
|
145
153
|
proposal: Propuestas
|
146
154
|
result: Resultados
|
147
155
|
forms:
|
156
|
+
current_file: Archivo actual
|
157
|
+
current_image: Imagen actual
|
158
|
+
default_image: Imagen predeterminada
|
148
159
|
errors:
|
149
160
|
error: Hay un error en este campo.
|
150
161
|
menu:
|
@@ -284,6 +295,8 @@ es:
|
|
284
295
|
too_many_marks: Está utilizando demasiados símbolos de interrogación o exclamación
|
285
296
|
too_much_caps: Está usando demasiadas mayúsculas
|
286
297
|
too_short: Es demasiado corto
|
298
|
+
forms:
|
299
|
+
required: Obligatorio
|
287
300
|
invisible_captcha:
|
288
301
|
sentence_for_humans: Si eres humano, ignora este campo
|
289
302
|
timestamp_error_message: Lo siento, has sido demasiado rápido! Por favor, vuelva a enviar.
|
@@ -345,13 +358,6 @@ es:
|
|
345
358
|
locale:
|
346
359
|
name: Castellano
|
347
360
|
pages:
|
348
|
-
'404':
|
349
|
-
back_home: Volver al inicio
|
350
|
-
content_doesnt_exist: Esta dirección es incorrecta o ha sido eliminada.
|
351
|
-
title: '¡No se ha encontrado la página que buscas!'
|
352
|
-
'500':
|
353
|
-
title: Ha habido un problema con nuestro servidor
|
354
|
-
try_later: Por favor, vuelve a intentarlo más tarde.
|
355
361
|
home:
|
356
362
|
extended:
|
357
363
|
debates: Debates
|
data/config/locales/eu.yml
CHANGED
@@ -323,13 +323,6 @@ eu:
|
|
323
323
|
locale:
|
324
324
|
name: Euskera
|
325
325
|
pages:
|
326
|
-
'404':
|
327
|
-
back_home: Itzuli hasierara
|
328
|
-
content_doesnt_exist: Helbide okerra da edo ezabatu da.
|
329
|
-
title: Ez dugu aurkitu bilatzen duzun orria!
|
330
|
-
'500':
|
331
|
-
title: Arazo bat izan da gure zerbitzariarekin
|
332
|
-
try_later: Mesedez, berriz saiatu geroago.
|
333
326
|
home:
|
334
327
|
extended:
|
335
328
|
debates: Eztabaidak
|