spontaneous 0.2.0.beta4 → 0.2.0.beta5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (205) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +39 -0
  3. data/Gemfile +11 -6
  4. data/Readme.markdown +136 -69
  5. data/application/css/core.css.scss +27 -7
  6. data/application/css/editing.css.scss +4 -26
  7. data/application/css/schema_error.css.scss +22 -0
  8. data/application/js/content.js +11 -3
  9. data/application/js/edit_panel.js +1 -4
  10. data/application/js/field/file.js +17 -0
  11. data/application/js/field/image.js +30 -21
  12. data/application/js/field/string.js +4 -1
  13. data/application/js/field_preview.js +21 -16
  14. data/application/js/publish.js +6 -6
  15. data/application/js/types.js +5 -13
  16. data/application/js/views.js +2 -2
  17. data/application/js/views/box_view.js +3 -2
  18. data/application/js/views/page_piece_view.js +1 -1
  19. data/application/js/views/piece_view.js +1 -1
  20. data/application/views/schema_modification_error.html.erb +13 -3
  21. data/db/migrations/20131104101935_site_must_publish_all.rb +14 -0
  22. data/lib/spontaneous.rb +0 -1
  23. data/lib/spontaneous/box_style.rb +15 -9
  24. data/lib/spontaneous/capistrano/deploy.rb +13 -1
  25. data/lib/spontaneous/change.rb +11 -13
  26. data/lib/spontaneous/cli.rb +5 -2
  27. data/lib/spontaneous/cli/assets.rb +7 -1
  28. data/lib/spontaneous/cli/console.rb +7 -1
  29. data/lib/spontaneous/cli/content.rb +35 -0
  30. data/lib/spontaneous/cli/fields.rb +3 -2
  31. data/lib/spontaneous/cli/generate.rb +5 -2
  32. data/lib/spontaneous/cli/server.rb +12 -8
  33. data/lib/spontaneous/cli/site.rb +12 -12
  34. data/lib/spontaneous/cli/user.rb +28 -14
  35. data/lib/spontaneous/collections/box_set.rb +4 -4
  36. data/lib/spontaneous/collections/field_set.rb +4 -4
  37. data/lib/spontaneous/collections/prototype_set.rb +12 -4
  38. data/lib/spontaneous/data_mapper.rb +11 -7
  39. data/lib/spontaneous/data_mapper/content_model.rb +8 -0
  40. data/lib/spontaneous/data_mapper/content_model/associations.rb +1 -1
  41. data/lib/spontaneous/data_mapper/dataset.rb +14 -2
  42. data/lib/spontaneous/data_mapper/scope.rb +33 -13
  43. data/lib/spontaneous/facet.rb +4 -0
  44. data/lib/spontaneous/field.rb +12 -12
  45. data/lib/spontaneous/field/base.rb +27 -22
  46. data/lib/spontaneous/field/boolean.rb +4 -4
  47. data/lib/spontaneous/field/date.rb +2 -2
  48. data/lib/spontaneous/field/file.rb +24 -18
  49. data/lib/spontaneous/field/html.rb +1 -1
  50. data/lib/spontaneous/field/image.rb +6 -19
  51. data/lib/spontaneous/field/location.rb +1 -1
  52. data/lib/spontaneous/field/long_string.rb +3 -3
  53. data/lib/spontaneous/field/markdown.rb +3 -3
  54. data/lib/spontaneous/field/select.rb +2 -2
  55. data/lib/spontaneous/field/string.rb +2 -2
  56. data/lib/spontaneous/field/tags.rb +2 -2
  57. data/lib/spontaneous/field/update.rb +21 -20
  58. data/lib/spontaneous/field/webvideo.rb +6 -6
  59. data/lib/spontaneous/field/webvideo/fallback.rb +2 -2
  60. data/lib/spontaneous/field/webvideo/vimeo.rb +7 -7
  61. data/lib/spontaneous/generators/site.rb +2 -2
  62. data/lib/spontaneous/generators/site/Gemfile.tt +5 -1
  63. data/lib/spontaneous/layout.rb +2 -2
  64. data/lib/spontaneous/media.rb +1 -0
  65. data/lib/spontaneous/media/file.rb +6 -5
  66. data/lib/spontaneous/media/image/attributes.rb +4 -0
  67. data/lib/spontaneous/media/image/renderable.rb +4 -4
  68. data/lib/spontaneous/media/store.rb +22 -0
  69. data/lib/spontaneous/{storage → media/store}/backend.rb +1 -1
  70. data/lib/spontaneous/{storage → media/store}/cloud.rb +1 -1
  71. data/lib/spontaneous/{storage → media/store}/local.rb +1 -1
  72. data/lib/spontaneous/media/temp_file.rb +1 -1
  73. data/lib/spontaneous/model.rb +10 -7
  74. data/lib/spontaneous/model/action.rb +7 -0
  75. data/lib/spontaneous/model/action/clean.rb +87 -0
  76. data/lib/spontaneous/model/box/allowed_types.rb +15 -1
  77. data/lib/spontaneous/model/core.rb +10 -0
  78. data/lib/spontaneous/model/core/aliases.rb +1 -1
  79. data/lib/spontaneous/model/core/content_groups.rb +1 -1
  80. data/lib/spontaneous/model/core/fields.rb +1 -1
  81. data/lib/spontaneous/model/core/modifications.rb +2 -2
  82. data/lib/spontaneous/model/core/page_search.rb +4 -0
  83. data/lib/spontaneous/model/core/publishing.rb +4 -17
  84. data/lib/spontaneous/model/core/render.rb +4 -4
  85. data/lib/spontaneous/model/core/styles.rb +2 -2
  86. data/lib/spontaneous/model/core/visibility.rb +6 -2
  87. data/lib/spontaneous/model/page.rb +6 -2
  88. data/lib/spontaneous/model/page/controllers.rb +55 -17
  89. data/lib/spontaneous/model/page/formats.rb +12 -7
  90. data/lib/spontaneous/model/page/layouts.rb +2 -2
  91. data/lib/spontaneous/model/page/locks.rb +4 -1
  92. data/lib/spontaneous/model/page/page_tree.rb +40 -6
  93. data/lib/spontaneous/output.rb +14 -52
  94. data/lib/spontaneous/output/context.rb +11 -39
  95. data/lib/spontaneous/output/context/navigation.rb +31 -0
  96. data/lib/spontaneous/output/format.rb +15 -19
  97. data/lib/spontaneous/output/renderable.rb +99 -0
  98. data/lib/spontaneous/output/store.rb +24 -0
  99. data/lib/spontaneous/output/store/backend.rb +52 -0
  100. data/lib/spontaneous/output/store/file.rb +77 -0
  101. data/lib/spontaneous/output/store/moneta.rb +117 -0
  102. data/lib/spontaneous/output/store/revision.rb +34 -0
  103. data/lib/spontaneous/output/store/store.rb +15 -0
  104. data/lib/spontaneous/output/store/transaction.rb +44 -0
  105. data/lib/spontaneous/output/template/engine.rb +17 -7
  106. data/lib/spontaneous/output/template/renderer.rb +66 -40
  107. data/lib/spontaneous/page_lock.rb +5 -7
  108. data/lib/spontaneous/page_piece.rb +2 -2
  109. data/lib/spontaneous/permissions/user.rb +14 -7
  110. data/lib/spontaneous/plugins/application/features.rb +8 -4
  111. data/lib/spontaneous/plugins/application/state.rb +12 -6
  112. data/lib/spontaneous/prototypes/box_prototype.rb +9 -10
  113. data/lib/spontaneous/prototypes/field_prototype.rb +66 -15
  114. data/lib/spontaneous/publishing/immediate.rb +30 -26
  115. data/lib/spontaneous/rack.rb +12 -7
  116. data/lib/spontaneous/rack/back.rb +43 -37
  117. data/lib/spontaneous/rack/back/base.rb +4 -4
  118. data/lib/spontaneous/rack/back/changes.rb +2 -2
  119. data/lib/spontaneous/rack/back/file.rb +16 -24
  120. data/lib/spontaneous/rack/back/map.rb +5 -5
  121. data/lib/spontaneous/rack/back/preview.rb +3 -4
  122. data/lib/spontaneous/rack/back/schema.rb +1 -1
  123. data/lib/spontaneous/rack/back/site.rb +6 -7
  124. data/lib/spontaneous/rack/front.rb +19 -16
  125. data/lib/spontaneous/rack/middleware/authenticate.rb +3 -3
  126. data/lib/spontaneous/rack/middleware/reloader.rb +3 -2
  127. data/lib/spontaneous/rack/middleware/scope.rb +25 -19
  128. data/lib/spontaneous/rack/page_controller.rb +164 -13
  129. data/lib/spontaneous/rack/public.rb +23 -62
  130. data/lib/spontaneous/rack/static.rb +2 -3
  131. data/lib/spontaneous/schema.rb +27 -8
  132. data/lib/spontaneous/schema/schema_modification.rb +9 -1
  133. data/lib/spontaneous/schema/uid.rb +2 -2
  134. data/lib/spontaneous/schema/uid_map.rb +3 -2
  135. data/lib/spontaneous/search/database.rb +2 -2
  136. data/lib/spontaneous/search/field.rb +5 -3
  137. data/lib/spontaneous/search/index.rb +12 -7
  138. data/lib/spontaneous/search/results.rb +5 -3
  139. data/lib/spontaneous/server.rb +2 -2
  140. data/lib/spontaneous/site.rb +10 -3
  141. data/lib/spontaneous/site/features.rb +26 -6
  142. data/lib/spontaneous/site/helpers.rb +9 -12
  143. data/lib/spontaneous/site/level.rb +7 -9
  144. data/lib/spontaneous/site/map.rb +9 -11
  145. data/lib/spontaneous/site/paths.rb +5 -5
  146. data/lib/spontaneous/site/publishing.rb +83 -80
  147. data/lib/spontaneous/site/schema.rb +1 -7
  148. data/lib/spontaneous/site/search.rb +8 -18
  149. data/lib/spontaneous/site/selectors.rb +60 -54
  150. data/lib/spontaneous/site/state.rb +36 -30
  151. data/lib/spontaneous/site/storage.rb +10 -16
  152. data/lib/spontaneous/state.rb +8 -0
  153. data/lib/spontaneous/style.rb +32 -33
  154. data/lib/spontaneous/version.rb +1 -1
  155. data/spontaneous.gemspec +22 -21
  156. data/test/fixtures/public/templates/layouts/default.html.cut +1 -1
  157. data/test/fixtures/public/templates/layouts/default.pdf.cut +1 -1
  158. data/test/fixtures/public/templates/layouts/default.rss.cut +1 -1
  159. data/test/fixtures/search/config/indexes.rb +1 -1
  160. data/test/fixtures/serialisation/class_hash.yaml.erb +13 -1
  161. data/test/fixtures/serialisation/root_hash.yaml.erb +10 -0
  162. data/test/functional/test_application.rb +20 -24
  163. data/test/functional/test_back.rb +26 -27
  164. data/test/functional/test_cli.rb +146 -0
  165. data/test/functional/test_front.rb +287 -216
  166. data/test/functional/test_user_manager.rb +1 -1
  167. data/test/test_helper.rb +15 -11
  168. data/test/unit/test_alias.rb +32 -25
  169. data/test/unit/test_asset_bundler.rb +1 -1
  170. data/test/unit/test_assets.rb +34 -33
  171. data/test/unit/test_authentication.rb +1 -1
  172. data/test/unit/test_boxes.rb +16 -2
  173. data/test/unit/test_changesets.rb +23 -11
  174. data/test/unit/test_content.rb +15 -0
  175. data/test/unit/test_context.rb +139 -0
  176. data/test/unit/test_controllers.rb +374 -0
  177. data/test/{experimental → unit}/test_crypt.rb +0 -0
  178. data/test/unit/test_datamapper.rb +260 -237
  179. data/test/unit/test_datamapper_content.rb +42 -12
  180. data/test/{experimental → unit}/test_features.rb +85 -3
  181. data/test/unit/test_fields.rb +117 -42
  182. data/test/unit/test_formats.rb +11 -1
  183. data/test/unit/test_generators.rb +2 -2
  184. data/test/unit/test_helpers.rb +7 -8
  185. data/test/unit/test_images.rb +39 -2
  186. data/test/unit/test_layouts.rb +14 -12
  187. data/test/unit/test_media.rb +32 -23
  188. data/test/unit/test_output_store.rb +342 -0
  189. data/test/unit/test_page.rb +8 -1
  190. data/test/unit/test_permissions.rb +11 -7
  191. data/test/unit/test_plugins.rb +3 -3
  192. data/test/unit/test_prototype_set.rb +8 -1
  193. data/test/unit/test_publishing.rb +67 -54
  194. data/test/unit/test_render.rb +91 -38
  195. data/test/unit/test_revisions.rb +4 -4
  196. data/test/unit/test_schema.rb +109 -84
  197. data/test/unit/test_search.rb +42 -42
  198. data/test/unit/test_serialisation.rb +3 -2
  199. data/test/unit/test_site.rb +39 -27
  200. data/test/unit/test_storage.rb +9 -6
  201. data/test/unit/test_styles.rb +25 -32
  202. data/test/unit/test_templates.rb +8 -4
  203. metadata +89 -54
  204. data/lib/spontaneous/model/page/request.rb +0 -105
  205. data/lib/spontaneous/storage.rb +0 -22
