spontaneous 0.2.0.beta9 → 0.2.0.beta10

Sign up to get free protection for your applications and to get access to all the features.
Files changed (97) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +61 -0
  3. data/LICENSE +18 -17
  4. data/Rakefile +1 -1
  5. data/application/css/core.css.scss +1 -1
  6. data/application/css/dialogue.css.scss +8 -20
  7. data/application/js/preview.js +28 -7
  8. data/application/js/publish.js +15 -4
  9. data/application/js/top_bar.js +0 -16
  10. data/application/js/views/piece_view.js +1 -1
  11. data/lib/spontaneous/asset/environment.rb +16 -1
  12. data/lib/spontaneous/box.rb +68 -0
  13. data/lib/spontaneous/capistrano/deploy.rb +7 -4
  14. data/lib/spontaneous/capistrano/sync.rb +2 -2
  15. data/lib/spontaneous/cli/init.rb +70 -19
  16. data/lib/spontaneous/cli/init/db.rb +34 -55
  17. data/lib/spontaneous/cli/init/mysql.rb +5 -5
  18. data/lib/spontaneous/cli/init/postgresql.rb +8 -9
  19. data/lib/spontaneous/cli/init/sqlite.rb +1 -2
  20. data/lib/spontaneous/cli/migrate.rb +0 -1
  21. data/lib/spontaneous/cli/site.rb +4 -0
  22. data/lib/spontaneous/collections/entry_set.rb +11 -0
  23. data/lib/spontaneous/data_mapper/content_model.rb +2 -0
  24. data/lib/spontaneous/data_mapper/content_model/serialization.rb +2 -2
  25. data/lib/spontaneous/extensions/array.rb +12 -2
  26. data/lib/spontaneous/field/base.rb +10 -0
  27. data/lib/spontaneous/field/file.rb +32 -2
  28. data/lib/spontaneous/field/image.rb +24 -2
  29. data/lib/spontaneous/field/select.rb +8 -0
  30. data/lib/spontaneous/field/webvideo.rb +8 -0
  31. data/lib/spontaneous/generators/site/config/initializers/fields.rb +55 -0
  32. data/lib/spontaneous/json.rb +3 -2
  33. data/lib/spontaneous/logger.rb +2 -2
  34. data/lib/spontaneous/media/file.rb +3 -3
  35. data/lib/spontaneous/media/image/attributes.rb +72 -6
  36. data/lib/spontaneous/media/image/renderable.rb +53 -20
  37. data/lib/spontaneous/media/store.rb +3 -3
  38. data/lib/spontaneous/media/store/backend.rb +16 -0
  39. data/lib/spontaneous/media/store/cloud.rb +52 -12
  40. data/lib/spontaneous/media/store/local.rb +6 -3
  41. data/lib/spontaneous/model.rb +3 -0
  42. data/lib/spontaneous/model/core/entries.rb +34 -13
  43. data/lib/spontaneous/model/core/entry.rb +3 -1
  44. data/lib/spontaneous/model/page/controllers.rb +1 -2
  45. data/lib/spontaneous/model/page/paths.rb +18 -7
  46. data/lib/spontaneous/output/context.rb +0 -8
  47. data/lib/spontaneous/output/template/renderer.rb +2 -0
  48. data/lib/spontaneous/plugins/application/state.rb +0 -4
  49. data/lib/spontaneous/prototypes/field_prototype.rb +4 -0
  50. data/lib/spontaneous/publishing/immediate.rb +0 -5
  51. data/lib/spontaneous/publishing/progress.rb +2 -2
  52. data/lib/spontaneous/publishing/rerender.rb +1 -4
  53. data/lib/spontaneous/publishing/simultaneous.rb +19 -17
  54. data/lib/spontaneous/publishing/steps.rb +12 -3
  55. data/lib/spontaneous/rack.rb +2 -0
  56. data/lib/spontaneous/rack/asset_server.rb +5 -2
  57. data/lib/spontaneous/rack/back.rb +9 -1
  58. data/lib/spontaneous/rack/back/base.rb +1 -0
  59. data/lib/spontaneous/rack/back/changes.rb +5 -0
  60. data/lib/spontaneous/rack/back/preview.rb +4 -4
  61. data/lib/spontaneous/rack/back/private.rb +11 -0
  62. data/lib/spontaneous/rack/middleware/scope.rb +16 -4
  63. data/lib/spontaneous/rack/page_controller.rb +2 -2
  64. data/lib/spontaneous/rack/public.rb +52 -4
  65. data/lib/spontaneous/sequel.rb +10 -13
  66. data/lib/spontaneous/site.rb +28 -8
  67. data/lib/spontaneous/site/publishing.rb +1 -1
  68. data/lib/spontaneous/site/storage.rb +7 -4
  69. data/lib/spontaneous/tasks/environment.rake +3 -0
  70. data/lib/spontaneous/utils/database/postgres_dumper.rb +23 -2
  71. data/lib/spontaneous/version.rb +1 -1
  72. data/spontaneous.gemspec +7 -12
  73. data/test/fixtures/assets/public1/css/data.css.scss +1 -1
  74. data/test/functional/test_application.rb +15 -0
  75. data/test/functional/test_cli.rb +109 -3
  76. data/test/functional/test_front.rb +108 -10
  77. data/test/test_helper.rb +3 -3
  78. data/test/unit/fields/test_boolean_fields.rb +80 -0
  79. data/test/unit/fields/test_date_fields.rb +47 -0
  80. data/test/unit/fields/test_file_field.rb +210 -0
  81. data/test/unit/{test_images.rb → fields/test_image_fields.rb} +133 -15
  82. data/test/unit/fields/test_location_fields.rb +41 -0
  83. data/test/unit/fields/test_option_fields.rb +61 -0
  84. data/test/unit/fields/test_tag_list_fields.rb +45 -0
  85. data/test/unit/fields/test_text_fields.rb +124 -0
  86. data/test/unit/fields/test_web_video_fields.rb +198 -0
  87. data/test/unit/test_assets.rb +22 -22
  88. data/test/unit/test_boxes.rb +34 -13
  89. data/test/unit/test_changesets.rb +1 -0
  90. data/test/unit/test_extensions.rb +17 -0
  91. data/test/unit/test_fields.rb +20 -643
  92. data/test/unit/test_media.rb +9 -9
  93. data/test/unit/test_page.rb +47 -0
  94. data/test/unit/test_publishing_pipeline.rb +2 -2
  95. data/test/unit/test_serialisation.rb +37 -0
  96. data/test/unit/test_storage.rb +42 -3
  97. metadata +37 -17
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c521e3b9b35cfba180daf3fcc24894af352ca2eb
4
- data.tar.gz: 5f3fb38da829759b10dd33a2b4224a7bae9dfbdc
3
+ metadata.gz: 75f4d83b1920a348269f6d51f4060015a6f567fd
4
+ data.tar.gz: ef4257ad7ada51eed31e316efb5f712ee77be7ba
5
5
  SHA512:
