locomotive_cms 2.5.5 → 2.5.6.rc1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +1 -2
- data/README.textile +3 -3
- data/app/assets/images/locomotive/icons/flags/pt.png +0 -0
- data/app/assets/javascripts/locomotive/utils/aloha_settings.js.coffee +1 -1
- data/app/assets/javascripts/locomotive/views/current_site/edit_view.js.coffee +5 -0
- data/app/assets/stylesheets/locomotive/backoffice/menu/main.css.scss +1 -1
- data/app/controllers/locomotive/api/theme_assets_controller.rb +1 -1
- data/app/controllers/locomotive/public/content_entries_controller.rb +6 -11
- data/app/controllers/locomotive/public/pages_controller.rb +1 -1
- data/app/controllers/locomotive/public/sitemaps_controller.rb +1 -1
- data/app/models/locomotive/extensions/content_type/sync.rb +16 -5
- data/app/models/locomotive/extensions/page/tree.rb +7 -3
- data/app/models/locomotive/extensions/site/locales.rb +12 -9
- data/app/models/locomotive/site.rb +10 -0
- data/app/views/locomotive/current_site/_form.html.haml +1 -0
- data/config/locales/admin_ui.cs.yml +2 -0
- data/config/locales/admin_ui.de.yml +3 -2
- data/config/locales/admin_ui.en.yml +1 -0
- data/config/locales/admin_ui.es.yml +2 -1
- data/config/locales/admin_ui.et.yml +1 -0
- data/config/locales/admin_ui.fr.yml +2 -1
- data/config/locales/admin_ui.it.yml +23 -0
- data/config/locales/admin_ui.ja.yml +1 -0
- data/config/locales/admin_ui.nb.yml +1 -0
- data/config/locales/admin_ui.nl.yml +4 -0
- data/config/locales/admin_ui.pl.yml +1 -0
- data/config/locales/admin_ui.pt-BR.yml +6 -5
- data/config/locales/admin_ui.pt.yml +320 -0
- data/config/locales/admin_ui.ru.yml +1 -0
- data/config/locales/admin_ui.sk.yml +2 -0
- data/config/locales/admin_ui.sr.yml +1 -0
- data/config/locales/admin_ui.zh-CN.yml +1 -0
- data/config/locales/carrierwave.pt.yml +4 -0
- data/config/locales/default.pt.yml +212 -0
- data/config/locales/devise.pt.yml +62 -0
- data/config/locales/flash.pt.yml +106 -0
- data/config/locales/formtastic.pt.yml +70 -0
- data/config/routes.rb +5 -6
- data/features/api/authorization/theme_assets.feature +4 -4
- data/features/public/contact_form.feature +1 -1
- data/features/public/new_contact_form.feature +95 -0
- data/features/public/pages.feature +2 -2
- data/features/step_definitions/relationships_steps.rb +37 -34
- data/lib/generators/locomotive/install/install_generator.rb +2 -0
- data/lib/generators/locomotive/install/templates/README +3 -2
- data/lib/generators/locomotive/install/templates/devise.rb +175 -0
- data/lib/generators/locomotive/install/templates/dragonfly.rb +1 -1
- data/lib/generators/locomotive/install/templates/locomotive.rb +2 -2
- data/lib/locomotive.rb +9 -0
- data/lib/locomotive/action_controller/public_responder.rb +45 -28
- data/lib/locomotive/configuration.rb +2 -2
- data/lib/locomotive/liquid/drops/content_entry.rb +6 -8
- data/lib/locomotive/liquid/drops/site.rb +1 -5
- data/lib/locomotive/liquid/filters/translate.rb +1 -1
- data/lib/locomotive/liquid/tags/fetch_page.rb +14 -6
- data/lib/locomotive/liquid/tags/model_form.rb +75 -0
- data/lib/locomotive/middlewares.rb +5 -1
- data/lib/locomotive/middlewares/base.rb +45 -0
- data/lib/locomotive/middlewares/locale.rb +32 -0
- data/lib/locomotive/middlewares/locale_redirection.rb +46 -0
- data/lib/locomotive/middlewares/seo_trailing_slash.rb +7 -28
- data/lib/locomotive/middlewares/site.rb +26 -0
- data/lib/locomotive/regexps.rb +1 -1
- data/lib/locomotive/render.rb +22 -2
- data/lib/locomotive/routing.rb +1 -0
- data/lib/locomotive/routing/post_content_entry_constraint.rb +11 -0
- data/lib/locomotive/routing/site_dispatcher.rb +1 -12
- data/lib/locomotive/version.rb +1 -1
- data/spec/dummy/config/boot.rb +1 -1
- data/spec/dummy/config/initializers/dragonfly.rb +1 -1
- data/spec/dummy/config/initializers/locomotive.rb +2 -2
- data/spec/lib/locomotive/liquid/drops/content_entry_spec.rb +23 -18
- data/spec/lib/locomotive/liquid/drops/site_spec.rb +25 -15
- data/spec/lib/locomotive/liquid/tags/model_form_spec.rb +46 -0
- data/spec/lib/locomotive/routing/site_dispatcher_spec.rb +0 -41
- data/spec/models/locomotive/site_spec.rb +1 -1
- data/spec/requests/locale_redirection_spec.rb +109 -0
- data/spec/requests/locale_spec.rb +85 -0
- data/spec/requests/seo_trailing_slash_spec.rb +1 -1
- data/spec/requests/site_spec.rb +27 -0
- data/spec/support/factories.rb +6 -0
- data/spec/support/middlewares.rb +3 -0
- metadata +48 -10
@@ -6,7 +6,7 @@ Dragonfly.app.configure do
|
|
6
6
|
convert_command: `which convert`.strip.presence || '/usr/local/bin/convert',
|
7
7
|
identify_command: `which identify`.strip.presence || '/usr/local/bin/identify'
|
8
8
|
|
9
|
-
|
9
|
+
verify_urls true
|
10
10
|
|
11
11
|
secret '<%= generate_secret %>'
|
12
12
|
|
@@ -23,11 +23,11 @@ Locomotive.configure do |config|
|
|
23
23
|
# per_page: 10
|
24
24
|
# }
|
25
25
|
|
26
|
-
# default locale (for now, only en, de, fr, pl, pt-BR, it, nb, ja, zh-CN, cs, bg and sk are supported)
|
26
|
+
# default locale (for now, only en, de, fr, pl, pt, pt-BR, it, nb, ja, zh-CN, cs, bg and sk are supported)
|
27
27
|
config.default_locale = :en
|
28
28
|
|
29
29
|
# available locales suggested to "localize" a site. You will have to pick up at least one among that list.
|
30
|
-
# config.site_locales = %w{en de fr pl pt-BR it nl nb es ru ja zh-CN cs bg sk sr}
|
30
|
+
# config.site_locales = %w{en de fr pl pt pt-BR it nl nb es ru ja zh-CN cs bg sk sr}
|
31
31
|
|
32
32
|
# tell if logs are enabled. Useful for debug purpose.
|
33
33
|
config.enable_logs = true
|
data/lib/locomotive.rb
CHANGED
@@ -69,6 +69,9 @@ module Locomotive
|
|
69
69
|
if ::Dragonfly::VERSION =~ /^0\.9\.([0-9]+)/
|
70
70
|
Locomotive.log :error, "WARNING: Old Dragonfly config detected, image uploads might be broken. Use 'rails g locomotive:install' to get the latest configuration files."
|
71
71
|
end
|
72
|
+
|
73
|
+
# avoid I18n warnings
|
74
|
+
I18n.enforce_available_locales = false
|
72
75
|
end
|
73
76
|
|
74
77
|
def self.add_middlewares
|
@@ -77,6 +80,12 @@ module Locomotive
|
|
77
80
|
self.app_middleware.use '::Locomotive::Middlewares::SeoTrailingSlash'
|
78
81
|
|
79
82
|
self.app_middleware.use '::Locomotive::Middlewares::InlineEditor'
|
83
|
+
|
84
|
+
self.app_middleware.use '::Locomotive::Middlewares::Site'
|
85
|
+
|
86
|
+
self.app_middleware.use '::Locomotive::Middlewares::Locale'
|
87
|
+
|
88
|
+
self.app_middleware.use '::Locomotive::Middlewares::LocaleRedirection'
|
80
89
|
end
|
81
90
|
|
82
91
|
def self.configure_multi_sites
|
@@ -6,48 +6,65 @@ module Locomotive
|
|
6
6
|
if get?
|
7
7
|
raise error
|
8
8
|
elsif has_errors? && default_action
|
9
|
-
|
10
|
-
entry = self.controller.instance_variable_get :@entry
|
11
|
-
|
12
|
-
if navigation_location =~ %r(^http://)
|
13
|
-
# simple redirection for outside urls
|
14
|
-
redirect_to navigation_location
|
15
|
-
else
|
16
|
-
# render the locomotive page
|
17
|
-
self.controller.send :render_locomotive_page, navigation_location_for_locomotive, {
|
18
|
-
entry.content_type.slug.singularize => entry.to_presenter(include_errors: true).as_json
|
19
|
-
}
|
20
|
-
end
|
9
|
+
navigation_error_behavior
|
21
10
|
else
|
22
|
-
|
11
|
+
navigation_success_behavior
|
12
|
+
end
|
13
|
+
end
|
23
14
|
|
24
|
-
|
15
|
+
def navigation_error_behavior
|
16
|
+
if error_location =~ %r(^http://)
|
17
|
+
# simple redirection for outside urls
|
18
|
+
redirect_to error_location
|
19
|
+
else
|
20
|
+
path = page_path ? page_path : extract_locale_and_path(error_location)
|
25
21
|
|
26
|
-
#
|
27
|
-
|
22
|
+
# render the locomotive page
|
23
|
+
self.controller.send :render_locomotive_page, path, {
|
24
|
+
content_entry.content_type.slug.singularize => content_entry.to_presenter(include_errors: true).as_json
|
25
|
+
}
|
28
26
|
end
|
29
27
|
end
|
30
28
|
|
31
|
-
def
|
32
|
-
|
29
|
+
def navigation_success_behavior
|
30
|
+
# store in session the newly created content entry
|
31
|
+
self.controller.flash['submitted_entry_id'] = self.content_entry.try(:_id).try(:to_s)
|
33
32
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
33
|
+
# redirect to a locomotive page
|
34
|
+
redirect_to success_location
|
35
|
+
end
|
36
|
+
|
37
|
+
def error_location
|
38
|
+
callback_url(:error) || (page_path ? request.path : '/')
|
39
|
+
end
|
40
|
+
|
41
|
+
def success_location
|
42
|
+
callback_url(:success) || (page_path ? request.path : '/')
|
43
|
+
end
|
44
|
+
|
45
|
+
# get the content entry from the controller
|
46
|
+
def content_entry
|
47
|
+
self.controller.instance_variable_get :@entry
|
48
|
+
end
|
49
|
+
|
50
|
+
def page_path
|
51
|
+
self.controller.params[:path]
|
52
|
+
end
|
53
|
+
|
54
|
+
def callback_url(state)
|
55
|
+
self.controller.params[:"#{state}_callback"]
|
40
56
|
end
|
41
57
|
|
42
58
|
protected
|
43
59
|
|
44
|
-
def
|
60
|
+
def extract_locale_and_path(path)
|
45
61
|
locales = self.controller.send(:current_site).locales.join('|')
|
46
62
|
|
47
|
-
if
|
48
|
-
|
63
|
+
if path =~ /\/(#{locales})+\/(.+)/
|
64
|
+
::I18n.locale = ::Mongoid::Fields::I18n.locale = $1
|
65
|
+
$2
|
49
66
|
else
|
50
|
-
|
67
|
+
path
|
51
68
|
end
|
52
69
|
end
|
53
70
|
|
@@ -7,8 +7,8 @@ module Locomotive
|
|
7
7
|
reserved_subdomains: %w{www admin email blog webmail mail support help site sites},
|
8
8
|
# forbidden_paths: %w{layouts snippets stylesheets javascripts assets admin system api},
|
9
9
|
reserved_slugs: %w{stylesheets javascripts assets admin locomotive images api pages edit},
|
10
|
-
locales: %w{en de fr pl pt-BR it nl nb es ru et ja zh-CN cs bg sk sr},
|
11
|
-
site_locales: %w{en de fr pl pt-BR it nl nb es ru et ja zh-CN cs bg sk sr},
|
10
|
+
locales: %w{en de fr pl pt pt-BR it nl nb es ru et ja zh-CN cs bg sk sr},
|
11
|
+
site_locales: %w{en de fr pl pt pt-BR it nl nb es ru et ja zh-CN cs bg sk sr},
|
12
12
|
cookie_key: '_locomotive_session',
|
13
13
|
enable_logs: false,
|
14
14
|
enable_admin_ssl: false,
|
@@ -62,16 +62,14 @@ module Locomotive
|
|
62
62
|
protected
|
63
63
|
|
64
64
|
def filter_and_order_list(list)
|
65
|
-
|
66
|
-
if @context['with_scope']
|
67
|
-
conditions = HashWithIndifferentAccess.new(@context['with_scope'])
|
68
|
-
order_by = conditions.delete(:order_by).try(:split)
|
65
|
+
conditions, order_by = HashWithIndifferentAccess.new(_visible: true), nil
|
69
66
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
list.ordered
|
67
|
+
if @context['with_scope']
|
68
|
+
conditions.merge!(@context['with_scope'])
|
69
|
+
order_by = conditions.delete(:order_by).try(:split)
|
74
70
|
end
|
71
|
+
|
72
|
+
list.filtered(conditions, order_by)
|
75
73
|
end
|
76
74
|
|
77
75
|
end
|
@@ -15,7 +15,7 @@ module Locomotive
|
|
15
15
|
locale ||= I18n.locale.to_s
|
16
16
|
|
17
17
|
if scope.blank?
|
18
|
-
translation =
|
18
|
+
translation = @context.registers[:site].translations.where(key: input).first
|
19
19
|
|
20
20
|
# key not found
|
21
21
|
return input if translation.nil?
|
@@ -1,10 +1,18 @@
|
|
1
1
|
module Locomotive
|
2
2
|
module Liquid
|
3
3
|
module Tags
|
4
|
+
|
5
|
+
# Fetch a page from its handle and assign it to a liquid variable.
|
6
|
+
#
|
7
|
+
# Usage:
|
8
|
+
#
|
9
|
+
# {% fetch_page 'about_us' as a_page %}
|
10
|
+
# <p>{{ a_page.title }}</p>
|
11
|
+
#
|
4
12
|
class FetchPage < ::Liquid::Tag
|
5
|
-
|
13
|
+
|
6
14
|
Syntax = /(#{::Liquid::VariableSignature}+)\s+as\s+(#{::Liquid::VariableSignature}+)/
|
7
|
-
|
15
|
+
|
8
16
|
def initialize(tag_name, markup, tokens, context)
|
9
17
|
if markup =~ Syntax
|
10
18
|
@handle = $1
|
@@ -12,16 +20,16 @@ module Locomotive
|
|
12
20
|
else
|
13
21
|
raise SyntaxError.new("Syntax Error in 'fetch_page' - Valid syntax: fetch_page page_handle as variable")
|
14
22
|
end
|
15
|
-
|
23
|
+
|
16
24
|
super
|
17
|
-
end
|
18
|
-
|
25
|
+
end
|
26
|
+
|
19
27
|
def render(context)
|
20
28
|
context.scopes.last[@var] = context.registers[:site].pages.where(handle: @handle).first
|
21
29
|
''
|
22
30
|
end
|
23
31
|
end
|
24
|
-
|
32
|
+
|
25
33
|
::Liquid::Template.register_tag('fetch_page', FetchPage)
|
26
34
|
end
|
27
35
|
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
module Locomotive
|
2
|
+
module Liquid
|
3
|
+
module Tags
|
4
|
+
|
5
|
+
# Display the form html tag with the appropriate hidden fields in order to create
|
6
|
+
# a content entry from a public site.
|
7
|
+
# It handles callbacks, csrf and target url out of the box.
|
8
|
+
#
|
9
|
+
# Usage:
|
10
|
+
#
|
11
|
+
# {% model_form 'newsletter_addresses' %}
|
12
|
+
# <input type='text' name='content[email]' />
|
13
|
+
# <input type='submit' value='Add' />
|
14
|
+
# {% endform_form %}
|
15
|
+
#
|
16
|
+
# {% model_form 'newsletter_addresses', class: 'a-css-class', success: 'http://www.google.fr', error: '/error' %}...{% endform_form %}
|
17
|
+
#
|
18
|
+
class ModelForm < Solid::Block
|
19
|
+
|
20
|
+
tag_name :model_form
|
21
|
+
|
22
|
+
def display(*options, &block)
|
23
|
+
name = options.shift
|
24
|
+
options = options.shift || {}
|
25
|
+
|
26
|
+
form_attributes = { method: 'POST', enctype: 'multipart/form-data' }.merge(options.slice(:id, :class))
|
27
|
+
|
28
|
+
html_content_tag :form,
|
29
|
+
content_type_html(name) + csrf_html + callbacks_html(options) + yield,
|
30
|
+
form_attributes
|
31
|
+
end
|
32
|
+
|
33
|
+
def content_type_html(name)
|
34
|
+
html_tag :input, type: 'hidden', name: 'content_type_slug', value: name
|
35
|
+
end
|
36
|
+
|
37
|
+
def csrf_html
|
38
|
+
name = controller.send(:request_forgery_protection_token).to_s
|
39
|
+
value = controller.send(:form_authenticity_token)
|
40
|
+
|
41
|
+
html_tag :input, type: 'hidden', name: name, value: value
|
42
|
+
end
|
43
|
+
|
44
|
+
def callbacks_html(options)
|
45
|
+
options.slice(:success, :error).map do |(name, value)|
|
46
|
+
html_tag :input, type: 'hidden', name: "#{name}_callback", value: value
|
47
|
+
end.join('')
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def controller
|
53
|
+
current_context.registers[:controller]
|
54
|
+
end
|
55
|
+
|
56
|
+
def html_content_tag(name, content, options = {})
|
57
|
+
"<#{name} #{inline_options(options)}>#{content}</#{name}>"
|
58
|
+
end
|
59
|
+
|
60
|
+
def html_tag(name, options = {})
|
61
|
+
"<#{name} #{inline_options(options)} />"
|
62
|
+
end
|
63
|
+
|
64
|
+
# Write options (Hash) into a string according to the following pattern:
|
65
|
+
# <key1>="<value1>", <key2>="<value2", ...etc
|
66
|
+
def inline_options(options = {})
|
67
|
+
return '' if options.empty?
|
68
|
+
(options.stringify_keys.to_a.collect { |a, b| "#{a}=\"#{b}\"" }).join(' ')
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -1,5 +1,9 @@
|
|
1
1
|
require 'rack/cache'
|
2
2
|
|
3
|
+
require 'locomotive/middlewares/base'
|
3
4
|
require 'locomotive/middlewares/seo_trailing_slash'
|
4
5
|
require 'locomotive/middlewares/inline_editor'
|
5
|
-
require 'locomotive/middlewares/permalink'
|
6
|
+
require 'locomotive/middlewares/permalink'
|
7
|
+
require 'locomotive/middlewares/site'
|
8
|
+
require 'locomotive/middlewares/locale'
|
9
|
+
require 'locomotive/middlewares/locale_redirection'
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Locomotive
|
2
|
+
module Middlewares
|
3
|
+
class Base
|
4
|
+
|
5
|
+
protected
|
6
|
+
|
7
|
+
def is_backoffice?(request)
|
8
|
+
request.path.match(%r(^#{Locomotive.mounted_on}(/|$))) != nil
|
9
|
+
end
|
10
|
+
|
11
|
+
def is_assets?(request)
|
12
|
+
request.path.match(%r(^/assets(/|$))) != nil
|
13
|
+
end
|
14
|
+
|
15
|
+
# Create a 301 response and set it up accordingly.
|
16
|
+
#
|
17
|
+
# @params [ String ] url The url for the redirection
|
18
|
+
#
|
19
|
+
# @return [ Array ] It has the 3 parameters (status, header, body)
|
20
|
+
#
|
21
|
+
def redirect_to(url)
|
22
|
+
response = Rack::Response.new
|
23
|
+
response.redirect(url, 301) # moved permanently
|
24
|
+
response.finish
|
25
|
+
response.to_a
|
26
|
+
end
|
27
|
+
|
28
|
+
# Modify the fullpath according to the regexp/replacement
|
29
|
+
# and return the updated url
|
30
|
+
#
|
31
|
+
# @params [ Rack::Request ] request The base request
|
32
|
+
# @params [ Regexp ] regexp The regexp to apply to the fullpath
|
33
|
+
# @params [ String ] replacement The replacement string for the fullpath
|
34
|
+
#
|
35
|
+
# @return [ String ] The updated url
|
36
|
+
#
|
37
|
+
def modify_url(request, path)
|
38
|
+
url = "#{request.base_url}#{path}"
|
39
|
+
url += "?#{request.query_string}" unless request.query_string.empty?
|
40
|
+
url
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Locomotive
|
2
|
+
module Middlewares
|
3
|
+
class Locale
|
4
|
+
|
5
|
+
def initialize(app, opts = {})
|
6
|
+
@app = app
|
7
|
+
end
|
8
|
+
|
9
|
+
def call(env)
|
10
|
+
retrieve_and_set_locale(env)
|
11
|
+
@app.call(env)
|
12
|
+
end
|
13
|
+
|
14
|
+
def retrieve_and_set_locale(env)
|
15
|
+
site = env['locomotive.site']
|
16
|
+
|
17
|
+
if site.try(:localized?)
|
18
|
+
if env['PATH_INFO'] =~ %r{^/(#{site.locales.join('|')})+(\/|$)}
|
19
|
+
locale = $1
|
20
|
+
path = env['PATH_INFO'].gsub($1 + $2, '').gsub(/(\/_edit|\/_admin)$/, '')
|
21
|
+
|
22
|
+
Locomotive.log "[extract locale] locale = #{locale} / #{path}"
|
23
|
+
|
24
|
+
env['locomotive.locale'] = locale
|
25
|
+
env['locomotive.path'] = path
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Locomotive
|
2
|
+
module Middlewares
|
3
|
+
class LocaleRedirection < Base
|
4
|
+
|
5
|
+
def initialize(app, opts = {})
|
6
|
+
@app = app
|
7
|
+
end
|
8
|
+
|
9
|
+
def call(env)
|
10
|
+
if url = redirect_url(env)
|
11
|
+
redirect_to url
|
12
|
+
else
|
13
|
+
@app.call(env)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
protected
|
18
|
+
|
19
|
+
def redirect_url(env)
|
20
|
+
request = Rack::Request.new(env)
|
21
|
+
|
22
|
+
site, locale = env['locomotive.site'], env['locomotive.locale']
|
23
|
+
|
24
|
+
if apply_redirection?(site, request)
|
25
|
+
segments = request.path.split '/'
|
26
|
+
|
27
|
+
if !locale && site.prefix_default_locale
|
28
|
+
# force locale in path by redirecting
|
29
|
+
segments.insert(1, "#{site.default_locale}")
|
30
|
+
modify_url(request, segments.join('/'))
|
31
|
+
|
32
|
+
elsif locale == site.default_locale && !site.prefix_default_locale
|
33
|
+
# strip locale
|
34
|
+
segments.delete_at(1)
|
35
|
+
modify_url(request, segments.join('/'))
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def apply_redirection?(site, request)
|
41
|
+
site.try(:localized?) && request.get? && !is_backoffice?(request) && !is_assets?(request)
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|