@@ -18,17 +18,23 @@ describe "DataMapperContent" do
18
18
  end
19
19
 
20
20
  before do
21
+ root ||= Dir.mktmpdir
21
22
  @expected_columns = DB[:content].columns
22
23
  @database = ::Sequel.mock(autoid: 1)
23
24
  @database.columns = @expected_columns
24
- @schema = Spontaneous::Schema.new(Dir.pwd, NameMap)
25
- content_super = Spontaneous::Model(:content, @database, @schema)
25
+ @site = Spontaneous::Site.instantiate(root, :test, :back)
26
+ @schema = Spontaneous::Schema.new(@site, Dir.pwd, NameMap)
27
+ content_super = Spontaneous::Model!(:content, @database, @schema)
26
28
  content_class = Class.new(content_super)
29
+ page_class = Class.new(content_class::Page)
30
+ @site.model = content_class
27
31
  Object.const_set(:Content, content_class)
32
+ Object.const_set(:Page, page_class)
28
33
  end
29
34
 
30
35
  after do
31
36
  Object.send :remove_const, :Content rescue nil
37
+ Object.send :remove_const, :Page rescue nil
32
38
  end
33
39
 
34
40
  it "be defined as a top-level constant" do
@@ -39,11 +45,25 @@ describe "DataMapperContent" do
39
45
  ::Content.serialized_columns.must_equal [:field_store, :entry_store, :box_store, :serialized_modifications]