6
- metadata.gz: c82fea8cd02838425f8f20eb2b21eab9ae3084414dcce39c1d72351e5292d7c9d09483729a9610e2e66cabedeb8bf7afb70570110b0582515ba87ae27e8b2d82
7
- data.tar.gz: 2f1a515a14f373b3535c864084529894d12878d8601b5c2f86e99267342816a30989acf5b0dd479369c0301ee56134b7996eb286304029d509715876f93166fe
6
+ metadata.gz: d8874945d474270fd5c73d376f65d2b31385a0d14c33d386bb55c3f61ae6495e28ffd70d64fe9aaa366f94f17347f4be3dd713a293a213ef0aa1849111c88395
7
+ data.tar.gz: 7c58d0ef193f9f709563eb6c1c34e33dacbb3823169d9b377714386d7aacd9b943f9d6c0ce59f007e6f75ae3b843e23469e1f00e8f6e814b908e67395f1ddc38
@@ -1,3 +1,64 @@
1
+ ## 0.2.0.beta10, released 2015-03-20
2
+
3
+ Is it wise to release a new version during a solar eclipse?
4
+
5
+ #### New Features
6
+
7
+ - Enabled previewing of private pages within the CMS. Previously this would
8
+ invoke some Inception-like preview-within-preview loop
9
+
10
+ - Boxes can now define a `path_origin` function that returns a `Content::Page` instance or a string which sets the root path for all pages added to it. This allows for your content & path hierarchies to differ (e.g. having child pages of the `/bands` page, e.g. `/bands/the-beatles` appear publicly at the root of the site i.e. `/the-beatles`).
11
+
12
+ - Page types can set a custom default slug root so that instead of being added as `page-YMD-HMS` they can be configured to default to `something-YMD-HMS` by overriding the value returned by `Content::Page#default_slug_root`.
13
+
14
+ - You can now get a list of content ids for a box using the `Box#ids` method
15
+
16
+ - The db connection can now be set using a `DATABASE_URL` env setting
17
+
18
+ - File and Image fields now generate their URLs dynamically. In the case of cloud hosted media this means you can change the way you address the media without having to regenerate the values e.g. in the case that you move from a direct S3 bucket to a CDN. You can also use any object that responds to `#call(path)` to generate the URLs which opens the door to splitting your media across multiple asset hosts.
19
+
20
+ - Pages can now declare 'wildcard' routes without namespaces which makes them more like standard controllers. E.g. declaring
21
+
22
+ controller do
23
+ get '/wibble/:id' do ... end
24
+ end
25
+
26
+ will allow a page mounted at `/womble` to accept requests to `/womble/wibble/23` etc.
27
+
28
+ - The publish dialogue now has a `Rerender` button that allows developers to re-render the site without publishing any content in the case of a template/asset change. Great if you need to push a fix but don't want to wait for or force the editors to publish something.
29
+
30
+ - You can now configure some default options for rendering image fields (and the default output for `${ image }` no longer includes the width & height by default (we’re responsive now, right?)).
31
+
32
+ #### Misc
33
+
34
+ - JSON de/en-coding is now handled by [Oj](https://github.com/ohler55/oj) not [Yajl](https://lloyd.github.io/yajl/) after some informal testing showed a ~50% speed improvement.
35
+ - The site initialization has been re-written to improve clarity & reliability
36
+ - Capistrano tasks can now be configured to use a custom binary for `rake` and `spot` commands
37
+ - Boxes now have a `#clear!` method to remove all their contents
38
+ - `Content#/` is an alias for `#get` by id
39
+ - The db timezone is set to UTC which results in some improved performance
40
+ - `Site#inspect` output is less mad
41
+ - The asset serving in preview mode has been moved before the authentication checks so we aren't running hundreds of db calls just to return some JS and images.
42
+ - The sprockets context has been given some Rails-compatible methods to help with using 3rd party sprockets plugins, e.g. `sprockets-less`.
43
+ - Select fields can now have a default value set
44
+ - Video fields now have an `aspect_ratio` method
45
+ - `spot site rerender` is an alias for `spot site render`
46
+
47
+ #### Fixes
48
+
49
+ - Front server no longer needs restarting to pick up a new revision in development mode (https://github.com/SpontaneousCMS/spontaneous/issues/47)
50
+ - Fixed entry deletion confirmation popup
51
+ - `Array#render` now calls `render_inline` rather than `render` so that rendering an array of pages doesn’t do mad things.
52
+ - The postgres database dumper/loader now works with custom hosts and ports and correctly authenticates itself when password authentication is in place
53
+ - File & image fields `#blank?` methods now work correctly (and are aliased as `#empty?`)
54
+ - Removed broken render context implementations of `#first?` and `#last?`
55
+ - `spot site init` ensures that the `log` & `tmp` directories exist
56
+ - Don't try to set encoding values when there's no content-type header returned
57
+ - `asset-data-uri` is now correctly wrapping the result in a `url()` declaration.
58
+ - Displaying exceptions in development preview now works
59
+ - Second level boxes now correctly show an 'add' toolbar below the last entry
60
+ - The 'no changes' message is now a more pleasing size & the spinner goes away
61
+
1
62
  ## 0.2.0.beta9, released 2014-10-30
2
63
 
3
64
  #### Fixes
data/LICENSE CHANGED
@@ -1,20 +1,21 @@
1
- Copyright (c) 2011-2013 Garry Hill <garry@magnetised.net>
1
+ The MIT License (MIT)
2
2
 
3
- Permission is hereby granted, free of charge, to any person obtaining
4
- a copy of this software and associated documentation files (the
5
- "Software"), to deal in the Software without restriction, including
6
- without limitation the rights to use, copy, modify, merge, publish,
7
- distribute, sublicense, and/or sell copies of the Software, and to
8
- permit persons to whom the Software is furnished to do so, subject to
9
- the following conditions:
3
+ Copyright (c) 2011-2014 Garry Richard Hill
10
4
 
11
- The above copyright notice and this permission notice shall be included
12
- in all copies or substantial portions of the Software.
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
13
11
 
14
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
18
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
19
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
20
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/Rakefile CHANGED
@@ -28,7 +28,7 @@ require 'rake/testtask'
28
28
  Rake::TestTask.new(:test) do |test|
29
29
  test.libs << 'test'
30
30
  test.ruby_opts << '-rubygems'
31
- test.pattern = 'test/{unit,functional,experimental}/test_*.rb'
31
+ test.pattern = 'test/{unit,functional,experimental}/**/test_*.rb'
32
32
  test.verbose = false
33
33
  end
34
34
 
@@ -1203,7 +1203,7 @@
1203
1203
  }
1204
1204
  &:last-child {
1205
1205
  .entry-spacer {
1206
- display: none;
1206
+ // display: none;
1207
1207
  }
1208
1208
  .content-type {
1209
1209
  bottom: $content-type-offset;
@@ -84,7 +84,7 @@ $dialogue-title-height: 32px;
84
84
  float: right;
85
85
  }
86
86
  .disabled, .disabled:hover {
87
- background-color: #333333 !important;
87
+ @include button-color(#333, #333);
88
88
  color: #6b6b6b !important;
89
89
  }
90
90
  }
@@ -142,6 +142,12 @@ $dialogue-title-height: 32px;
142
142
  #dialogue-wrap #dialogue-outer.publishing {
143
143
  left: 5%;
144
144
  right: 5%;
145
+ .dialogue-actions {
146
+ .button.rerender {
147
+ position: absolute;
148
+ left: 0;
149
+ }
150
+ }
145
151
  }
146
152
 
147
153
  #publishing-dialogue {
@@ -251,25 +257,7 @@ $dialogue-title-height: 32px;
251
257
  font-size: 24px;
252
258
  padding-top: 24px;
253
259
  color: #fff;
254
- // margin: $container-padding / 2;
255
- // margin-top: 0;
256
- // .publish-all {
257
- // @include button(#666);
258
- // @include interface;
259
- // width: 128px;
260
- // float: right;
261
- // text-align: left;
262
- // // background-color: #666666;
263
- // input {
264
- // margin: 1px 8px 0 8px;
265
- // padding: 0;
266
- // vertical-align: top;
267
- // position: relative;
268
- // }
269
- // }
270
- // .publish-all.checked {
271
- // background-color: $action;
272
- // }
260
+ max-height: 55px;
273
261
  }
