slices 1.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.
- data/CHANGELOG.md +3 -0
- data/README.md +51 -0
- data/Rakefile +9 -0
- data/app/assets/images/slices/ajax-loader.gif +0 -0
- data/app/assets/images/slices/asset-background.png +0 -0
- data/app/assets/images/slices/asset-spinner.gif +0 -0
- data/app/assets/images/slices/bg_header.gif +0 -0
- data/app/assets/images/slices/black-Linen.png +0 -0
- data/app/assets/images/slices/calendar.svg +68 -0
- data/app/assets/images/slices/chosen-sprite.png +0 -0
- data/app/assets/images/slices/drag-handle.svg +9 -0
- data/app/assets/images/slices/icon_admins.png +0 -0
- data/app/assets/images/slices/icon_app.png +0 -0
- data/app/assets/images/slices/icon_assets.png +0 -0
- data/app/assets/images/slices/icon_collapse.png +0 -0
- data/app/assets/images/slices/icon_drag.png +0 -0
- data/app/assets/images/slices/icon_files.png +0 -0
- data/app/assets/images/slices/icon_generic_file.png +0 -0
- data/app/assets/images/slices/icon_images.png +0 -0
- data/app/assets/images/slices/icon_padlock.png +0 -0
- data/app/assets/images/slices/icon_page.png +0 -0
- data/app/assets/images/slices/icon_search.png +0 -0
- data/app/assets/images/slices/icon_set-link.png +0 -0
- data/app/assets/images/slices/icon_set.png +0 -0
- data/app/assets/images/slices/icon_sitemap.png +0 -0
- data/app/assets/images/slices/icon_snippets.png +0 -0
- data/app/assets/images/slices/icon_template.jpg +0 -0
- data/app/assets/images/slices/icon_upload_happy.png +0 -0
- data/app/assets/images/slices/icon_upload_sad.png +0 -0
- data/app/assets/images/slices/icon_upload_thinking.png +0 -0
- data/app/assets/images/slices/noise.png +0 -0
- data/app/assets/images/slices/sitemap_icon_ghost.png +0 -0
- data/app/assets/images/slices/sitemap_icon_home.png +0 -0
- data/app/assets/images/slices/sitemap_icon_page.png +0 -0
- data/app/assets/images/slices/sitemap_icon_set_page.png +0 -0
- data/app/assets/images/slices/sitemap_icon_virtual_page.png +0 -0
- data/app/assets/images/slices/sitemap_overlay.png +0 -0
- data/app/assets/images/slices/spinner.gif +0 -0
- data/app/assets/images/slices/trash.png +0 -0
- data/app/assets/javascripts/admin.js.erb +18 -0
- data/app/assets/javascripts/slices/app/backbones/admins.js +114 -0
- data/app/assets/javascripts/slices/app/backbones/entries.js +172 -0
- data/app/assets/javascripts/slices/app/backbones/generic.js +101 -0
- data/app/assets/javascripts/slices/app/backbones/snippets.js +113 -0
- data/app/assets/javascripts/slices/app/helpers/assets.js +61 -0
- data/app/assets/javascripts/slices/app/helpers/breadcrumbs.js +30 -0
- data/app/assets/javascripts/slices/app/helpers/composer.js +26 -0
- data/app/assets/javascripts/slices/app/helpers/date_field.js +16 -0
- data/app/assets/javascripts/slices/app/helpers/get_value.js +31 -0
- data/app/assets/javascripts/slices/app/helpers/icon_upload_names.js.erb +5 -0
- data/app/assets/javascripts/slices/app/helpers/layout.js +20 -0
- data/app/assets/javascripts/slices/app/helpers/sitemap.js +150 -0
- data/app/assets/javascripts/slices/app/helpers/slice_preview.js +48 -0
- data/app/assets/javascripts/slices/app/helpers/tagging.js +73 -0
- data/app/assets/javascripts/slices/app/helpers/token_field.js +17 -0
- data/app/assets/javascripts/slices/app/helpers/upload_icons.js.erb +5 -0
- data/app/assets/javascripts/slices/app/helpers/uploader.js +127 -0
- data/app/assets/javascripts/slices/app/models/asset.js +29 -0
- data/app/assets/javascripts/slices/app/models/asset_collection.js +41 -0
- data/app/assets/javascripts/slices/app/models/attachment.js +29 -0
- data/app/assets/javascripts/slices/app/models/attachment_collection.js +7 -0
- data/app/assets/javascripts/slices/app/models/composer_item.js +1 -0
- data/app/assets/javascripts/slices/app/models/composer_item_collection.js +3 -0
- data/app/assets/javascripts/slices/app/models/file.js +103 -0
- data/app/assets/javascripts/slices/app/models/page.js +186 -0
- data/app/assets/javascripts/slices/app/models/s3_file.js +64 -0
- data/app/assets/javascripts/slices/app/slices.js +661 -0
- data/app/assets/javascripts/slices/app/views/asset_editor_view.js.erb +209 -0
- data/app/assets/javascripts/slices/app/views/asset_library_view.js +720 -0
- data/app/assets/javascripts/slices/app/views/asset_thumb_view.js.erb +191 -0
- data/app/assets/javascripts/slices/app/views/attachment_composer_view.js +350 -0
- data/app/assets/javascripts/slices/app/views/attachment_view.js +101 -0
- data/app/assets/javascripts/slices/app/views/calendar_view.js +198 -0
- data/app/assets/javascripts/slices/app/views/composer_item_view.js +54 -0
- data/app/assets/javascripts/slices/app/views/composer_view.js +130 -0
- data/app/assets/javascripts/slices/app/views/date_field_view.js +177 -0
- data/app/assets/javascripts/slices/app/views/file_view.js +142 -0
- data/app/assets/javascripts/slices/app/views/token_field_view.js +253 -0
- data/app/assets/javascripts/slices/lib/freeze.js +14 -0
- data/app/assets/javascripts/slices/lib/human_file_size.js +16 -0
- data/app/assets/javascripts/slices/lib/json_patch.js +9 -0
- data/app/assets/javascripts/slices/lib/moment.js +47 -0
- data/app/assets/javascripts/slices/lib/plugins.js +101 -0
- data/app/assets/javascripts/slices/lib/sortable.js +14 -0
- data/app/assets/javascripts/slices/slices.js +27 -0
- data/app/assets/javascripts/slices/vendor/autoscroll.js +188 -0
- data/app/assets/javascripts/slices/vendor/backbone.js +38 -0
- data/app/assets/javascripts/slices/vendor/handlebars.js +1920 -0
- data/app/assets/javascripts/slices/vendor/jqmodal.js +69 -0
- data/app/assets/javascripts/slices/vendor/jquery-ui.js +274 -0
- data/app/assets/javascripts/slices/vendor/jquery-ui_nested-sortable.js +357 -0
- data/app/assets/javascripts/slices/vendor/jquery.ajaxprogress.js +76 -0
- data/app/assets/javascripts/slices/vendor/jquery.js +2 -0
- data/app/assets/javascripts/slices/vendor/livefield.js +459 -0
- data/app/assets/javascripts/slices/vendor/moment.js +6 -0
- data/app/assets/javascripts/slices/vendor/rails.js +315 -0
- data/app/assets/javascripts/slices/vendor/underscore-string.js +1 -0
- data/app/assets/javascripts/slices/vendor/underscore.js +5 -0
- data/app/assets/stylesheets/admin.css +1 -0
- data/app/assets/stylesheets/slices/admin.css.erb +2237 -0
- data/app/assets/stylesheets/slices/reset_html5.css +106 -0
- data/app/assets/stylesheets/slices/slices.css +7 -0
- data/app/controllers/admin/admin_controller.rb +10 -0
- data/app/controllers/admin/admins_controller.rb +76 -0
- data/app/controllers/admin/assets_controller.rb +53 -0
- data/app/controllers/admin/auth/omniauth_callbacks_controller.rb +15 -0
- data/app/controllers/admin/auth/passwords_controller.rb +4 -0
- data/app/controllers/admin/auth/sessions_controller.rb +4 -0
- data/app/controllers/admin/entries_controller.rb +88 -0
- data/app/controllers/admin/page_search_controller.rb +12 -0
- data/app/controllers/admin/pages_controller.rb +103 -0
- data/app/controllers/admin/site_maps_controller.rb +15 -0
- data/app/controllers/admin/snippets_controller.rb +33 -0
- data/app/controllers/application_controller.rb +4 -0
- data/app/controllers/pages_controller.rb +45 -0
- data/app/controllers/slices_controller.rb +63 -0
- data/app/controllers/static_assets_controller.rb +52 -0
- data/app/helpers/admin/admin_helper.rb +63 -0
- data/app/helpers/admin/assets_helper.rb +36 -0
- data/app/helpers/admin/entries_helper.rb +13 -0
- data/app/helpers/admin/site_maps_helper.rb +104 -0
- data/app/helpers/assets_helper.rb +64 -0
- data/app/helpers/navigation_helper.rb +195 -0
- data/app/helpers/pages_helper.rb +119 -0
- data/app/models/admin.rb +34 -0
- data/app/models/asset.rb +211 -0
- data/app/models/attachment.rb +11 -0
- data/app/models/layout.rb +44 -0
- data/app/models/page.rb +214 -0
- data/app/models/placeholder_slice.rb +8 -0
- data/app/models/set_page.rb +12 -0
- data/app/models/set_slice.rb +57 -0
- data/app/models/site_map.rb +24 -0
- data/app/models/slice.rb +80 -0
- data/app/models/snippet.rb +21 -0
- data/app/observers/asset_observer.rb +6 -0
- data/app/observers/page_observer.rb +37 -0
- data/app/presenters/entry_presenter.rb +17 -0
- data/app/presenters/page_presenter.rb +67 -0
- data/app/presenters/presenter.rb +9 -0
- data/app/presenters/set_page_presenter.rb +2 -0
- data/app/views/admin/admins/index.html.erb +26 -0
- data/app/views/admin/admins/show.html.erb +27 -0
- data/app/views/admin/assets/index.html.erb +1 -0
- data/app/views/admin/auth/passwords/edit.html.erb +20 -0
- data/app/views/admin/auth/passwords/new.html.erb +14 -0
- data/app/views/admin/auth/sessions/_form.html.erb +35 -0
- data/app/views/admin/auth/sessions/new.html.erb +14 -0
- data/app/views/admin/entries/index.html.erb +32 -0
- data/app/views/admin/pages/_breadcrumbs.html.erb +32 -0
- data/app/views/admin/pages/_slices.html.erb +27 -0
- data/app/views/admin/pages/new.html.erb +14 -0
- data/app/views/admin/pages/show.html.erb +50 -0
- data/app/views/admin/shared/_asset_storage.html.erb +17 -0
- data/app/views/admin/shared/_custom_links.html.erb +1 -0
- data/app/views/admin/shared/_custom_navigation.html.erb +1 -0
- data/app/views/admin/shared/_navigation.html.erb +5 -0
- data/app/views/admin/site_maps/_page_li.html.erb +20 -0
- data/app/views/admin/site_maps/_set_page_li.html.erb +23 -0
- data/app/views/admin/site_maps/index.html.erb +29 -0
- data/app/views/admin/snippets/form.html.erb +12 -0
- data/app/views/admin/snippets/index.html.erb +20 -0
- data/app/views/admin/snippets/update.html.erb +0 -0
- data/app/views/layouts/admin.html.erb +72 -0
- data/lib/ext/file_store_cache.rb +18 -0
- data/lib/generators/humans/USAGE +8 -0
- data/lib/generators/humans/humans_generator.rb +10 -0
- data/lib/generators/humans/templates/humans.txt +6 -0
- data/lib/generators/slice/USAGE +28 -0
- data/lib/generators/slice/slice_generator.rb +123 -0
- data/lib/generators/slice/templates/main_fields.hbs +11 -0
- data/lib/generators/slice/templates/meta_fields.hbs +11 -0
- data/lib/generators/slice/templates/page.rb +19 -0
- data/lib/generators/slice/templates/presenter.rb +53 -0
- data/lib/generators/slice/templates/set.html.erb +8 -0
- data/lib/generators/slice/templates/set_slice.rb +14 -0
- data/lib/generators/slice/templates/set_slice_fields.hbs +5 -0
- data/lib/generators/slice/templates/show.html.erb +48 -0
- data/lib/generators/slice/templates/show_slice.rb +20 -0
- data/lib/generators/slice/templates/slice.rb +58 -0
- data/lib/generators/slice/templates/slice_fields.hbs +74 -0
- data/lib/generators/templates/slices.rb +211 -0
- data/lib/mongo_search.rb +84 -0
- data/lib/paperclip_validator.rb +5 -0
- data/lib/rack_utf8_fix.rb +10 -0
- data/lib/sRGB.icc +0 -0
- data/lib/set_link_renderer.rb +31 -0
- data/lib/slices.rb +68 -0
- data/lib/slices/asset/maker.rb +55 -0
- data/lib/slices/asset/rename.rb +67 -0
- data/lib/slices/available_slices.rb +43 -0
- data/lib/slices/cms_form_builder.rb +42 -0
- data/lib/slices/config.rb +93 -0
- data/lib/slices/container_parser.rb +70 -0
- data/lib/slices/generator_macros.rb +36 -0
- data/lib/slices/has_attachments.rb +111 -0
- data/lib/slices/has_slices.rb +88 -0
- data/lib/slices/i18n.rb +6 -0
- data/lib/slices/i18n/backend.rb +32 -0
- data/lib/slices/i18n_backend.rb +24 -0
- data/lib/slices/paperclip.rb +13 -0
- data/lib/slices/position_helper.rb +98 -0
- data/lib/slices/renderer.rb +52 -0
- data/lib/slices/slices_engine.rb +51 -0
- data/lib/slices/split_date_time_field.rb +14 -0
- data/lib/slices/tasks/assets.rake +35 -0
- data/lib/slices/tasks/db.rake +50 -0
- data/lib/slices/tasks/seeds.rake +93 -0
- data/lib/slices/tasks/validate.rake +62 -0
- data/lib/slices/tree.rb +306 -0
- data/lib/slices/version.rb +4 -0
- data/lib/slices/will_paginate.rb +12 -0
- data/lib/slices/will_paginate_mongoid.rb +45 -0
- data/lib/standard_tree.rb +193 -0
- metadata +483 -0
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
# This module provices helpers for rendering common navigation from page structure
|
|
2
|
+
#
|
|
3
|
+
# primary_navigation
|
|
4
|
+
# # => <ul id="primary_navigation">
|
|
5
|
+
# # => <li class="first active nav-home"><a href="/">Home page</a></li>
|
|
6
|
+
# # => <li class="nav-about"><a href="/about">About us</a></li>
|
|
7
|
+
# # => <li class="last nav-blog"><a href="/blog">Blog</a></li>
|
|
8
|
+
# # => </ul>
|
|
9
|
+
#
|
|
10
|
+
module NavigationHelper
|
|
11
|
+
|
|
12
|
+
# primary_navigation
|
|
13
|
+
# # => <ul id="primary_navigation">
|
|
14
|
+
# # => <li class="first active nav-home"><a href="/">Home page</a></li>
|
|
15
|
+
# # => <li class="nav-about"><a href="/about">About us</a></li>
|
|
16
|
+
# # => <li class="nav-parent"><a href="/parent">Parent</a></li>
|
|
17
|
+
# # => <li class="nav-aunt"><a href="/aunt">Aunt</a></li>
|
|
18
|
+
# # => <li class="nav-uncle"><a href="/uncle">Uncle</a></li>
|
|
19
|
+
# # => <li class="last nav-blog"><a href="/blog">Blog</a></li>
|
|
20
|
+
# # => </ul>
|
|
21
|
+
#
|
|
22
|
+
# @param [String] id Dom id of the surronding ul element
|
|
23
|
+
# @return [String]
|
|
24
|
+
#
|
|
25
|
+
def primary_navigation(id = 'primary_navigation')
|
|
26
|
+
cache 'page/navigation/' + id + '/' + @page.id.to_s do
|
|
27
|
+
benchmark 'Rendered primary_navigation' do
|
|
28
|
+
nav = navigation(page: Page.home, depth: 1, id: id)
|
|
29
|
+
safe_concat nav
|
|
30
|
+
''
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Render the secondary navigation if the current page isn't already rendered
|
|
36
|
+
# in the primary navigation.
|
|
37
|
+
#
|
|
38
|
+
# secondary_navigaiton :depth => 10
|
|
39
|
+
# # => <ul id="secondary_navigation">
|
|
40
|
+
# # => <li class="first nav-parent-child"><a href="/parent/child">Child</a>
|
|
41
|
+
# # => <ul>
|
|
42
|
+
# # => <li class="first last nav-parent-child-grand-child"><a href="/parent/child/grand-child">Grand child</a></li>
|
|
43
|
+
# # => </ul>
|
|
44
|
+
# # => </li>
|
|
45
|
+
# # => <li class="nav-parent-sibling"><a href="/parent/sibling">Sibling</a></li>
|
|
46
|
+
# # => <li class="last nav-parent-youngest"><a href="/parent/youngest">Youngest</a></li>
|
|
47
|
+
# # => </ul>
|
|
48
|
+
#
|
|
49
|
+
# @param [Hash] options
|
|
50
|
+
# @option options [Integer] :depth (1) Number of levels to render
|
|
51
|
+
# @option options [Integer] :id ('secondary_navigation') Id of surrounding UL
|
|
52
|
+
# @return [String]
|
|
53
|
+
#
|
|
54
|
+
def secondary_navigation(options = {})
|
|
55
|
+
cache 'page/navigation/secondary/' + @page.id.to_s do
|
|
56
|
+
benchmark 'Rendered secondary_navigation' do
|
|
57
|
+
secondary_ancestors = (@page.ancestors[-2] || @page).navigable_children
|
|
58
|
+
nav = if secondary_ancestors.empty? || @page.home?
|
|
59
|
+
''
|
|
60
|
+
else
|
|
61
|
+
page = secondary_ancestors.first
|
|
62
|
+
navigation(options.reverse_merge(page: page, id: 'secondary_navigation'))
|
|
63
|
+
end
|
|
64
|
+
safe_concat nav
|
|
65
|
+
''
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Use the page structure to render nested navigation, each visible page is
|
|
71
|
+
# rendered in a +li+ element with child pages in a nested +ul+
|
|
72
|
+
#
|
|
73
|
+
# The first +li+ in a +ul+ has the class +first+, the last +li+ in a +ul+ has
|
|
74
|
+
# the class +last+ and the current page has the class +active+.
|
|
75
|
+
#
|
|
76
|
+
# Each +li+ has a class identifier generated from it's path and prefixed with
|
|
77
|
+
# +nav+, for example a page with path +/parent/child+ would have the class
|
|
78
|
+
# +nav-parent-child+. This identifier is a class is to avoid duplicate +ids+
|
|
79
|
+
# if multiple navigation elements are used on a page.
|
|
80
|
+
#
|
|
81
|
+
# The home page is assumed to be at the same level as it's children.
|
|
82
|
+
#
|
|
83
|
+
# navigation depth: 4, page: Page.home, id: 'navigation'
|
|
84
|
+
# # => <ul id="navigation">
|
|
85
|
+
# # => <li class="first nav-home"><a href="/">Home page</a></li>
|
|
86
|
+
# # => <li class="nav-about"><a href="/about">About us</a></li>
|
|
87
|
+
# # => <li class="active nav-parent">
|
|
88
|
+
# # => <a href="/parent">Parent</a>
|
|
89
|
+
# # => <ul>
|
|
90
|
+
# # => <li class="first nav-parent-child">
|
|
91
|
+
# # => <a href="/parent/child">Child</a>
|
|
92
|
+
# # => <ul>
|
|
93
|
+
# # => <li class="first last nav-parent-child-grand-child"><a href="/parent/child/grand-child">Grand child</a></li>
|
|
94
|
+
# # => </ul>
|
|
95
|
+
# # => </li>
|
|
96
|
+
# # => <li class="nav-parent-sibling"><a href="/parent/sibling">Sibling</a></li>
|
|
97
|
+
# # => <li class="last nav-parent-youngest"><a href="/parent/youngest">Youngest</a></li>
|
|
98
|
+
# # => </ul>
|
|
99
|
+
# # => </li>
|
|
100
|
+
# # => <li class="nav-aunt"><a href="/aunt">Aunt</a></li>
|
|
101
|
+
# # => <li class="nav-uncle"><a href="/uncle">Uncle</a></li>
|
|
102
|
+
# # => <li class="last nav-blog"><a href="/blog">Blog</a></li>
|
|
103
|
+
# # => </ul>
|
|
104
|
+
#
|
|
105
|
+
# navigation depth: 1, page: Page.home
|
|
106
|
+
# # => <ul>
|
|
107
|
+
# # => <li class="first nav-home"><a href="/">Home page</a></li>
|
|
108
|
+
# # => <li class="nav-about"><a href="/about">About us</a></li>
|
|
109
|
+
# # => <li class="active nav-parent"><a href="/parent">Parent</a></li>
|
|
110
|
+
# # => <li class="nav-aunt"><a href="/aunt">Aunt</a></li>
|
|
111
|
+
# # => <li class="nav-uncle"><a href="/uncle">Uncle</a></li>
|
|
112
|
+
# # => <li class="last nav-blog"><a href="/blog">Blog</a></li>
|
|
113
|
+
# # => </ul>
|
|
114
|
+
#
|
|
115
|
+
# @param [Hash] options
|
|
116
|
+
# @option options [Integer] :depth (1) Number of levels to render
|
|
117
|
+
# @option options [Integer] :page (current_page) Page to start rendering
|
|
118
|
+
# @option options [Integer] :id Id of surrounding UL
|
|
119
|
+
# @option options [Integer] :class Class of surrounding UL
|
|
120
|
+
# @return [String]
|
|
121
|
+
#
|
|
122
|
+
def navigation(options = {})
|
|
123
|
+
options.reverse_merge!( page: @page, depth: 1 )
|
|
124
|
+
page = options[:page]
|
|
125
|
+
children = page.home? ? page.navigable_children : page.parent.navigable_children
|
|
126
|
+
html = (is_home_and_shown_in_nav?(page) ? nav_link(Page.home) : '').html_safe
|
|
127
|
+
html << list_items(children, options[:depth])
|
|
128
|
+
content_tag(:ul, html, options.slice(:id, :class))
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# Link to a +page+, using the page name as the link name.
|
|
132
|
+
#
|
|
133
|
+
# link_to Page.find_by_path('/parent')
|
|
134
|
+
# # => <a href="/parent">Parent</a>
|
|
135
|
+
#
|
|
136
|
+
# @param [Page] page Page to link to
|
|
137
|
+
# @return [String]
|
|
138
|
+
#
|
|
139
|
+
def link_to_page(page)
|
|
140
|
+
link_to(page.name, page.navigation_path)
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
private
|
|
144
|
+
|
|
145
|
+
def list_items(pages, depth)
|
|
146
|
+
depth -= 1
|
|
147
|
+
pages.inject(''.html_safe) do |html, page|
|
|
148
|
+
html << nav_link(page, depth)
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def nav_link(page, depth = 0)
|
|
153
|
+
content_tag(:li, class: nav_list_item_class(page)) do
|
|
154
|
+
html = link_to_page(page)
|
|
155
|
+
if (depth > 0) && page.navigable_children.any?
|
|
156
|
+
html << content_tag(:ul, list_items(page.navigable_children, depth))
|
|
157
|
+
end
|
|
158
|
+
html
|
|
159
|
+
end + "\n"
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def is_home_and_shown_in_nav?(page)
|
|
163
|
+
page.home? && page.show_in_nav?
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def is_first_sibling?(page)
|
|
167
|
+
if page.home? || is_home_and_shown_in_nav?(page.parent)
|
|
168
|
+
page.home?
|
|
169
|
+
else
|
|
170
|
+
page.first_sibling?
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def is_active_sibling?(page)
|
|
175
|
+
if page.home?
|
|
176
|
+
page == @page
|
|
177
|
+
else
|
|
178
|
+
(page == @page) || @page.descended_from?(page)
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def nav_list_item_class(page)
|
|
183
|
+
classes = []
|
|
184
|
+
classes << 'first' if is_first_sibling?(page)
|
|
185
|
+
classes << 'active' if is_active_sibling?(page)
|
|
186
|
+
classes << 'last' if page.last_sibling?
|
|
187
|
+
classes << nav_list_identifier(page)
|
|
188
|
+
classes.empty? ? nil : classes.join(' ')
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
def nav_list_identifier(page)
|
|
192
|
+
page.home? ? 'nav-home' : ('nav' + page.path.gsub('/', '-'))
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# This module provides helper methods rendering pages and slices.
|
|
2
|
+
module PagesHelper
|
|
3
|
+
include NavigationHelper
|
|
4
|
+
include AssetsHelper
|
|
5
|
+
|
|
6
|
+
# Defines and renders a container in a layout.
|
|
7
|
+
#
|
|
8
|
+
# For example adding the following to a layout will create two containers called
|
|
9
|
+
# container_one and container_two
|
|
10
|
+
#
|
|
11
|
+
# <article class="container_one">
|
|
12
|
+
# <%= container "container_one" %>
|
|
13
|
+
# </article>
|
|
14
|
+
# <aside class="container_two">
|
|
15
|
+
# <%= container "container_two" %>
|
|
16
|
+
# </aside>
|
|
17
|
+
#
|
|
18
|
+
# @param [String] container Name of container
|
|
19
|
+
# @return [String] Contents of rendered slices in container
|
|
20
|
+
#
|
|
21
|
+
def container(container, options = {})
|
|
22
|
+
if @slice_renderer.present?
|
|
23
|
+
@slice_renderer.render_container(container)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Returns the text with all the Textile[http://www.textism.com/tools/textile] codes turned into HTML tags.
|
|
28
|
+
#
|
|
29
|
+
# @param [String] text Text to be textilized
|
|
30
|
+
# @return [String] Textilized string
|
|
31
|
+
#
|
|
32
|
+
def textilize(text)
|
|
33
|
+
unless text.blank?
|
|
34
|
+
red_cloth = RedCloth.new(text)
|
|
35
|
+
red_cloth.no_span_caps = true
|
|
36
|
+
red_cloth.to_html.html_safe
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Converts markdown to html.
|
|
41
|
+
#
|
|
42
|
+
# @param [String] text
|
|
43
|
+
# @return [String]
|
|
44
|
+
#
|
|
45
|
+
def markdown(text)
|
|
46
|
+
return if text.blank?
|
|
47
|
+
renderer = Redcarpet::Render::HTML.new(hard_wrap: true)
|
|
48
|
+
parser = Redcarpet::Markdown.new(renderer)
|
|
49
|
+
parser.render(text).html_safe
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Quick link to edit the current page in the CMS
|
|
53
|
+
#
|
|
54
|
+
# @return [String] Edit in CMS link
|
|
55
|
+
#
|
|
56
|
+
def edit_in_cms
|
|
57
|
+
if admin_signed_in?
|
|
58
|
+
links = []
|
|
59
|
+
links << link_to("Edit #{@page.class.to_s.underscore.humanize} in CMS", admin_page_path(@page), target: '_blank')
|
|
60
|
+
links << link_to('Edit template in CMS', admin_page_path(@page.parent, entries: 1), target: '_blank') if @page.entry?
|
|
61
|
+
content = links.collect {|l| content_tag(:p, l) }.join.html_safe
|
|
62
|
+
|
|
63
|
+
content_tag(:div, content, id: 'edit_in_cms')
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Translates the key and return an empty string if there is no translation.
|
|
68
|
+
#
|
|
69
|
+
#
|
|
70
|
+
# @param [String] key Translation key
|
|
71
|
+
# @param [Hash] options
|
|
72
|
+
# @return [String] Translated key
|
|
73
|
+
#
|
|
74
|
+
def if_t(key, options = {})
|
|
75
|
+
translate(key, options) || ''
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Asynchronous Google Analytics Tracking Code. Only added in production when the
|
|
79
|
+
# user is not signed into the CMS.
|
|
80
|
+
#
|
|
81
|
+
# google_analytics_tracking_code('ABC132')
|
|
82
|
+
#
|
|
83
|
+
# To track multiple accounts use the following
|
|
84
|
+
#
|
|
85
|
+
# google_analytics_tracking_code('ABC132', 'DEF546')
|
|
86
|
+
#
|
|
87
|
+
# @param [Array, String] web_property_ids Web property ID(s)
|
|
88
|
+
# @return [String] GA embed code
|
|
89
|
+
#
|
|
90
|
+
def google_analytics_tracking_code(*web_property_ids)
|
|
91
|
+
if web_property_ids.first.present? && add_tracking_code?
|
|
92
|
+
analytics_que = "['_setAccount', '#{web_property_ids.shift}'], ['_trackPageview'], ['_trackPageLoadTime']"
|
|
93
|
+
web_property_ids.each_with_index do |web_property_id, index|
|
|
94
|
+
account = (index + 98).chr
|
|
95
|
+
analytics_que << ", ['#{account}._setAccount', '#{web_property_id}'], ['#{account}._trackPageview'], ['#{account}._trackPageLoadTime']"
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
javascript_tag <<-JAVASCRIPT
|
|
99
|
+
var _gaq = _gaq || [];
|
|
100
|
+
_gaq.push(#{analytics_que});
|
|
101
|
+
(function() {
|
|
102
|
+
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
|
|
103
|
+
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
|
|
104
|
+
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
|
|
105
|
+
})();
|
|
106
|
+
JAVASCRIPT
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# If a tracking code should be added to the page. Returns +true+ when the app
|
|
111
|
+
# is in production mode and the user is not signed into the CMS
|
|
112
|
+
#
|
|
113
|
+
# @return [Boolean]
|
|
114
|
+
#
|
|
115
|
+
def add_tracking_code?
|
|
116
|
+
Rails.env.production? && ! admin_signed_in?
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
data/app/models/admin.rb
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
class Admin
|
|
2
|
+
include Mongoid::Document
|
|
3
|
+
include MongoSearch::Searchable
|
|
4
|
+
|
|
5
|
+
field :name
|
|
6
|
+
field :super_user, type: Boolean, :default => false
|
|
7
|
+
|
|
8
|
+
text_search_in :name, :email
|
|
9
|
+
|
|
10
|
+
validates_uniqueness_of :email, case_sensitive: false, scope: :site_id
|
|
11
|
+
|
|
12
|
+
validates_presence_of :email
|
|
13
|
+
attr_accessible :email, :password, :password_confirmation, :name
|
|
14
|
+
|
|
15
|
+
devise :database_authenticatable, :recoverable, :rememberable,
|
|
16
|
+
:trackable, :validatable, :omniauthable
|
|
17
|
+
|
|
18
|
+
def self.find_for_domain(domain)
|
|
19
|
+
where(email: /@#{domain}$/).first
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def super?
|
|
23
|
+
self.super_user == true
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def as_json(options)
|
|
27
|
+
super(options).tap do |json|
|
|
28
|
+
if current_admin = options[:current_admin]
|
|
29
|
+
json[:current_admin] = current_admin.id == id
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
data/app/models/asset.rb
ADDED
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
class Asset
|
|
2
|
+
include Mongoid::Document
|
|
3
|
+
include Mongoid::Timestamps
|
|
4
|
+
include MongoSearch::Searchable
|
|
5
|
+
include Mongoid::Paperclip
|
|
6
|
+
|
|
7
|
+
IMAGE_REGEX = /(jpg|jpeg|gif|png)/
|
|
8
|
+
NORMALIZED_EXTENSIONS = { ".jpeg" => ".jpg" }
|
|
9
|
+
|
|
10
|
+
validates_with PaperclipValidator
|
|
11
|
+
|
|
12
|
+
field :file_content_type, type: String
|
|
13
|
+
field :file_file_name, type: String
|
|
14
|
+
field :file_file_size, type: Integer
|
|
15
|
+
field :file_fingerprint, type: String
|
|
16
|
+
field :file_dimensions, type: Hash, default: {}
|
|
17
|
+
|
|
18
|
+
field :destroyed_at, type: Time
|
|
19
|
+
field :tags, type: String
|
|
20
|
+
field :page_cache, type: Array, default: []
|
|
21
|
+
|
|
22
|
+
text_search_in :file_file_name, :tags, :search_page_cache
|
|
23
|
+
|
|
24
|
+
has_mongoid_attached_file :file,
|
|
25
|
+
default_style: :admin,
|
|
26
|
+
max_size: 50.megabytes,
|
|
27
|
+
url: '/system/:attachment/:mon_year/:id/:style/:filename',
|
|
28
|
+
path: ':rails_root/public:url',
|
|
29
|
+
only_process: [:admin],
|
|
30
|
+
styles: Slices::Config.asset_styles,
|
|
31
|
+
convert_options: Slices::Config.asset_convert_options
|
|
32
|
+
|
|
33
|
+
before_file_post_process :is_image?
|
|
34
|
+
after_file_post_process :store_dimensions, if: :is_image?
|
|
35
|
+
before_save :reset_file_dimensions!, if: :file_fingerprint_changed?
|
|
36
|
+
before_save :rename_file
|
|
37
|
+
|
|
38
|
+
has_and_belongs_to_many :pages
|
|
39
|
+
|
|
40
|
+
index(
|
|
41
|
+
[
|
|
42
|
+
[ :destroyed_at, Mongo::ASCENDING ],
|
|
43
|
+
[ :created_at, Mongo::DESCENDING ]
|
|
44
|
+
]
|
|
45
|
+
)
|
|
46
|
+
index :file_fingerprint
|
|
47
|
+
index :_keywords
|
|
48
|
+
|
|
49
|
+
def self.make(*args)
|
|
50
|
+
Slices::Asset::Maker.run(*args)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def self.search_for(term = nil)
|
|
54
|
+
if term.present?
|
|
55
|
+
all.ordered_active.text_search term
|
|
56
|
+
else
|
|
57
|
+
all.ordered_active
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def self.ordered_active
|
|
62
|
+
where(destroyed_at: nil).desc(:created_at)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def as_json(options = nil)
|
|
66
|
+
{
|
|
67
|
+
id: id.to_s,
|
|
68
|
+
name: name,
|
|
69
|
+
file_file_name: file_file_name,
|
|
70
|
+
file_file_size: file_file_size,
|
|
71
|
+
file_content_type: file_content_type,
|
|
72
|
+
asset_url: admin_image_url,
|
|
73
|
+
original_url: file.url(:original),
|
|
74
|
+
errors: errors,
|
|
75
|
+
created_at: created_at,
|
|
76
|
+
tags: tags,
|
|
77
|
+
pages: page_cache
|
|
78
|
+
}
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Returns the url for the asset style. The style is created if it has not already
|
|
82
|
+
# been generated
|
|
83
|
+
#
|
|
84
|
+
# @param [Symbol] style The asset style is defined in Slice::Config.asset_styles
|
|
85
|
+
def url_for(style)
|
|
86
|
+
reprocess_for(style)
|
|
87
|
+
file.url(style)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Returns the dimensions the asset style. The style is created if it has not already
|
|
91
|
+
# been generated
|
|
92
|
+
#
|
|
93
|
+
# asset.dimensions_for(:main)
|
|
94
|
+
# "100x100"
|
|
95
|
+
#
|
|
96
|
+
# @param [Symbol] style The asset style is defined in Slice::Config.asset_styles
|
|
97
|
+
def dimensions_for(style)
|
|
98
|
+
reprocess_for(style)
|
|
99
|
+
file_dimensions[style.to_s]
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def reprocess_for(style)
|
|
103
|
+
if file.styles.has_key?(style) && ! file_dimensions.has_key?(style.to_s)
|
|
104
|
+
file.reprocess!(style)
|
|
105
|
+
save
|
|
106
|
+
end
|
|
107
|
+
rescue Errors::NotIdentifiedByImageMagickError, Errno::ENOENT
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def reprocess_for!(style)
|
|
111
|
+
file.clear(style)
|
|
112
|
+
file_dimensions.delete(style.to_s)
|
|
113
|
+
file.flush_deletes
|
|
114
|
+
reprocess_for(style)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def store_dimensions
|
|
118
|
+
file.queued_for_write.each do |style, adapter|
|
|
119
|
+
geometry = Paperclip::Geometry.from_file(adapter)
|
|
120
|
+
self.file_dimensions[style.to_s] = geometry.to_s
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def is_image?
|
|
125
|
+
file_content_type.present? && file_content_type.match(IMAGE_REGEX).present?
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def file_extension
|
|
129
|
+
if file_file_name.present?
|
|
130
|
+
file_file_name.split('.').last.downcase
|
|
131
|
+
else
|
|
132
|
+
''
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def admin_image_url
|
|
137
|
+
if !new_record? && is_image?
|
|
138
|
+
url_for(:admin)
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def soft_destroy!
|
|
143
|
+
update_attributes!(destroyed_at: Time.now)
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def soft_destroyed?
|
|
147
|
+
destroyed_at.present?
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def soft_restore!
|
|
151
|
+
update_attributes!(destroyed_at: nil)
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def name
|
|
155
|
+
@new_name || file_file_name
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def name=(new_name)
|
|
159
|
+
@new_name = new_name
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def update_page_cache
|
|
163
|
+
self.page_cache = pages.collect do |page|
|
|
164
|
+
{ id: page.id, name: page.name, path: page.path }
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def search_page_cache
|
|
169
|
+
page_cache.inject([]) do |memo, page|
|
|
170
|
+
memo << page['name']
|
|
171
|
+
memo << page['path']
|
|
172
|
+
end.join ' '
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
# Reset all stored file dimensions except for the origin and admin styles,
|
|
176
|
+
# this is used when a new version of an asset is uploaded
|
|
177
|
+
#
|
|
178
|
+
def reset_file_dimensions!
|
|
179
|
+
self.file_dimensions.keys.each do |key|
|
|
180
|
+
next if key == 'original' || key == 'admin'
|
|
181
|
+
self.file_dimensions.delete key
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
private
|
|
186
|
+
|
|
187
|
+
def rename_file
|
|
188
|
+
return if @new_name.blank?
|
|
189
|
+
|
|
190
|
+
new_file_name = sanitize(@new_name)
|
|
191
|
+
Slices::Asset::Rename.run(file, new_file_name)
|
|
192
|
+
file.instance_write(:file_name, new_file_name)
|
|
193
|
+
set_keywords
|
|
194
|
+
@new_name = nil
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
def sanitize(filename)
|
|
198
|
+
file.send(:cleanup_filename, filename).tap do |name|
|
|
199
|
+
if self.file && self.file.original_filename
|
|
200
|
+
extension = normalized_extension
|
|
201
|
+
name << extension unless name.ends_with?(extension)
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
def normalized_extension
|
|
207
|
+
extname = File.extname(file.original_filename).downcase
|
|
208
|
+
NORMALIZED_EXTENSIONS.fetch(extname, extname)
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
end
|