40
46
  end
41
47
 
48
+ it "has a dataset that doesnt filter by type" do
49
+ ds = ::Content.dataset
50
+ ds.must_be_instance_of Spontaneous::DataMapper::Dataset
51
+ ds.sql.must_equal "SELECT * FROM content WHERE (type_sid IN ('Page'))"
52
+ end
53
+
42
54
  it "search without type filters" do
43
55
  @database.sqls # clear sql log
44
56
  ::Content.all
45
57
  @database.sqls.must_equal [
46
- "SELECT * FROM content"
58
+ "SELECT * FROM content WHERE (type_sid IN ('Page'))"
59
+ ]
60
+ end
61
+
62
+ it "allows for retrieval of a single instance by primary key" do
63
+ @database.sqls # clear sql log
64
+ ::Content.primary_key_lookup(23)
65
+ @database.sqls.must_equal [
66
+ "SELECT * FROM content WHERE ((type_sid IN ('Page')) AND (id = 23)) LIMIT 1"
47
67
  ]
48
68
  end
49
69
 
@@ -63,7 +83,7 @@ describe "DataMapperContent" do
63
83
  b = ::Content.root
64
84
  end
65
85
  @database.sqls.must_equal [
66
- "SELECT * FROM content WHERE (path = '/') LIMIT 1"
86
+ "SELECT * FROM content WHERE ((type_sid IN ('Page')) AND (path = '/')) LIMIT 1"
67
87
  ]
68
88
  end
69
89
 
@@ -75,7 +95,7 @@ describe "DataMapperContent" do
75
95
  b = ::Content.path("/this")
76
96
  end
77
97
  @database.sqls.must_equal [
78
- "SELECT * FROM content WHERE (path = '/this') LIMIT 1"
98
+ "SELECT * FROM content WHERE ((type_sid IN ('Page')) AND (path = '/this')) LIMIT 1"
79
99
  ]
80
100
  end
81
101
 
@@ -87,20 +107,17 @@ describe "DataMapperContent" do
87
107
  b = ::Content.uid("fish")
88
108
  end
89
109
  @database.sqls.must_equal [
90
- "SELECT * FROM content WHERE (uid = 'fish') LIMIT 1"
110
+ "SELECT * FROM content WHERE ((type_sid IN ('Page')) AND (uid = 'fish')) LIMIT 1"
91
111
  ]
92
112
  end
93
113
 
94
114
  describe "Pages" do
95
115
  before do
96
- page_class = Class.new(::Content::Page)
97
- Object.const_set(:Page, page_class)
98
- Object.const_set(:P1, Class.new(page_class))
99
- Object.const_set(:P2, Class.new(page_class))
116
+ Object.const_set(:P1, Class.new(::Page))
117
+ Object.const_set(:P2, Class.new(::Page))
100
118
  end
101
119
 
102
120
  after do
103
- Object.send :remove_const, :Page rescue nil
104
121
  Object.send :remove_const, :P1 rescue nil
105
122
  Object.send :remove_const, :P2 rescue nil
106
123
  end
@@ -113,6 +130,13 @@ describe "DataMapperContent" do
113
130
  ]
114
131
  end
115
132
 