274
262
  .change-set {
275
263
  cursor: pointer;
@@ -1,7 +1,7 @@
1
1
  // console.log('Loading Preview...');
2
2
 
3
3
  Spontaneous.Preview = (function($, S, $window) {
4
- var dom = S.Dom, goto_id = 0;
4
+ var dom = S.Dom, goto_id = 0, Ajax = S.Ajax;
5
5
  var click_param = function() {
6
6
  return '?__click='+(++goto_id);
7
7
  };
@@ -31,13 +31,16 @@ Spontaneous.Preview = (function($, S, $window) {
31
31
  if (currentLocation !== location) {
32
32
  location = currentLocation;
33
33
  S.Preview.set({
34
- 'title': iframe.contentWindow.document.title,
35
- 'path': iframe.contentWindow.location.pathname
34
+ 'title': icw.document.title,
35
+ 'path': icw.location.pathname
36
36
  });
37
37
  $(icw).bind('unload', function(e) {
38
38
  // trigger a progress indicator here
39
39
  });
40
- S.Location.load_path(iframe.contentWindow.location.pathname);
40
+ // don't load the page details into the top-bar if we're viewing a private page
41
+ if (preview.pathIsPublic(icw.location.pathname)) {
42
+ S.Location.load_path(icw.location.pathname);
43
+ }
41
44
  }
42
45
  };
43
46
 
@@ -48,18 +51,36 @@ Spontaneous.Preview = (function($, S, $window) {
48
51
  preview.previewPathMonitor = $window.setInterval(monitor, monitorInterval);
49
52
  }
50
53
  });
51
- this.goto_path(path);
54
+ this._goto_page(page, path)
55
+ },
56
+ pathIsPublic: function(path) {
57
+ return (path.indexOf([Ajax.namespace, 'private'].join('/')) === -1)
52
58
  },
53
59
  goto_path: function(path) {
54
60
  if (path) {
55
61
  // path += click_param();
56
- this.iframe[0].contentWindow.location.href = path;
62
+ this.load_url(path);
63
+ }
64
+ },
65
+ goto_private: function(page) {
66
+ if (page) {
67
+ this.load_url([Ajax.namespace, 'private', page.id].join('/'));
57
68
  }
58
69
  },
70
+ load_url: function(url) {
71
+ this.iframe[0].contentWindow.location.href = url;
72
+ },
59
73
  goto_page: function(page) {
60
74
  var current = this.get('path');
61
75
  if (!current || (page && (page.path !== current))) {
62
- this.goto_path(page.path);
76
+ this._goto_page(page, page.path)
77
+ }
78
+ },
79
+ _goto_page: function(page, path) {
80
+ if (page && page.private) {
81
+ this.goto_private(page);
82
+ } else {
83
+ this.goto_path(path);
63
84
  }
64
85
  },
65
86
  hide: function() {
@@ -1,7 +1,7 @@
1
1
  // console.log('Loading Publishing...')
2
2
 
3
3
  Spontaneous.Publishing = (function($, S) {
4
- var dom = S.Dom, Dialogue = Spontaneous.Dialogue;
4
+ var dom = S.Dom, Dialogue = Spontaneous.Dialogue, User = S.User;
5
5
 
6
6
  var PublishingDialogue = new JS.Class(Dialogue, {
7
7
  initialize: function(content) {
@@ -50,6 +50,9 @@ Spontaneous.Publishing = (function($, S) {
50
50
  },
51
51
  buttons: function() {
52
52
  btns = {};
53
+ if (User.is_developer()) {
54
+ btns['Rerender'] = this.rerender.bind(this);
55
+ }
53
56
  btns['Publish'] = this.publish.bind(this);
54
57
  return btns;
55
58
  },
@@ -64,6 +67,9 @@ Spontaneous.Publishing = (function($, S) {
64
67
  }
65
68
  },
66
69
 
70
+ rerender: function() {
71
+ Spontaneous.Ajax.post('/changes/rerender', {}, this.publish_requested.bind(this));
72
+ },
67
73
 
68
74
  publish_requested: function() {
69
75
  Spontaneous.TopBar.publishing_started();
@@ -78,7 +84,12 @@ Spontaneous.Publishing = (function($, S) {
78
84
  , publish_wrap = dom.div('#to-publish.change-list').css('opacity', 0)
79
85
  , must_publish_all = outstanding.first_publish || outstanding.must_publish_all
80
86
  , spinner = this.spinner
81
- , append_to;
87
+ , spinnerWrap = this.spinnerWrap
88
+ , append_to
89
+ , stop_spinner = function() {
90
+ spinner.stop();
91
+ spinnerWrap.remove();
92
+ };
82
93
  if (must_publish_all) {
83
94
  w.addClass('first-publish');
84
95
  }
@@ -86,6 +97,7 @@ Spontaneous.Publishing = (function($, S) {
86
97
  var summary = dom.p('.publish-up-to-date').text('The site is up to date');
87
98
  w.append(summary);
88
99
  self.disable_button('Publish');
100
+ stop_spinner();
89
101
  } else {
90
102
  var publish_all = dom.a('.button').text('Publish All').click(function() {
91
103
  self.set_publish_all(true);
@@ -114,8 +126,7 @@ Spontaneous.Publishing = (function($, S) {
114
126
  if (!must_publish_all) {
115
127
  publish_entries.append(dom.div('.instructions').text('Add pages to publish from the list on the left'));
116
128
  }
117
- spinner.stop();
118
- this.spinnerWrap.remove();
129
+ stop_spinner();
119
130
  w.empty();
120
131
  w.append(changed_wrap, publish_wrap);
121
132
  changed_wrap.add(publish_wrap).velocity({opacity: 1});
@@ -468,22 +468,6 @@ Spontaneous.TopBar = (function($, S) {
468
468
  var children_node = new ChildrenNode(location);
469
469
  self.location.append(children_node.element());
470
470
  self.children_node = children_node;
471
- self.updateModeCompatibility(location);
472
- },
473
- updateModeCompatibility: function(location) {
474
- var self = this;
475
- if (location.private) {
476
- self.previewModeDisabled = true;
477
- self.mode_switch.addClass('disabled');
478
- } else {
479
- self.previewModeDisabled = false;
480
- self.mode_switch.removeClass('disabled');
481
- }
482
- if (self.previewModeDisabled) {
483
- this.topBar.set_mode('edit');
484
- } else {
485
-
486
- }
487
471
  },
488
472
  page_loaded: function(page) {
489
473
  var self = this, children_node = self.children_node;
@@ -181,7 +181,7 @@ Spontaneous.Views.PieceView = (function($, S) {
181
181
  this.fields_preview.removeClass('hover');
182
182
  }
183
183
  },
184
- confirm_destroy: function() {
184
+ confirm_destroy: function(event) {
185
185
  if (this._dialogue && !this._dialogue.is_open) { this.close_destroy_dialogue(); }
186
186
  if (!this._dialogue) {
187
187
  this._dialogue = Spontaneous.Popover.open(event, new ConfirmDeletePopup(this));
@@ -38,12 +38,23 @@ module Spontaneous::Asset
38
38
 
39
39
  module SassFunctions
40
40
  def asset_data_uri(path)
41
- Sass::Script::String.new(sprockets_context.asset_data_uri(path.value))
41
+ uri = sprockets_context.asset_data_uri(path.value)
42
+ Sass::Script::String.new("url(#{uri})")
42
43
  end
43
44
  end
44
45
 
45
46
  ::Sass::Script::Functions.send :include, SassFunctions
46
47
 
48
+ module RailsCompatibilityShim
49
+ # actually more a sprockets-less compatibility shim
50
+ def compute_public_path(path, root = nil)
51
+ asset_path(path)
52
+ end
53
+
54
+ def asset_paths
55
+ self
56
+ end
57
+ end
47
58
 
48
59
  class Preview
49
60
  attr_reader :environment, :site
@@ -83,6 +94,8 @@ module Spontaneous::Asset
83
94
  Spontaneous::Asset::Environment.join_asset_path(make_absolute(asset.logical_path), query, fragment)
84
95
  end
85
96
 
97
+ include RailsCompatibilityShim
98
+
86
99
  def make_absolute(logical)
87
100
  "/" << self.class.asset_mount_point << "/" << logical
88
101
  end
@@ -294,6 +307,8 @@ module Spontaneous::Asset
294
307
  Spontaneous::Asset::Environment.join_asset_path(make_absolute(asset), query, fragment)
295
308
  end
296
309
 
310
+ include RailsCompatibilityShim
311
+
297
312
  def make_absolute(logical)
298
313
  "/" << self.class.asset_mount_point << "/" << logical
299
314
  end
@@ -223,6 +223,8 @@ module Spontaneous
223
223
  resolve_style(self)
224
224
  end
225
225
 
226
+ # Container represents the object one level up from us,
227
+ # which in this case is the parent Content instance.
226
228
  def container
227
229
  owner
228
230
  end
@@ -231,10 +233,62 @@ module Spontaneous
231
233
  owner
232
234
  end
233
235
 
236
+ # A pointer to the containing page. This may not be the same as the
237
+ # `owner` of the box in the case where the box is owned by a Piece.
234
238
  def page
235
239
  owner.page
236
240
  end
237
241
 
242
+ # A convenience method to return the root of the page tree
243
+ def root
244
+ page.root
245
+ end
246
+
247
+ def site
248
+ owner.site
249
+ end
250
+
251
+ # Used to determine the page to use to define the path of any
252
+ # contained pages.
253
+ #
254
+ # Overwrite this to set up custom paths for pages.
255
+ #
256
+ # It can either return a page instance (in which case
257
+ # child pages will be based on the #path of the returned
258
+ # instance) or a string (which will form the root of the
259
+ # generated paths:
260
+ #
261
+ # Page.box :sections
262
+ #
263
+ # Page.box :custom do
264
+ # def path_origin
265
+ # root
266
+ # end
267
+ # end
268
+ #
269
+ # home = Page.create # set up a new site homepage
270
+ # section = Page.create(slug: 'a-section')
271
+ # home.sections << section # add section page to the root
272
+ # section.custom.path_origin.path #=> "/"
273
+ #
274
+ # child = Page.create(slug: 'child')
275
+ # section.custom << child
276
+ # child.path #=> "/child" # this would normally be "/a-section/child"
277
+ #
278
+ def path_origin
279
+ page
280
+ end
281
+
282
+ # This is used by new pages to generate the path component of the container.
283
+ def path!
284
+ case (origin = path_origin)
285
+ when @owner.content_model
286
+ origin.path!
287
+ else
288
+ origin.to_s
289
+ end
290
+ end
291
+
238
292
  alias_method :to_page, :page
239
293
 
240
294
  def depth
@@ -275,6 +329,14 @@ module Spontaneous
275
329
  @modified
276
330
  end
277
331
 
332
+ # Returns a list of the ids of the content within the box
333
+ #
334
+ # This is designed to be fast by not requiring the actual
335
+ # loading of the box contents.
336
+ def ids
337
+ owner.contents.ids(self)
338
+ end
339
+
278
340
  def contents
279
341
  owner.contents.for_box(self)
280
342
  end
@@ -297,6 +359,12 @@ module Spontaneous
297
359
  end
298
360
  end
299
361
 
362
+ def clear!
363
+ contents.dup.each do |entry|
364
+ entry.destroy
365
+ end
366
+ end
367
+
300
368
  def empty?
301
369
  contents.count == 0
302
370
  end