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,52 @@
|
|
|
1
|
+
module Slices
|
|
2
|
+
class Renderer
|
|
3
|
+
include ActiveSupport::Benchmarkable
|
|
4
|
+
|
|
5
|
+
def initialize(options = {})
|
|
6
|
+
options.assert_valid_keys(:controller, :current_page, :slices, :params)
|
|
7
|
+
|
|
8
|
+
@controller = options[:controller] || SlicesController.new
|
|
9
|
+
@current_page = options[:current_page]
|
|
10
|
+
@slices = options[:slices]
|
|
11
|
+
@params = options[:params] || {}
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def render_slices(slices = @slices)
|
|
15
|
+
[].tap do |rendered|
|
|
16
|
+
slices.each do |slice|
|
|
17
|
+
benchmark "Prepared #{slice.reference}" do
|
|
18
|
+
slice.setup(setup_options)
|
|
19
|
+
slice.prepare(@params)
|
|
20
|
+
end
|
|
21
|
+
rendered << slice.render
|
|
22
|
+
end
|
|
23
|
+
end.join("\n").html_safe
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def render_container(container, slices = @slices)
|
|
27
|
+
render_slices slices.where(container: container)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def render_to_string(*args)
|
|
31
|
+
@controller.render_to_string(*args)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def fragment_exist?(cache_key)
|
|
35
|
+
@controller.fragment_exist?(cache_key)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
|
|
40
|
+
def setup_options
|
|
41
|
+
{
|
|
42
|
+
renderer: self,
|
|
43
|
+
current_page: @current_page
|
|
44
|
+
}
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def logger
|
|
48
|
+
Rails.logger
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
module Slices
|
|
2
|
+
class Engine < Rails::Engine; end
|
|
3
|
+
|
|
4
|
+
def self.autoload_slices(app, root)
|
|
5
|
+
slice_dirs = Dir.glob(File.expand_path('app/slices/*', root))
|
|
6
|
+
app.config.autoload_paths.push(*slice_dirs.select { |f| File.directory?(f) })
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
# We need to add the app's own app/slices/* directories to the load
|
|
10
|
+
# path so that slices created within the application are automatically
|
|
11
|
+
# detected. This means that we need a Railtie as well as an engine, as
|
|
12
|
+
# an engine's initializer can't add autoload_paths that aren't scoped
|
|
13
|
+
# within the engine itself (for some stupid reason).
|
|
14
|
+
class Railtie < Rails::Railtie
|
|
15
|
+
initializer :autoload_slices, before: :set_autoload_paths do |app|
|
|
16
|
+
Slices.autoload_slices(app, Slices.gem_path) if Slices.test_environment?
|
|
17
|
+
Slices.autoload_slices(app, Rails.root)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
initializer :active_mongoid_observers do
|
|
21
|
+
config.mongoid.observers.concat [:page_observer, :asset_observer]
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
initializer :load_config_initializers, after: :config_initializers do |app|
|
|
25
|
+
Dir.glob('config/initializers/*.rb').each do |initializer|
|
|
26
|
+
load(initializer)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
initializer :slices_will_paginate_active_view, after: 'will_paginate.action_view' do
|
|
31
|
+
load Slices.gem_path + '/lib/set_link_renderer.rb'
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
initializer :slices_precompile_hook do |app|
|
|
35
|
+
app.config.assets.precompile += %w(slices/slices.css slices/slices.js)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
config.after_initialize do
|
|
39
|
+
Slices.load_slice_classes_into_object_space(Rails.root)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
rake_tasks do
|
|
43
|
+
root = Slices.gem_path + '/lib/'
|
|
44
|
+
|
|
45
|
+
Dir[root + 'slices/tasks/*.rake'].each do |taskfile|
|
|
46
|
+
load taskfile.sub root, ''
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
module Slices #:nodoc:
|
|
2
|
+
module SplitDateTimeField #:nodoc:
|
|
3
|
+
extend ActiveSupport::Concern #:nodoc:
|
|
4
|
+
|
|
5
|
+
module ClassMethods #:nodoc:
|
|
6
|
+
|
|
7
|
+
def split_date_time_field(name)
|
|
8
|
+
raise Error, 'Split date time is depreciated use dateField instead'
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
module Slices
|
|
2
|
+
module Tasks
|
|
3
|
+
|
|
4
|
+
def self.remove_all_unused_styles
|
|
5
|
+
Asset.all.each do |asset|
|
|
6
|
+
remove_unused_styles(asset)
|
|
7
|
+
end
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def self.remove_unused_styles(asset)
|
|
11
|
+
asset.remove_attribute :dimensions
|
|
12
|
+
|
|
13
|
+
used_styles = asset.file_dimensions.symbolize_keys.keys << :original
|
|
14
|
+
styles_to_clear = asset.file.styles.keys - used_styles
|
|
15
|
+
asset.file.clear(*styles_to_clear)
|
|
16
|
+
asset.save
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
namespace :slices do
|
|
22
|
+
namespace :assets do
|
|
23
|
+
|
|
24
|
+
desc "Mark assets for reprocessing"
|
|
25
|
+
task reprocess: :environment do
|
|
26
|
+
Asset.update_all(file_dimensions: {})
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
desc "Remove unused asset styles"
|
|
30
|
+
task remove_unused_styles: :environment do
|
|
31
|
+
Slices::Tasks.remove_all_unused_styles
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
module Slices
|
|
2
|
+
module Tasks
|
|
3
|
+
def self.create_indexes
|
|
4
|
+
[
|
|
5
|
+
Slices.gem_path + '/app/models/**/*.rb',
|
|
6
|
+
Rails.root.join('app/slices/**/*.rb')
|
|
7
|
+
].each do |pattern|
|
|
8
|
+
Dir.glob(pattern).each do |file|
|
|
9
|
+
model = determine_model(file)
|
|
10
|
+
if model
|
|
11
|
+
model.create_indexes
|
|
12
|
+
Logger.new($stdout).info("Generated indexes for #{model}")
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def self.determine_model(file)
|
|
19
|
+
segments = file[0 .. -4].split('/')
|
|
20
|
+
start = if segments.include?('slices')
|
|
21
|
+
segments.rindex('slices') + 2
|
|
22
|
+
else
|
|
23
|
+
segments.rindex('models') + 1
|
|
24
|
+
end
|
|
25
|
+
model_path = segments[start .. -1]
|
|
26
|
+
klass = model_path.map { |path| path.camelize }.join('::').constantize
|
|
27
|
+
if klass.ancestors.include?(::Mongoid::Document) && !klass.embedded
|
|
28
|
+
return klass
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
namespace :slices do
|
|
35
|
+
namespace :db do
|
|
36
|
+
desc "Create indexes for slices models"
|
|
37
|
+
task create_indexes: "db:mongoid:create_indexes" do
|
|
38
|
+
Slices::Tasks.create_indexes
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
namespace :migrate do
|
|
43
|
+
desc "Rename description field to meta_description"
|
|
44
|
+
task meta_description: :environment do
|
|
45
|
+
Page.collection.update({},
|
|
46
|
+
{'$rename' => {'description' => 'meta_description'}},
|
|
47
|
+
multi: true)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
require 'highline/import'
|
|
2
|
+
|
|
3
|
+
module Slices
|
|
4
|
+
module Tasks
|
|
5
|
+
class Exists < RuntimeError; end
|
|
6
|
+
|
|
7
|
+
def self.with_message(text)
|
|
8
|
+
print "#{text} ... "
|
|
9
|
+
yield
|
|
10
|
+
puts "ok"
|
|
11
|
+
rescue Exists
|
|
12
|
+
puts "(exists)"
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def self.make_virtual(role, name, status)
|
|
16
|
+
with_message("Creating #{status} page") do
|
|
17
|
+
if Page.where(role: role).count > 0
|
|
18
|
+
raise Exists
|
|
19
|
+
end
|
|
20
|
+
Page.make(role: role, name: name, active: true)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def self.make_home
|
|
25
|
+
with_message("Creating home page") do
|
|
26
|
+
raise Exists if Page.where(path: '/').any?
|
|
27
|
+
Page.make(name: 'Home',
|
|
28
|
+
permalink: '',
|
|
29
|
+
show_in_nav: true,
|
|
30
|
+
active: true)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def self.make_errors
|
|
35
|
+
make_virtual('not_found', 'Page not found', 404)
|
|
36
|
+
make_virtual('error', 'Whoops', 500)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def self.make_admin
|
|
40
|
+
if Admin.exists?
|
|
41
|
+
say "Creating admin user ... (exists)"
|
|
42
|
+
return
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
say("Creating admin user ...")
|
|
46
|
+
|
|
47
|
+
admin = Admin.create!(
|
|
48
|
+
name: ask('Enter a name: '),
|
|
49
|
+
email: ask('Enter an email address: '),
|
|
50
|
+
password: ask('Enter a password: ') { |q| q.echo = false }
|
|
51
|
+
)
|
|
52
|
+
admin.super_user = true
|
|
53
|
+
admin.save!
|
|
54
|
+
|
|
55
|
+
say("... ok")
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def self.make_all
|
|
59
|
+
make_home
|
|
60
|
+
make_errors
|
|
61
|
+
make_admin
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
namespace :slices do
|
|
67
|
+
namespace :seed do
|
|
68
|
+
task :foo do
|
|
69
|
+
puts "Bar"
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
desc "Create the home page"
|
|
73
|
+
task :home => :environment do
|
|
74
|
+
Slices::Tasks.make_home
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
desc "Create 404 and 500 pages"
|
|
78
|
+
task :errors => :home do
|
|
79
|
+
Slices::Tasks.make_errors
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
desc "With Associates admin user"
|
|
83
|
+
task :admin => :environment do
|
|
84
|
+
Slices::Tasks.make_admin
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
desc "Seed home, errors and admin user"
|
|
89
|
+
task :seed => :environment do
|
|
90
|
+
Slices::Tasks.make_all
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
require 'open-uri'
|
|
2
|
+
|
|
3
|
+
module Slices
|
|
4
|
+
module Tasks
|
|
5
|
+
|
|
6
|
+
class PageHtml
|
|
7
|
+
attr_accessor :body
|
|
8
|
+
|
|
9
|
+
def initialize(host, path)
|
|
10
|
+
document = open('http://' + host + path)
|
|
11
|
+
self.body = document.present? ? document.read : nil
|
|
12
|
+
self.body.gsub!(/\?[0-9]{10}/, '') # remove cache busting
|
|
13
|
+
rescue OpenURI::HTTPError
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def ==(other)
|
|
17
|
+
body == other.body
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def temp_path
|
|
21
|
+
write_to_tempfile unless @tempfile
|
|
22
|
+
@tempfile.path
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
|
|
27
|
+
def write_to_tempfile
|
|
28
|
+
@tempfile = Tempfile.new('html').tap do |file|
|
|
29
|
+
file << body
|
|
30
|
+
file.flush
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def self.validate(local, remote)
|
|
37
|
+
hosts = [local, remote]
|
|
38
|
+
Page.all.each do |page|
|
|
39
|
+
puts page.path
|
|
40
|
+
a, b = hosts.map do |host|
|
|
41
|
+
PageHtml.new(host, page.path)
|
|
42
|
+
end
|
|
43
|
+
if a != b
|
|
44
|
+
puts `diff #{a.temp_path} #{b.temp_path}`
|
|
45
|
+
puts ''
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
namespace :slices do
|
|
54
|
+
|
|
55
|
+
desc "Validate localhost is the same is live"
|
|
56
|
+
task valdiate: :environment do
|
|
57
|
+
remote = ENV['HOST']
|
|
58
|
+
local = ENV.include?('LOCAL') ? ENV['LOCAL'] : 'localhost:3000'
|
|
59
|
+
Slices::Tasks.validate(remote, local)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
data/lib/slices/tree.rb
ADDED
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
module Slices
|
|
2
|
+
|
|
3
|
+
# Containers the tree behaviour.
|
|
4
|
+
module Tree
|
|
5
|
+
extend ActiveSupport::Concern
|
|
6
|
+
|
|
7
|
+
# @!attribute [rw] path
|
|
8
|
+
# The path for the page, this is a unique identifier and directly maps to
|
|
9
|
+
# the page's url.
|
|
10
|
+
#
|
|
11
|
+
# @return [String]
|
|
12
|
+
|
|
13
|
+
# @!attribute [rw] external_url
|
|
14
|
+
# An optional url override for the page, if this is set then this page will
|
|
15
|
+
# link to the url in any navigation block. Usefull when linking to a
|
|
16
|
+
# different website.
|
|
17
|
+
#
|
|
18
|
+
# @return [String]
|
|
19
|
+
|
|
20
|
+
# @!attribute [rw] position
|
|
21
|
+
# The page's position amongst it's siblings
|
|
22
|
+
#
|
|
23
|
+
# @return [Integer]
|
|
24
|
+
|
|
25
|
+
# @!attribute [rw] show_in_nav
|
|
26
|
+
# If the +page+ should be shown in any navigation block.
|
|
27
|
+
#
|
|
28
|
+
# @return [Boolean]
|
|
29
|
+
|
|
30
|
+
# @!method minimal
|
|
31
|
+
# A scope with the minmial attributes needed to render navigaiton
|
|
32
|
+
#
|
|
33
|
+
# @!scope class
|
|
34
|
+
# @return [Mongoid::Criteria]
|
|
35
|
+
#
|
|
36
|
+
included do
|
|
37
|
+
field :path
|
|
38
|
+
field :external_url, type: String, default: ''
|
|
39
|
+
field :position, type: Integer
|
|
40
|
+
field :show_in_nav, type: Boolean, default: false
|
|
41
|
+
|
|
42
|
+
scope :minimal, only: %w(page_id path external_url name has_content)
|
|
43
|
+
index :path
|
|
44
|
+
|
|
45
|
+
belongs_to :page
|
|
46
|
+
|
|
47
|
+
attr_accessor :parent_id
|
|
48
|
+
|
|
49
|
+
before_create :ensure_path_unique
|
|
50
|
+
before_save :ensure_path_unique
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
module ClassMethods
|
|
54
|
+
|
|
55
|
+
# Get the home page.
|
|
56
|
+
#
|
|
57
|
+
# @return [Page]
|
|
58
|
+
#
|
|
59
|
+
def home
|
|
60
|
+
find_by_path('/')
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Finds a page by it's path, a {Page::NotFound} is raised if the page
|
|
64
|
+
# can't be found.
|
|
65
|
+
#
|
|
66
|
+
# @return [Page]
|
|
67
|
+
#
|
|
68
|
+
def find_by_path(path)
|
|
69
|
+
first(conditions: { path: path }) || (raise Page::NotFound.new(path))
|
|
70
|
+
end
|
|
71
|
+
alias :f :find_by_path
|
|
72
|
+
|
|
73
|
+
# Generate a +path+ from attributes.
|
|
74
|
+
#
|
|
75
|
+
# @param [Hash] attributes
|
|
76
|
+
# @option attributes [String] :parent_path Path to parent page
|
|
77
|
+
# @option attributes [Page] :parent Parent page
|
|
78
|
+
# @option attributes [String] :name Page name
|
|
79
|
+
# @option attributes [String] :permalink Permalink
|
|
80
|
+
# @param [Page] parent
|
|
81
|
+
# @return [String]
|
|
82
|
+
#
|
|
83
|
+
def path_from_attributes(attributes, parent = nil)
|
|
84
|
+
parent_path = if parent
|
|
85
|
+
parent.path
|
|
86
|
+
else
|
|
87
|
+
attributes[:parent_path] || attributes[:parent].try(:path).to_s
|
|
88
|
+
end
|
|
89
|
+
permalink = attributes.delete(:permalink) || attributes[:name]
|
|
90
|
+
File.join(parent_path, permalink.to_url)
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Get the parent page.
|
|
95
|
+
#
|
|
96
|
+
# @return [Page]
|
|
97
|
+
def parent
|
|
98
|
+
page
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Set the parent.
|
|
102
|
+
#
|
|
103
|
+
# @param [Page] page The new parent
|
|
104
|
+
# @return [Page]
|
|
105
|
+
#
|
|
106
|
+
def parent=(page)
|
|
107
|
+
self.page = page
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# Is this page the home page
|
|
111
|
+
#
|
|
112
|
+
def home?
|
|
113
|
+
path == '/'
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# A list of pages descended from this page ordered by +position+
|
|
117
|
+
#
|
|
118
|
+
# @return [Mongoid::Criteria]
|
|
119
|
+
#
|
|
120
|
+
def children
|
|
121
|
+
Page.where(page_id: self.id).ascending(:position)
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# A list of the page's ancestors
|
|
125
|
+
#
|
|
126
|
+
# @return [Array<Page>]
|
|
127
|
+
#
|
|
128
|
+
def ancestors
|
|
129
|
+
ancestors = []
|
|
130
|
+
parent_page = parent
|
|
131
|
+
while parent_page
|
|
132
|
+
ancestors << parent_page
|
|
133
|
+
parent_page = parent_page.parent
|
|
134
|
+
end
|
|
135
|
+
ancestors
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
# Is the current page a descendant of +page+
|
|
139
|
+
#
|
|
140
|
+
# @param [Page] page
|
|
141
|
+
#
|
|
142
|
+
def descended_from?(page)
|
|
143
|
+
ancestors.include?(page)
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
# The navigaiton path for a page, the navigation differers from +path+
|
|
147
|
+
# in a few important ways. If #external_url is set then it is returned. If
|
|
148
|
+
# the page has no content and children then the path of it's first child is
|
|
149
|
+
# returned
|
|
150
|
+
#
|
|
151
|
+
# @return [String]
|
|
152
|
+
#
|
|
153
|
+
def navigation_path
|
|
154
|
+
if external_url?
|
|
155
|
+
external_url
|
|
156
|
+
elsif home? || has_content?
|
|
157
|
+
path
|
|
158
|
+
else
|
|
159
|
+
navigable_children.first ? navigable_children.first.path : path
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
# A list of child pages which have both +active+ and +show_in_nav+ set to
|
|
164
|
+
# true. Only the attributes needed to render #navigation are returned,
|
|
165
|
+
# these are: +page_id+, +path+, +external_url+, +name+ and +has_content+.
|
|
166
|
+
#
|
|
167
|
+
# @return [Mongoid::Criteria]
|
|
168
|
+
#
|
|
169
|
+
def navigable_children
|
|
170
|
+
page_children.minimal.where(active: true, show_in_nav: true)
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
# The parent pages's navigable children, including this page
|
|
174
|
+
#
|
|
175
|
+
# @return [Mongoid::Criteria]
|
|
176
|
+
#
|
|
177
|
+
def peers
|
|
178
|
+
parent.nil? ? [] : parent.navigable_children
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
# An array of the parent page's children excluding this page
|
|
182
|
+
#
|
|
183
|
+
# @return [Mongoid::Criteria]
|
|
184
|
+
#
|
|
185
|
+
def siblings
|
|
186
|
+
parent.nil? ? [] : parent.children.where(:_id.nin => [id])
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
# An array of the parent page's children excluding this page, ordered by
|
|
190
|
+
# position
|
|
191
|
+
#
|
|
192
|
+
# @return [Mongoid::Criteria]
|
|
193
|
+
#
|
|
194
|
+
def siblings_by_position
|
|
195
|
+
siblings.ascending(:position)
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
# Is this page the first child of its parent
|
|
199
|
+
#
|
|
200
|
+
def first_sibling?
|
|
201
|
+
parent.nil? ? false : peers.first == self
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
# Is this page the last child of its parent
|
|
205
|
+
#
|
|
206
|
+
def last_sibling?
|
|
207
|
+
parent.nil? ? false : peers.last == self
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
# The previous page(of the parent's active children), or nil
|
|
211
|
+
# if this is the first page.
|
|
212
|
+
#
|
|
213
|
+
# @return [Page]
|
|
214
|
+
#
|
|
215
|
+
def previous_sibling
|
|
216
|
+
index_in_peers < 1 ? nil : peers[index_in_peers - 1]
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
# The next page(of the parent's active children), or nil
|
|
220
|
+
# if this is the last page.
|
|
221
|
+
#
|
|
222
|
+
# @return [Page]
|
|
223
|
+
#
|
|
224
|
+
def next_sibling
|
|
225
|
+
peers[index_in_peers + 1]
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
# A list of children which are either {Page} or {SetPage}
|
|
229
|
+
#
|
|
230
|
+
# @return [Mongoid::Criteria]
|
|
231
|
+
#
|
|
232
|
+
def page_children
|
|
233
|
+
children.where(:_type.in => [nil, 'Page', 'SetPage'])
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
# A list of children which are neither {Page} or {SetPage}
|
|
237
|
+
#
|
|
238
|
+
# @return [Mongoid::Criteria]
|
|
239
|
+
#
|
|
240
|
+
def entry_children
|
|
241
|
+
children.where(:_type.nin => ['Page', 'SetPage'])
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
# Recusivly decend from this page and update each descendants path,
|
|
245
|
+
# useful when changing permalinks.
|
|
246
|
+
#
|
|
247
|
+
# @return [Mongoid::Criteria]
|
|
248
|
+
#
|
|
249
|
+
def update_path_for_children
|
|
250
|
+
children.each do |child|
|
|
251
|
+
child.path = child.generate_path
|
|
252
|
+
child.save!
|
|
253
|
+
child.update_path_for_children
|
|
254
|
+
end
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
# Generate a new +path+ for the page, using it's parent's path and the
|
|
258
|
+
# permalink.
|
|
259
|
+
#
|
|
260
|
+
# @return [String]
|
|
261
|
+
#
|
|
262
|
+
def generate_path
|
|
263
|
+
unless home?
|
|
264
|
+
File.join(parent.path, permalink)
|
|
265
|
+
else
|
|
266
|
+
'/'
|
|
267
|
+
end
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
# The permalink for the page
|
|
271
|
+
#
|
|
272
|
+
# @return [String]
|
|
273
|
+
#
|
|
274
|
+
def permalink
|
|
275
|
+
if path
|
|
276
|
+
File.basename(path)
|
|
277
|
+
else
|
|
278
|
+
name.to_url
|
|
279
|
+
end
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
private
|
|
283
|
+
|
|
284
|
+
# Make sure that a page's path is unique amoungst it's siblings, if there is
|
|
285
|
+
# a dupliacation then +-1+ is appended
|
|
286
|
+
#
|
|
287
|
+
# @return [void]
|
|
288
|
+
#
|
|
289
|
+
def ensure_path_unique
|
|
290
|
+
if parent.present? && path_changed?
|
|
291
|
+
matching_paths = siblings.where(path: /#{path}(-\d+)?$/)
|
|
292
|
+
if matching_paths.any?
|
|
293
|
+
self.path << "-#{matching_paths.length}"
|
|
294
|
+
end
|
|
295
|
+
end
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
# The page's position amoung it's peers
|
|
299
|
+
#
|
|
300
|
+
# @return [Integer]
|
|
301
|
+
#
|
|
302
|
+
def index_in_peers
|
|
303
|
+
parent.nil? ? 0 : peers.only(:id).entries.index(self)
|
|
304
|
+
end
|
|
305
|
+
end
|
|
306
|
+
end
|