133
+ it "has a dataset that filters for pages" do
134
+ ds = ::Content::Page.dataset
135
+ ds.must_be_instance_of Spontaneous::DataMapper::Dataset
136
+ ds.sql.must_equal "SELECT * FROM content WHERE (type_sid IN ('Page', 'P1', 'P2'))"
137
+ end
138
+
139
+
116
140
  it "limit searches to a single class for all other page types" do
117
141
  @database.sqls # clear sql log
118
142
  ::Page.all
@@ -144,7 +168,7 @@ describe "DataMapperContent" do
144
168
  @database.sqls # clear sql log
145
169
  P2.root
146
170
  @database.sqls.must_equal [
147
- "SELECT * FROM content WHERE (path = '/') LIMIT 1"
171
+ "SELECT * FROM content WHERE ((type_sid IN ('Page', 'P1', 'P2')) AND (path = '/')) LIMIT 1"
148
172
  ]
149
173
  end
150
174
 
@@ -187,6 +211,12 @@ describe "DataMapperContent" do
187
211
  ]
188
212
  end
189
213
 
214
+ it "has a dataset that filters for pieces" do
215
+ ds = ::Content::Piece.dataset
216
+ ds.must_be_instance_of Spontaneous::DataMapper::Dataset
217
+ ds.sql.must_equal "SELECT * FROM content WHERE (type_sid IN ('Piece', 'P1', 'P2'))"
218
+ end
219
+
190
220
  it "limit searches to a single class for all other page types" do
191
221
  @database.sqls # clear sql log
192
222
  ::Piece.all
@@ -7,16 +7,23 @@ require 'sinatra/base'
7
7
  describe "Features" do
8
8
  include RackTestMethods
9
9
 
10
+ start do
11
+ S::State.delete
12
+ end
13
+
10
14
  before do
11
15
  @site = setup_site
16
+ @site.background_mode = :immediate
17
+ @site.output_store :Memory
12
18
  end
13
19
 
14
20
  after do
15
21
  teardown_site
22
+ S::State.delete
16
23
  end
17
24
 
18
25
  def app
19
- Spontaneous::Rack.application
26
+ Spontaneous::Rack.application(@site)
20
27
  end
21
28
 
22
29
  def api_key
@@ -34,6 +41,8 @@ describe "Features" do
34
41
  config.stubs(:auto_login).returns('root')
35
42
  config.stubs(:default_charset).returns('utf-8')
36
43
  config.stubs(:publishing_method).returns(:immediate)
44
+ config.stubs(:publishing_delay).returns(nil)
45
+ config.stubs(:keep_revisions).returns(5)
37
46
  config.stubs(:simultaneous_connection).returns("")
38
47
  config.stubs(:site_domain).returns('example.org')
39
48
  config.stubs(:site_id).returns('example_org')
@@ -49,6 +58,12 @@ describe "Features" do
49
58
  Spontaneous::Permissions::AccessKey.stubs(:valid?).with(nil, nil).returns(false)
50
59
  Spontaneous::Permissions::AccessKey.stubs(:valid?).with(nil, @user).returns(false)
51
60
  Spontaneous::Permissions::AccessKey.stubs(:valid?).with(@key, @user).returns(true)
61
+
62
+ @root = ::Page.create
63
+ Content.delete_revision(1) rescue nil
64
+ Spontaneous.logger.silent! {
65
+ @site.publish_all
66
+ }
52
67
  end
53
68
 
54
69
  after do
@@ -58,6 +73,37 @@ describe "Features" do
58
73
  Content.delete
59
74
  end
60
75
 
76
+ describe "middleware" do
77
+ before do
78
+ @middleware = Class.new do
79
+ def initialize(app, options = {})
80
+ @app, @options = app, options
81
+ end
82
+
83
+ def call(env)
84
+ status, headers, body = @app.call(env)
85
+ [status, headers.merge('X-Middleware' => @options[:header] || "present"), body]
86
+ end
87
+ end
88
+ end
89
+
90
+ it "allows for insertion of custom middleware into the front app" do
91
+ Spontaneous.mode = :front
92
+ Spontaneous.front.use @middleware, header: "inserted"
93
+ get "/"
94
+ assert last_response.status == 200, "Expected an 200 OK response but got #{last_response.status}"
95
+ last_response.headers['X-Middleware'].must_equal "inserted"
96
+ end
97
+
98
+ it "inserts defined middleware into the preview app" do
99
+ Spontaneous.mode = :back
100
+ Spontaneous.front.use @middleware, header: "inserted"
101
+ auth_get "/", preview: true
102
+ assert last_response.status == 200, "Expected an 200 OK response but got #{last_response.status}"
103
+ last_response.headers['X-Middleware'].must_equal "inserted"
104
+ end
105
+ end
106
+
61
107
  describe "controllers" do
62
108
  before do
63
109
  class ::FeatureBackController < ::Sinatra::Base
@@ -85,7 +131,7 @@ describe "Features" do
85
131
 
86
132
  it "be able to injectable into the back application" do
87
133
  Spontaneous.mode = :back
88
- Spontaneous.register_back_controller(:myfeature, FeatureBackController)
134
+ @site.register_back_controller(:myfeature, FeatureBackController)
89
135
 
90
136
  get "/@myfeature/hello"
91
137
  assert last_response.status == 401, "Expected an Unauthorised 401 response but got #{last_response.status}"
@@ -102,9 +148,17 @@ describe "Features" do
102
148
  assert last_response.body == "Cruel Editor"
103
149
  end
104
150
 
151
+ it "be able to injectable into the back application with custom path prefixes" do
152
+ Spontaneous.mode = :back
153
+ @site.register_back_controller(:myfeature, FeatureBackController, path_prefix: "/something")
154
+ auth_get "/something/hello"
155
+ assert last_response.ok?
156
+ assert last_response.body == "Editor"
157
+ end
158
+
105
159
  it "be able to inject controllers into the front application" do
106
160
  Spontaneous.mode = :front
107
- Spontaneous.register_front_controller(:myfeature, FeatureFrontController)
161
+ @site.register_front_controller(:myfeature, FeatureFrontController)
108
162
  get "/@myfeature/hello"
109
163
  assert last_response.ok?
110
164
  assert last_response.body == "World"
