slices 1.0.5 → 2.0.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/CHANGELOG.md +8 -0
- data/README.md +85 -17
- data/app/assets/images/slices/i18n.png +0 -0
- data/app/assets/images/slices/icon_collapse.png +0 -0
- data/app/assets/javascripts/slices/app/helpers/apply_defaults.js +9 -0
- data/app/assets/javascripts/slices/app/helpers/composer.js +2 -0
- data/app/assets/javascripts/slices/app/helpers/i18n.js +7 -0
- data/app/assets/javascripts/slices/app/helpers/markdown_cheat_sheet.js +50 -0
- data/app/assets/javascripts/slices/app/helpers/radio_field.js +21 -0
- data/app/assets/javascripts/slices/app/helpers/sitemap.js +82 -1
- data/app/assets/javascripts/slices/app/helpers/uploader.js +1 -1
- data/app/assets/javascripts/slices/app/helpers/url_field.js +17 -0
- data/app/assets/javascripts/slices/app/slices.js +14 -11
- data/app/assets/javascripts/slices/app/views/composer_view.js +16 -0
- data/app/assets/javascripts/slices/app/views/radio_field_view.js +67 -0
- data/app/assets/javascripts/slices/app/views/token_field_view.js +81 -24
- data/app/assets/javascripts/slices/app/views/url_field_view.js +44 -0
- data/app/assets/javascripts/slices/slices.js +1 -0
- data/app/assets/javascripts/slices/vendor/jquery.js +4 -2
- data/app/assets/stylesheets/slices/admin.css.erb +156 -31
- data/app/controllers/admin/admin_controller.rb +16 -1
- data/app/controllers/admin/assets_controller.rb +1 -1
- data/app/controllers/admin/entries_controller.rb +1 -1
- data/app/controllers/admin/pages_controller.rb +4 -13
- data/app/controllers/admin/site_maps_controller.rb +1 -2
- data/app/controllers/slices_controller.rb +12 -1
- data/app/helpers/admin/admin_helper.rb +8 -0
- data/app/helpers/admin/site_maps_helper.rb +41 -6
- data/app/helpers/navigation_helper.rb +8 -3
- data/app/helpers/pages_helper.rb +2 -2
- data/app/helpers/snippets_helper.rb +26 -0
- data/app/models/admin.rb +2 -3
- data/app/models/asset.rb +9 -20
- data/app/models/attachment.rb +10 -1
- data/app/models/page.rb +45 -34
- data/app/models/site_map.rb +1 -2
- data/app/models/slice.rb +46 -1
- data/app/models/snippet.rb +13 -4
- data/app/presenters/page_presenter.rb +8 -13
- data/app/views/admin/auth/sessions/_form.html.erb +3 -0
- data/app/views/admin/pages/_fields.html.erb +1 -0
- data/app/views/admin/pages/_slices.html.erb +8 -4
- data/app/views/admin/pages/new.html.erb +5 -1
- data/app/views/admin/pages/show.html.erb +30 -14
- data/app/views/admin/site_maps/_page_actions.html.erb +2 -0
- data/app/views/admin/site_maps/_page_li.html.erb +18 -7
- data/app/views/admin/site_maps/_set_page_li.html.erb +5 -5
- data/app/views/layouts/admin.html.erb +0 -17
- data/config/routes.rb +13 -16
- data/lib/generators/slice/templates/presenter.rb +11 -12
- data/lib/generators/slice/templates/set.html.erb +2 -2
- data/lib/generators/slices/install_generator.rb +13 -3
- data/lib/generators/slices/templates/slices.rb +3 -4
- data/lib/mongo_search.rb +1 -1
- data/lib/slices.rb +2 -9
- data/lib/slices/asset/maker.rb +2 -3
- data/lib/slices/available_slices.rb +8 -1
- data/lib/slices/config.rb +49 -8
- data/lib/slices/engine.rb +0 -4
- data/lib/slices/has_attachments.rb +25 -37
- data/lib/slices/has_slices.rb +15 -8
- data/lib/slices/localized_fields.rb +9 -0
- data/lib/slices/page_as_json.rb +7 -1
- data/lib/slices/renderer.rb +2 -2
- data/lib/slices/tree.rb +12 -3
- data/lib/slices/version.rb +1 -1
- data/lib/tasks/db.rake +6 -10
- data/lib/tasks/seeds.rake +1 -1
- data/public/slices/templates/page_main.hbs +1 -1
- data/public/slices/templates/page_meta.hbs +2 -2
- metadata +56 -84
- data/app/views/admin/shared/_custom_links.html.erb +0 -1
- data/app/views/admin/shared/_custom_navigation.html.erb +0 -1
- data/app/views/layouts/slices/application.html.erb +0 -14
- data/lib/ext/file_store_cache.rb +0 -18
- data/lib/generators/slices/templates/mongoid.yml +0 -12
- data/lib/generators/templates/slices.rb +0 -209
- data/lib/rack_utf8_fix.rb +0 -10
- data/lib/set_link_renderer.rb +0 -31
- data/lib/slices/i18n.rb +0 -6
- data/lib/slices/i18n/backend.rb +0 -32
- data/lib/slices/will_paginate_mongoid.rb +0 -45
- data/lib/standard_tree.rb +0 -193
data/app/models/site_map.rb
CHANGED
@@ -8,8 +8,6 @@ class SiteMap
|
|
8
8
|
'{root}'
|
9
9
|
end
|
10
10
|
|
11
|
-
private
|
12
|
-
|
13
11
|
def self.set_children_for(parent_id, data)
|
14
12
|
parent = Page.find(parent_id)
|
15
13
|
data.each_with_index do |child_data, i|
|
@@ -21,4 +19,5 @@ class SiteMap
|
|
21
19
|
set_children_for(child_data['id'], child_data['children']) if child_data['children']
|
22
20
|
end
|
23
21
|
end
|
22
|
+
private_class_method :set_children_for
|
24
23
|
end
|
data/app/models/slice.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
class Slice
|
2
2
|
include Mongoid::Document
|
3
|
+
include Slices::LocalizedFields
|
3
4
|
include Slices::PositionHelper
|
4
5
|
|
5
6
|
field :container
|
@@ -51,6 +52,23 @@ class Slice
|
|
51
52
|
[_type, id].join(':')
|
52
53
|
end
|
53
54
|
|
55
|
+
# Print out the cache key. This will use the updated_at time of the
|
56
|
+
# page which this document is embedded in.
|
57
|
+
#
|
58
|
+
# This is usually called inside a cache() block
|
59
|
+
#
|
60
|
+
# @example Returns the cache key
|
61
|
+
# document.cache_key
|
62
|
+
#
|
63
|
+
# @return [ String ] the string with updated_at
|
64
|
+
def cache_key
|
65
|
+
if time = normal_or_set_page.try(:updated_at)
|
66
|
+
"#{model_key}/#{id}-#{time.to_s(:number)}"
|
67
|
+
else
|
68
|
+
super
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
54
72
|
def client_id?
|
55
73
|
attributes.include?('client_id')
|
56
74
|
end
|
@@ -63,10 +81,38 @@ class Slice
|
|
63
81
|
attributes.include?('_deleted')
|
64
82
|
end
|
65
83
|
|
84
|
+
def write_attributes(attrs)
|
85
|
+
attrs = attrs.symbolize_keys
|
86
|
+
self.embedded_relations.each do |field, metadata|
|
87
|
+
field = field.to_sym
|
88
|
+
next unless attrs.has_key?(field)
|
89
|
+
|
90
|
+
attrs[field] = (attrs[field] || []).map do |embedded_attrs|
|
91
|
+
if embedded_attrs[:_id].present?
|
92
|
+
embedded_doc = send(field).find(embedded_attrs[:_id])
|
93
|
+
embedded_doc.write_attributes(embedded_attrs)
|
94
|
+
embedded_doc
|
95
|
+
else
|
96
|
+
metadata.class_name.constantize.new(embedded_attrs)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
super
|
102
|
+
end
|
103
|
+
|
66
104
|
def as_json(*args)
|
67
105
|
attributes.symbolize_keys.except(:_id, :_type).tap do |result|
|
68
106
|
result.merge!(id: id, type: type)
|
69
107
|
result.merge!(client_id: client_id) if client_id? && new_record?
|
108
|
+
|
109
|
+
self.embedded_relations.each do |field, metadata|
|
110
|
+
result.merge!(field.to_sym => send(field).map(&:as_json))
|
111
|
+
end
|
112
|
+
|
113
|
+
localized_field_names.each do |name|
|
114
|
+
result.merge!(name => send(name))
|
115
|
+
end
|
70
116
|
end
|
71
117
|
end
|
72
118
|
|
@@ -77,4 +123,3 @@ class Slice
|
|
77
123
|
text_fields.map { |field| self[field.name.to_sym] }.join(" ")
|
78
124
|
end
|
79
125
|
end
|
80
|
-
|
data/app/models/snippet.rb
CHANGED
@@ -3,12 +3,21 @@ class Snippet
|
|
3
3
|
|
4
4
|
field :key
|
5
5
|
field :value
|
6
|
-
index :
|
6
|
+
index({ key: 1 })
|
7
7
|
|
8
|
-
scope :by_key, ascending(:key)
|
8
|
+
scope :by_key, ->{ ascending(:key) }
|
9
9
|
|
10
|
-
|
11
|
-
|
10
|
+
# Finds the value of a snippet as a html_safe string
|
11
|
+
#
|
12
|
+
# Snippet.find_for_key 'address'
|
13
|
+
# # => "54B Downham Road"
|
14
|
+
#
|
15
|
+
# @param [String] key Snippet key to lookup
|
16
|
+
# @return [String]
|
17
|
+
#
|
18
|
+
def self.find_for_key(key)
|
19
|
+
find_by(key: key).value.try(:html_safe)
|
20
|
+
rescue Mongoid::Errors::DocumentNotFound
|
12
21
|
end
|
13
22
|
|
14
23
|
def as_json(*args)
|
@@ -3,19 +3,6 @@ class PagePresenter < Presenter
|
|
3
3
|
@source.id.to_s
|
4
4
|
end
|
5
5
|
|
6
|
-
def available_slices
|
7
|
-
slices = {}
|
8
|
-
ObjectSpace.each_object do |object|
|
9
|
-
ActiveSupport::Deprecation.silence do
|
10
|
-
if object.class == Class && object.name =~ /\w+Slice$/
|
11
|
-
key = object.name.underscore.sub('_slice', '')
|
12
|
-
slices[key] = key.humanize
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|
16
|
-
slices
|
17
|
-
end
|
18
|
-
|
19
6
|
def name
|
20
7
|
@source.name
|
21
8
|
end
|
@@ -44,6 +31,14 @@ class PagePresenter < Presenter
|
|
44
31
|
'page_meta_extra'
|
45
32
|
end
|
46
33
|
|
34
|
+
def main_extra_templates
|
35
|
+
[main_extra_template]
|
36
|
+
end
|
37
|
+
|
38
|
+
def meta_extra_templates
|
39
|
+
[meta_extra_template]
|
40
|
+
end
|
41
|
+
|
47
42
|
def breadcrumbs
|
48
43
|
[@source] + @source.ancestors
|
49
44
|
end
|
@@ -0,0 +1 @@
|
|
1
|
+
<%= form.cms_select :layout, @layouts %>
|
@@ -9,16 +9,20 @@ $(function() {
|
|
9
9
|
mainTemplate: '<%= @page.main_template %>',
|
10
10
|
metaTemplate: '<%= @page.meta_template %>',
|
11
11
|
|
12
|
-
|
13
|
-
|
12
|
+
mainExtraTemplates: <%= raw @page.main_extra_templates.to_json %>,
|
13
|
+
metaExtraTemplates: <%= raw @page.meta_extra_templates.to_json %>,
|
14
14
|
|
15
15
|
super_user: <%= current_admin.super? %>,
|
16
|
+
i18n: <%= Slices::Config.i18n? %>,
|
17
|
+
|
18
|
+
loadPagePath: '/admin/pages/{{id}}.json?locale=<%= I18n.locale %>',
|
19
|
+
savePagePath: '/admin/pages/{{id}}.json?locale=<%= I18n.locale %>',
|
16
20
|
|
17
21
|
<% if @page.editing_entry_content_slices?(params[:entries]) %>
|
18
22
|
mainTemplate: 'set_page_entry_content_main',
|
19
23
|
metaTemplate: 'set_page_entry_content_meta',
|
20
|
-
loadPagePath: '/admin/pages/{{id}}?entries=1',
|
21
|
-
savePagePath: '/admin/pages/{{id}}?entries=1'
|
24
|
+
loadPagePath: '/admin/pages/{{id}}?locale=<%= I18n.locale %>&entries=1',
|
25
|
+
savePagePath: '/admin/pages/{{id}}?locale=<%= I18n.locale %>&entries=1'
|
22
26
|
<% end %>
|
23
27
|
}
|
24
28
|
);
|
@@ -6,7 +6,11 @@
|
|
6
6
|
<%= hidden_field_tag :type, params[:type] %>
|
7
7
|
<% end %>
|
8
8
|
<%= form.cms_text_field :name, required: false %>
|
9
|
-
|
9
|
+
|
10
|
+
<%= render(
|
11
|
+
partial: Slices::Config.page_fields_template,
|
12
|
+
locals: { form: form }
|
13
|
+
) %>
|
10
14
|
</ul>
|
11
15
|
<div class="bottom-toolbar">
|
12
16
|
<%= form.submit value: "Create Page" %>
|
@@ -1,18 +1,34 @@
|
|
1
1
|
<form id="slices-form">
|
2
|
-
<div id="container-actions"
|
3
|
-
|
4
|
-
<
|
5
|
-
<
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
</
|
2
|
+
<div id="container-actions">
|
3
|
+
<% if Slices::Config.i18n? %>
|
4
|
+
<div class="toolbar translations">
|
5
|
+
<nav>
|
6
|
+
Edit in
|
7
|
+
<ul>
|
8
|
+
<% I18n.available_locales.each do |locale| %>
|
9
|
+
<li>
|
10
|
+
<%= link_to_if locale != I18n.locale, t("locales.#{locale}", default: locale.to_s), admin_page_path(@page.source, locale: locale) %>
|
11
|
+
</li>
|
12
|
+
<% end %>
|
13
|
+
</ul>
|
14
|
+
</nav>
|
15
|
+
</div>
|
16
|
+
<% end %>
|
17
|
+
<div class="toolbar">
|
18
|
+
<nav>
|
19
|
+
<ul>
|
20
|
+
<li class="back"><%= link_to "Cancel editing (lose unsaved changes)", admin_site_maps_path %></li>
|
21
|
+
</ul>
|
22
|
+
</nav>
|
23
|
+
<div class="actions">
|
24
|
+
<ul>
|
25
|
+
<li><a id="minimise" class="button">Minimise all slices</a></li>
|
26
|
+
<li>
|
27
|
+
<%= link_to_view_page(@page.source, id: 'page-view-on-site', target: 'site', class: 'button standard') %>
|
28
|
+
</li>
|
29
|
+
<li><input type="submit" value="Save changes" id="save-changes" disabled></li>
|
30
|
+
</ul>
|
31
|
+
</div>
|
16
32
|
</div>
|
17
33
|
</div>
|
18
34
|
|
@@ -1,20 +1,31 @@
|
|
1
|
-
<li class="
|
2
|
-
<div>
|
1
|
+
<li class="<%= page_classes(page) %>" rel="<%= page.id %>">
|
2
|
+
<div class="page-item">
|
3
3
|
<h2><%= link_to(page.name, admin_page_path(page)) %></h2>
|
4
4
|
|
5
5
|
<% unless page.virtual? %>
|
6
6
|
<span class="item-actions primary">
|
7
|
-
<%=
|
8
|
-
|
7
|
+
<%= render(
|
8
|
+
partial: Slices::Config.page_actions_template,
|
9
|
+
locals: { page: page }
|
10
|
+
) %>
|
9
11
|
</span>
|
10
12
|
|
11
13
|
<%= link_to_delete page unless page.home? %>
|
12
14
|
<% end %>
|
15
|
+
|
16
|
+
<% if page.home? %>
|
17
|
+
<div class="toggle-actions">
|
18
|
+
<a class="hide-all-children button">Minimise all</a>
|
19
|
+
<a class="show-all-children button">Expand all</a>
|
20
|
+
</div>
|
21
|
+
<% elsif children.any? %>
|
22
|
+
<a class="toggle-children" href="#page-<%= page.id %>-children"></a>
|
23
|
+
<% end %>
|
13
24
|
</div>
|
14
25
|
|
15
|
-
<% if
|
16
|
-
<ol>
|
17
|
-
<%= list_pages(
|
26
|
+
<% if children.any? %>
|
27
|
+
<ol id="page-<%= page.id %>-children" class="page-children">
|
28
|
+
<%= list_pages(children) %>
|
18
29
|
</ol>
|
19
30
|
<% end %>
|
20
31
|
</li>
|
@@ -1,5 +1,5 @@
|
|
1
|
-
<li class="
|
2
|
-
<div>
|
1
|
+
<li class="<%= page_classes(page) %>" rel="<%= page.id %>">
|
2
|
+
<div class="page-item">
|
3
3
|
<h2><%= link_to page.name, admin_page_entries_path(page) %></h2>
|
4
4
|
|
5
5
|
<span class="item-actions primary">
|
@@ -15,9 +15,9 @@
|
|
15
15
|
|
16
16
|
</div>
|
17
17
|
|
18
|
-
<% if
|
19
|
-
<ol>
|
20
|
-
<%= list_pages
|
18
|
+
<% if children.any? %>
|
19
|
+
<ol class="page-children">
|
20
|
+
<%= list_pages children %>
|
21
21
|
</ol>
|
22
22
|
<% end %>
|
23
23
|
</li>
|
@@ -6,7 +6,6 @@
|
|
6
6
|
<title><%= cms_title %></title>
|
7
7
|
|
8
8
|
<%= stylesheet_link_tag 'slices/slices' %>
|
9
|
-
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
|
10
9
|
<%= javascript_include_tag 'slices/slices' %>
|
11
10
|
<%= render "admin/shared/asset_storage" %>
|
12
11
|
|
@@ -52,21 +51,5 @@
|
|
52
51
|
<span class="path">{{path}}</span>
|
53
52
|
</li>
|
54
53
|
</script>
|
55
|
-
|
56
|
-
<% if Rails.env.production? %>
|
57
|
-
<script type="text/javascript">
|
58
|
-
var _gaq = _gaq || [];
|
59
|
-
_gaq.push(['_setAccount', 'UA-298580-55']);
|
60
|
-
_gaq.push(['_setDomainName', 'none']);
|
61
|
-
_gaq.push(['_setAllowLinker', true]);
|
62
|
-
_gaq.push(['_setCustomVar', 1, 'Slices Version', '<%= Slices::VERSION %>', 3]);
|
63
|
-
_gaq.push(['_trackPageview']);
|
64
|
-
(function() {
|
65
|
-
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
|
66
|
-
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
|
67
|
-
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
|
68
|
-
})();
|
69
|
-
</script>
|
70
|
-
<% end -%>
|
71
54
|
</body>
|
72
55
|
</html>
|
data/config/routes.rb
CHANGED
@@ -1,21 +1,18 @@
|
|
1
1
|
class PageConstraints
|
2
2
|
def matches?(request)
|
3
|
-
!request.path.start_with?(
|
3
|
+
!request.path.start_with?('/admin/auth')
|
4
4
|
end
|
5
5
|
end
|
6
6
|
|
7
7
|
Rails.application.routes.draw do
|
8
8
|
|
9
|
-
devise_for :admin,
|
10
|
-
:passwords => "admin/auth/passwords",
|
11
|
-
:sessions => "admin/auth/sessions",
|
12
|
-
}
|
9
|
+
devise_for :admin, Slices::Config.devise_for_options
|
13
10
|
|
14
11
|
namespace :admin do
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
12
|
+
get 'site_maps' => 'site_maps#index', as: :root # Devise redirects here after sign in
|
13
|
+
get 'site_maps' => 'site_maps#index'
|
14
|
+
put 'site_maps/update' => 'site_maps#update'
|
15
|
+
get 'pages/search' => 'page_search#show'
|
19
16
|
resources :pages, :except => [:index, :edit] do
|
20
17
|
resources :entries, :only => [:index]
|
21
18
|
end
|
@@ -23,18 +20,18 @@ Rails.application.routes.draw do
|
|
23
20
|
resources :snippets
|
24
21
|
resources :admins
|
25
22
|
end
|
26
|
-
|
23
|
+
get '/admin' => redirect('/admin/site_maps')
|
27
24
|
|
28
|
-
|
29
|
-
|
25
|
+
get 'slices/templates(/:slice)/:name.:format' => 'static_assets#templates'
|
26
|
+
get ':action/:asset_type(/:folder)/*name.:format' => 'static_assets',
|
30
27
|
:constraints => {
|
31
28
|
:asset_type => /(stylesheets|javascripts|images)/,
|
32
29
|
:action => /(slices|sites)/
|
33
30
|
}, :as => :static_assets
|
34
31
|
|
35
|
-
|
36
|
-
|
37
|
-
|
32
|
+
get ':status.html' => 'pages#virtual_error_pages'
|
33
|
+
post '*path' => 'pages#create'
|
34
|
+
get '*path' => 'pages#show', as: :page, :constraints => PageConstraints.new
|
38
35
|
|
39
|
-
root :
|
36
|
+
root to: 'pages#show'
|
40
37
|
end
|
@@ -18,25 +18,24 @@ class <%= page_name.classify %>Presenter < PagePresenter
|
|
18
18
|
end
|
19
19
|
|
20
20
|
<%- if options.with_entry_templates? -%>
|
21
|
-
def
|
22
|
-
'<%= file_name %>/<%= page_name %>_main'
|
21
|
+
def main_extra_templates
|
22
|
+
super + ['<%= file_name %>/<%= page_name %>_main']
|
23
23
|
end
|
24
24
|
|
25
|
-
def
|
26
|
-
'<%= file_name %>/<%= page_name %>_meta'
|
25
|
+
def meta_extra_templates
|
26
|
+
super + ['<%= file_name %>/<%= page_name %>_meta']
|
27
27
|
end
|
28
28
|
<%- else -%>
|
29
|
-
# If you want to display extra information while editing
|
30
|
-
#
|
31
|
-
#
|
32
|
-
# methods.
|
29
|
+
# If you want to display extra information while editing a page you'll need
|
30
|
+
# to define some .hbs files, and then tell the CMS to use them by
|
31
|
+
# uncommenting these methods.
|
33
32
|
|
34
|
-
# def
|
35
|
-
# '<%= file_name %>/<%= page_name %>_main'
|
33
|
+
# def main_extra_templates
|
34
|
+
# super + ['<%= file_name %>/<%= page_name %>_main']
|
36
35
|
# end
|
37
36
|
|
38
|
-
# def
|
39
|
-
# '<%= file_name %>/<%= page_name %>_meta'
|
37
|
+
# def meta_extra_templates
|
38
|
+
# super + ['<%= file_name %>/<%= page_name %>_meta']
|
40
39
|
# end
|
41
40
|
<%- end -%>
|
42
41
|
|
@@ -1,8 +1,8 @@
|
|
1
|
-
<%%= will_paginate slice.page_entries,
|
1
|
+
<%%= will_paginate slice.page_entries, class: 'pagination above' %>
|
2
2
|
<ul class="entries">
|
3
3
|
<%% slice.page_entries.each do |entry| -%>
|
4
4
|
<li><%%= link_to entry.name, entry.path %></li>
|
5
5
|
<%% end -%>
|
6
6
|
</ul>
|
7
|
-
<%%= will_paginate slice.page_entries,
|
7
|
+
<%%= will_paginate slice.page_entries, class: 'pagination below' %>
|
8
8
|
|