@@ -113,6 +167,34 @@ describe "Features" do
113
167
  assert last_response.ok?
114
168
  assert last_response.body == "Cruel World"
115
169
  end
170
+
171
+ it "be able to inject controllers into the front application with custom path prefixes" do
172
+ Spontaneous.mode = :front
173
+ @site.register_front_controller(:myfeature, FeatureFrontController, path_prefix: "/something/else")
174
+ get "/something/else/hello"
175
+ assert last_response.ok?
176
+ assert last_response.body == "World"
177
+ end
178
+
179
+ it "gives access to all mounted front apps in preview mode" do
180
+ Spontaneous.mode = :back
181
+ @site.register_front_controller(:myfeature, FeatureFrontController)
182
+ get "/@myfeature/hello"
183
+ assert last_response.ok?
184
+ assert last_response.body == "World"
185
+ end
186
+
187
+ it "allows injection of rack apps into the the back application" do
188
+ Spontaneous.mode = :back
189
+ @site.register_back_controller(:myfeature, proc { |env| [200, {}, "hello"] })
190
+
191
+ get "/@myfeature"
192
+ assert last_response.status == 401, "Expected an Unauthorised 401 response but got #{last_response.status}"
193
+
194
+ auth_get "/@myfeature"
195
+ assert last_response.ok?
196
+ assert last_response.body == "hello"
197
+ end
116
198
  end
117
199
  end
118
200
  end
@@ -279,14 +279,14 @@ describe "Fields" do
279
279
  def outputs
280
280
  [:html, :plain, :fancy]
281
281
  end
282
- def generate_html(value)
282
+ def generate_html(value, site)
283
283
  "<#{value}>"
284
284
  end
285
- def generate_plain(value)
285
+ def generate_plain(value, site)
286
286
  "*#{value}*"
287
287
  end
288
288
 
289
- def generate(output, value)
289
+ def generate(output, value, site)
290
290
  case output
291
291
  when :fancy
292
292
  "#{value}!"
@@ -347,7 +347,7 @@ describe "Fields" do
347
347
  end
348
348
  $transform = lambda { |value| "<#{value}>" }
349
349
  ContentClass1.field :title do
350
- def generate_html(value)
350
+ def generate_html(value, site)
351
351
  $transform[value]
352
352
  end
353
353
  end
@@ -366,7 +366,7 @@ describe "Fields" do
366
366
  before do
367
367
  ::CC = Class.new(Piece) do
368
368
  field :title, :default => "Magic" do
369
- def generate_html(value)
369
+ def generate_html(value, site)
370
370
  "*#{value}*"
371
371
  end
372
372
  end
@@ -511,7 +511,8 @@ describe "Fields" do
511
511
  before do
512
512
  class ::MarkdownContent < Piece
513
513
  field :text1, :markdown
514
- field :text2, :text
514
+ field :text2, :richtext
515
+ field :text3, :markup
515
516
  end
516
517
  @instance = MarkdownContent.new
517
518
  end
@@ -522,9 +523,12 @@ describe "Fields" do
522
523
  it "be available as the :markdown type" do
523
524
  assert MarkdownContent.field_prototypes[:text1].field_class < Spontaneous::Field::Markdown
524
525
  end
525
- it "be available as the :text type" do
526
+ it "be available as the :richtext type" do
526
527
  assert MarkdownContent.field_prototypes[:text2].field_class < Spontaneous::Field::Markdown
527
528
  end
529
+ it "be available as the :markup type" do
530
+ assert MarkdownContent.field_prototypes[:text3].field_class < Spontaneous::Field::Markdown
531
+ end
528
532
 
529
533
  it "process input into HTML" do
530
534
  @instance.text1 = "*Hello* **World**"
@@ -539,6 +543,37 @@ describe "Fields" do
539
543
  end
540
544
  end
541
545
 
546
+ describe "LongString fields" do
547
+ before do
548
+ class ::LongStringContent < Piece
549
+ field :long1, :longstring
550
+ field :long2, :long_string
551
+ field :long3, :text
552
+ end
553
+ @instance = LongStringContent.new
554
+ end
555
+ after do
556
+ Object.send(:remove_const, :LongStringContent)
557
+ end
558
+
559
+ it "is available as the :longstring type" do
560
+ assert LongStringContent.field_prototypes[:long1].field_class < Spontaneous::Field::LongString
561
+ end
562
+
563
+ it "is available as the :long_string type" do
564
+ assert LongStringContent.field_prototypes[:long2].field_class < Spontaneous::Field::LongString
565
+ end
566
+
567
+ it "is available as the :text type" do
568
+ assert LongStringContent.field_prototypes[:long3].field_class < Spontaneous::Field::LongString
569
+ end
570
+
571
+ it "translates newlines to <br/> tags" do
572
+ @instance.long1 = "this\nlong\nstring"
573
+ @instance.long1.value.must_equal "this<br />\nlong<br />\nstring"
574
+ end
575
+ end
576
+
542
577
  describe "Editor classes" do
543
578
  it "be defined in base types" do
544
579
  base_class = Spontaneous::Field::Image
@@ -1032,6 +1067,24 @@ describe "Fields" do
1032
1067
  @field.filesize.must_equal 2254
1033
1068
  end
1034
1069
 
1070
+ describe "clearing" do
1071
+ def assert_file_field_empty
1072
+ @field.value.must_equal ''
1073
+ @field.filename.must_equal ''
1074
+ @field.filesize.must_equal 0
1075
+ end
1076
+
1077
+ before do
1078
+ path = File.expand_path("../../fixtures/images/vimlogo.pdf", __FILE__)
1079
+ @field.value = path
1080
+ end
1081
+
1082
+ it "clears the value if set to the empty string" do
1083
+ @field.value = ''
1084
+ assert_file_field_empty
1085
+ end
1086
+ end
1087
+
1035
1088
  describe "with cloud storage" do
1036
1089
  before do
1037
1090
  ::Fog.mock!
@@ -1040,7 +1093,7 @@ describe "Fields" do
1040
1093
  :aws_secret_access_key=>"SECRET_ACCESS_KEY",
1041
1094
  :aws_access_key_id=>"ACCESS_KEY_ID"
1042
1095
  }
1043
- @storage = S::Storage::Cloud.new(@aws_credentials, "media.example.com")
1096
+ @storage = S::Media::Store::Cloud.new(@aws_credentials, "media.example.com")
1044
1097
  @site.expects(:storage).returns(@storage)
1045
1098
  end
1046
1099
 
@@ -1203,7 +1256,7 @@ describe "Fields" do
1203
1256
 
1204
1257
  describe "Asynchronous processing" do
1205
1258
  before do
1206
- S::Site.background_mode = :simultaneous
1259
+ @site.background_mode = :simultaneous
1207
1260
  @image = File.expand_path("../../fixtures/images/size.gif", __FILE__)
1208
1261
  @model = (::Piece)
1209
1262
  @model.field :title
@@ -1227,17 +1280,17 @@ describe "Fields" do
1227
1280
  # end
1228
1281
 
1229
1282
  it "be able to resolve fields id" do
1230
- S::Field.find(@instance.image.id, @instance.items.title.id).must_equal [
1283
+ S::Field.find(@site.model, @instance.image.id, @instance.items.title.id).must_equal [
1231
1284
  @instance.image, @instance.items.title
1232
1285
  ]
1233
1286
  end
1234
1287
 
1235
1288
  it "not raise errors for invalid fields" do
1236
- S::Field.find("0", "#{@instance.id}/xxx/#{@instance.items.title.schema_id}", "#{@instance.items.id}/nnn", @instance.items.title.id).must_equal [ @instance.items.title ]
1289
+ S::Field.find(@site.model, "0", "#{@instance.id}/xxx/#{@instance.items.title.schema_id}", "#{@instance.items.id}/nnn", @instance.items.title.id).must_equal [ @instance.items.title ]
1237
1290
  end
1238
1291
 
1239
1292
  it "return a single field if given a single id" do
1240
- S::Field.find(@instance.image.id).must_equal @instance.image
1293
+ S::Field.find(@site.model, @instance.image.id).must_equal @instance.image
1241
1294
  end
1242
1295
 
1243
1296
  it "be disabled for Date fields" do
@@ -1293,7 +1346,7 @@ describe "Fields" do
1293
1346
  @instance.image.schema_id.to_s => {:tempfile => file, :filename => "something.gif", :type => "image/gif"},
1294
1347
  @instance.description.schema_id.to_s => "Updated description"
1295
1348
  }
1296
- Spontaneous::Field.update(@instance, fields, nil, false)
1349
+ Spontaneous::Field.update(@site, @instance, fields, nil, false)
1297
1350
  @instance.reload
1298
1351
  @instance.title.value.must_equal "Updated title"
1299
1352
  @instance.description.value.must_equal "<p>Updated description</p>\n"
@@ -1315,7 +1368,7 @@ describe "Fields" do
1315
1368
  }
1316
1369
  @instance.expects(:save).at_least_once
1317
1370
 
1318
- Spontaneous::Field.update(@instance, fields, nil, true)
1371
+ Spontaneous::Field.update(@site, @instance, fields, nil, true)
1319
1372
 
1320
1373
  @instance.title.value.must_equal "Updated title"
1321
1374
  @instance.description.value.must_equal "<p>Updated description</p>\n"
@@ -1344,7 +1397,7 @@ describe "Fields" do
1344
1397
  })
1345
1398
  File.open(@image, "r") do |file|
1346
1399
  field.pending_version.must_equal 0
1347
- Spontaneous::Field.set(field, {:tempfile => file, :filename => "something.gif", :type => "image/gif"}, nil, true)
1400
+ Spontaneous::Field.set(@site, field, {:tempfile => file, :filename => "something.gif", :type => "image/gif"}, nil, true)
1348
1401
  field.value.must_equal ""
1349
1402
  field.pending_value.must_equal({
1350
1403
  :timestamp => S::Field.timestamp(@now),
@@ -1368,9 +1421,9 @@ describe "Fields" do
1368
1421
  File.open(@image, "r") do |file|
1369
1422
  fields = {
1370
1423
  box.title.schema_id.to_s => "Updated title",
1371
- box.image.schema_id.to_s => {:tempfile => file, :filename => "something.gif", :type => "image/gif"},
1424
+ box.image.schema_id.to_s => {:tempfile => file, :filename => "something.gif", :type => "image/gif"}
1372
1425
  }
1373
- Spontaneous::Field.update(box, fields, nil, false)
1426
+ Spontaneous::Field.update(@site, box, fields, nil, false)
1374
1427
  box.title.value.must_equal "Updated title"
1375
1428
  box.image.value.must_equal "/media/#{S::Media.pad_id(@instance.id)}/#{box.schema_id}/0001/something.gif"
1376
1429
  box.image.pending_version.must_equal 1
@@ -1386,9 +1439,9 @@ describe "Fields" do
1386
1439
  File.open(@image, "r") do |file|
1387
1440
  fields = {
1388
1441
  box.title.schema_id.to_s => "Updated title",
1389
- box.image.schema_id.to_s => {:tempfile => file, :filename => "something.gif", :type => "image/gif"},
1442
+ box.image.schema_id.to_s => {:tempfile => file, :filename => "something.gif", :type => "image/gif"}
1390
1443
  }
1391
- Spontaneous::Field.update(box, fields, nil, true)
1444
+ Spontaneous::Field.update(@site, box, fields, nil, true)
1392
1445
  box.title.value.must_equal "Updated title"
1393
1446
  field.value.must_equal ""
1394
1447
  field.pending_value.must_equal({
@@ -1413,7 +1466,7 @@ describe "Fields" do
1413
1466
  })
1414
1467
  File.open(@image, "r") do |file|
1415
1468
  field.pending_version.must_equal 0
1416
- Spontaneous::Field.set(field, {:tempfile => file, :filename => "something.gif", :type => "image/gif"}, nil, true)
1469
+ Spontaneous::Field.set(@site, field, {:tempfile => file, :filename => "something.gif", :type => "image/gif"}, nil, true)
1417
1470
  field.value.must_equal ""
1418
1471
  field.pending_value.must_equal({
1419
1472
  :timestamp => S::Field.timestamp(@now),
@@ -1435,24 +1488,46 @@ describe "Fields" do
1435
1488
  end
1436
1489
 
1437
1490
  it "immediately update asynchronous fields if background mode is :immediate" do
1438
- S::Site.background_mode = :immediate
1491
+ @site.background_mode = :immediate
1492
+ field = @instance.image
1439
1493
  File.open(@image, "r") do |file|
1440
1494
  fields = {
1441
- @instance.image.schema_id.to_s => {:tempfile => file, :filename => "something.gif", :type => "image/gif"}
1495
+ field.schema_id.to_s => {:tempfile => file, :filename => "something.gif", :type => "image/gif"}
1442
1496
  }
1443
1497
  Spontaneous::Simultaneous.expects(:fire).never
1444
- Spontaneous::Field.update(@instance, fields, nil, true)
1498
+ Spontaneous::Field.update(@site, @instance, fields, nil, true)
1445
1499
  @instance.image.value.must_equal "/media/#{S::Media.pad_id(@instance.id)}/0001/something.gif"
1446
1500
  end
1447
1501
  end
1448
1502
 
1503
+ it "immediately updates file fields when their new value is empty" do
1504
+ Spontaneous::Simultaneous.stubs(:fire)
1505
+ field = @instance.image
1506
+ File.open(@image, "r") do |file|
1507
+ fields = {
1508
+ field.schema_id.to_s => {:tempfile => file, :filename => "something.gif", :type => "image/gif"}
1509
+ }
1510
+ Spontaneous::Field.update(@site, @instance, fields, nil, false)
1511
+ @instance.reload
1512
+ field = @instance.image
1513
+ field.value.must_equal "/media/#{S::Media.pad_id(@instance.id)}/0001/something.gif"
1514
+ field.pending_value.must_be_nil
1515
+ end
1516
+ fields = {field.schema_id.to_s => ""}
1517
+ Spontaneous::Field.update(@site, @instance, fields, nil, true)
1518
+ @instance.reload
1519
+ field = @instance.image
1520
+ field.value.must_equal ""
1521
+ field.pending_value.must_be_nil
1522
+ end
1523
+
1449
1524
  it "not update a field if user does not have necessary permissions" do
1450
1525
  user = mock()
1451
1526
  @instance.title.expects(:writable?).with(user).at_least_once.returns(false)
1452
1527
  fields = {
1453
1528
  @instance.title.schema_id.to_s => "Updated title"
1454
1529
  }
1455
- Spontaneous::Field.update(@instance, fields, user, true)
1530
+ Spontaneous::Field.update(@site, @instance, fields, user, true)
1456
1531
  @instance.title.value.must_equal ""
1457
1532
  end
1458
1533
 
@@ -1460,7 +1535,7 @@ describe "Fields" do
1460
1535
  immediate = mock()
1461
1536
  immediate.expects(:pages).returns([])
1462
1537
  immediate.expects(:run)
1463
- Spontaneous::Field::Update::Immediate.expects(:new).with([@instance.image, @instance.items.title]).returns(immediate)
1538
+ Spontaneous::Field::Update::Immediate.expects(:new).with(@site, [@instance.image, @instance.items.title]).returns(immediate)
1464
1539
  # Thor generates a warning about creating a task with no 'desc'
1465
1540
  silence_logger {
1466
1541
  Spontaneous::Cli::Fields.any_instance.stubs(:prepare!)
@@ -1477,7 +1552,7 @@ describe "Fields" do
1477
1552
 
1478
1553
  it "revert to immediate updating if connection to simultaneous fails" do
1479
1554
  File.open(@image, "r") do |file|
1480
- Spontaneous::Field.set(@instance.image, {:tempfile => file, :filename => "something.gif", :type => "image/gif"}, nil, true)
1555
+ Spontaneous::Field.set(@site, @instance.image, {:tempfile => file, :filename => "something.gif", :type => "image/gif"}, nil, true)
1481
1556
  @instance.image.value.must_equal "/media/#{S::Media.pad_id(@instance.id)}/0001/something.gif"
1482
1557
  @instance.image.pending_value.must_be_nil
1483
1558
  end
@@ -1519,7 +1594,7 @@ describe "Fields" do
1519
1594
  "fields" => [@page.image.id]
1520
1595
  })
1521
1596
  File.open(@image, "r") do |file|
1522
- Spontaneous::Field.set(@page.image, {:tempfile => file, :filename => "something.gif", :type => "image/gif"}, nil, true)
1597
+ Spontaneous::Field.set(@site, @page.image, {:tempfile => file, :filename => "something.gif", :type => "image/gif"}, nil, true)
1523
1598
  @page.image.value.must_equal ""
1524
1599
  @page.update_locks.length.must_equal 1
1525
1600
  lock = @page.update_locks.first
@@ -1535,7 +1610,7 @@ describe "Fields" do
1535
1610
 
1536
1611
  it "not create locks for fields processed immediately" do
1537
1612
  field = @instance.title
1538
- Spontaneous::Field.set(field, "Updated Title", nil, true)
1613
+ Spontaneous::Field.set(@site, field, "Updated Title", nil, true)
1539
1614
  field.value.must_equal "Updated Title"
1540
1615
  @page.update_locks.length.must_equal 0
1541
1616
  refute @page.locked_for_update?
@@ -1547,7 +1622,7 @@ describe "Fields" do
1547
1622
  "fields" => [field.id]
1548
1623
  })
1549
1624
  File.open(@image, "r") do |file|
1550
- Spontaneous::Field.set(field, {:tempfile => file, :filename => "something.gif", :type => "image/gif"}, nil, true)
1625
+ Spontaneous::Field.set(@site, field, {:tempfile => file, :filename => "something.gif", :type => "image/gif"}, nil, true)
1551
1626
  field.value.must_equal ""
1552
1627
  @page.update_locks.length.must_equal 1
1553
1628
  lock = @page.update_locks.first
@@ -1567,7 +1642,7 @@ describe "Fields" do
1567
1642
  "fields" => [field.id]
1568
1643
  })
1569
1644
  File.open(@image, "r") do |file|
1570
- Spontaneous::Field.set(field, {:tempfile => file, :filename => "something.gif", :type => "image/gif"}, nil, true)
1645
+ Spontaneous::Field.set(@site, field, {:tempfile => file, :filename => "something.gif", :type => "image/gif"}, nil, true)
1571
1646
  field.value.must_equal ""
1572
1647
  @page.update_locks.length.must_equal 1
1573
1648
  lock = @page.update_locks.first
@@ -1586,14 +1661,14 @@ describe "Fields" do
1586
1661
  "fields" => [@page.image.id]
1587
1662
  })
1588
1663
  File.open(@image, "r") do |file|
1589
- Spontaneous::Field.set(@page.image, {:tempfile => file, :filename => "something.gif", :type => "image/gif"}, nil, true)
1664
+ Spontaneous::Field.set(@site, @page.image, {:tempfile => file, :filename => "something.gif", :type => "image/gif"}, nil, true)
1590
1665
  @page.image.value.must_equal ""
1591
1666
  @page.update_locks.length.must_equal 1
1592
1667
  assert @page.locked_for_update?
1593
1668
  # The lock manipulation is done by the updater
1594
1669
  # so calling update_pending_value on the field
1595
1670
  # won't clear any locks
1596
- Spontaneous::Field::Update::Immediate.process([@page.image])
1671
+ Spontaneous::Field::Update::Immediate.process(@site, [@page.image])
1597
1672
  @page.image.value.must_equal "/media/#{@page.id.to_s.rjust(5, "0")}/0001/something.gif"
1598
1673
  refute @page.reload.locked_for_update?
1599
1674
  end
@@ -1607,7 +1682,7 @@ describe "Fields" do
1607
1682
  Simultaneous.expects(:send_event).with('page_lock_status', "[#{@page.id}]")
1608
1683
 
1609
1684
  File.open(@image, "r") do |file|
1610
- Spontaneous::Field.set(field, {:tempfile => file, :filename => "something.gif", :type => "image/gif"}, nil, true)
1685
+ Spontaneous::Field.set(@site, field, {:tempfile => file, :filename => "something.gif", :type => "image/gif"}, nil, true)
1611
1686
  assert @page.locked_for_update?
1612
1687
  silence_logger {
1613
1688
  Spontaneous::Cli::Fields.any_instance.stubs(:prepare!)
@@ -1630,15 +1705,15 @@ describe "Fields" do
1630
1705
  "fields" => [field.id]
1631
1706
  })
1632
1707
  File.open(@image, "r") do |file|
1633
- Spontaneous::Field.set(field, {:tempfile => file, :filename => "something.gif", :type => "image/gif"}, nil, true)
1708
+ Spontaneous::Field.set(@site, field, {:tempfile => file, :filename => "something.gif", :type => "image/gif"}, nil, true)
1634
1709
  end
1635
- update = Spontaneous::Field::Update::Immediate.new(field)
1710
+ update = Spontaneous::Field::Update::Immediate.new(@site, field)
1636
1711
  old, field = field, field.reload
1637
1712
  later = @now + 1
1638
1713
  t = S::Field.timestamp(later)
1639
1714
  S::Field.stubs(:timestamp).returns(t)
1640
1715
  File.open(@image, "r") do |file|
1641
- Spontaneous::Field.set(field, {:tempfile => file, :filename => "else.gif", :type => "image/jpeg"}, nil, true)
1716
+ Spontaneous::Field.set(@site, field, {:tempfile => file, :filename => "else.gif", :type => "image/jpeg"}, nil, true)
1642
1717
  end
1643
1718
  update.run
1644
1719
 
@@ -1659,10 +1734,10 @@ describe "Fields" do
1659
1734
  "fields" => [field.id]
1660
1735
  })
1661
1736
  File.open(@image, "r") do |file|
1662
- Spontaneous::Field.set(field, {:tempfile => file, :filename => "something.gif", :type => "image/gif"}, nil, true)
1737
+ Spontaneous::Field.set(@site, field, {:tempfile => file, :filename => "something.gif", :type => "image/gif"}, nil, true)
1663
1738
  end
1664
1739
  # Create update but don't run it
1665
- update = Spontaneous::Field::Update::Immediate.new(field)
1740
+ update = Spontaneous::Field::Update::Immediate.new(@site, field)
1666
1741
  # Someone updates a field before the async update is run...
1667
1742
  content = ::Content.get(@instance.id)
1668
1743
  content.title = "Updated Title"
@@ -1684,10 +1759,10 @@ describe "Fields" do
1684
1759
  "fields" => [field.id]
1685
1760
  })
1686
1761
  File.open(@image, "r") do |file|
1687
- Spontaneous::Field.set(field, {:tempfile => file, :filename => "something.gif", :type => "image/gif"}, nil, true)
1762
+ Spontaneous::Field.set(@site, field, {:tempfile => file, :filename => "something.gif", :type => "image/gif"}, nil, true)
1688
1763
  end
1689
1764
  # Create update but don't run it
1690
- update = Spontaneous::Field::Update::Immediate.new(field)
1765
+ update = Spontaneous::Field::Update::Immediate.new(@site, field)
1691
1766
  # Someone updates a field before the async update is run...
1692
1767
  content = ::Content.get(@page.id)
1693
1768
  content.instances.title = "Updated Title"
@@ -1728,10 +1803,10 @@ describe "Fields" do
1728
1803
  "fields" => [field.id]
1729
1804
  })
1730
1805
  File.open(@image, "r") do |file|
1731
- Spontaneous::Field.set(field, {:tempfile => file, :filename => "something.gif", :type => "image/gif"}, nil, true)
1806
+ Spontaneous::Field.set(@site, field, {:tempfile => file, :filename => "something.gif", :type => "image/gif"}, nil, true)
1732
1807
  end
1733
1808
  # Create update but don't run it
1734
- update = Spontaneous::Field::Update::Immediate.new(field)
1809
+ update = Spontaneous::Field::Update::Immediate.new(@site, field)
1735
1810
 
1736
1811
  @page.destroy
1737
1812