spontaneous 0.1.0.alpha1 → 0.2.0.alpha1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (413) hide show
  1. data/Gemfile +40 -35
  2. data/LICENSE +20 -0
  3. data/Rakefile +39 -42
  4. data/Readme.markdown +155 -0
  5. data/application/css/definitions.scss +3 -0
  6. data/application/css/editing.scss +9 -4
  7. data/application/css/min/565d4c25e82148acb01c45c8d675b37a08676d77.css +1 -0
  8. data/application/css/min/84dbe894ea96eafd321c30823d630817bfc4b03b.css +1 -0
  9. data/application/css/min/d1b54ff4847c613618267ca1c15658e2aee0a4e5.css +1 -0
  10. data/application/css/v2.scss +144 -50
  11. data/application/js/add_alias_dialogue.js +18 -8
  12. data/application/js/ajax.js +1 -1
  13. data/application/js/authentication.js +8 -1
  14. data/application/js/box.js +37 -7
  15. data/application/js/box_container.js +1 -1
  16. data/application/js/compatibility.js +7 -0
  17. data/application/js/content.js +7 -0
  18. data/application/js/content_area.js +4 -0
  19. data/application/js/edit_panel.js +44 -18
  20. data/application/js/editing.js +12 -0
  21. data/application/js/event_source.js +17 -0
  22. data/application/js/extensions.js +2 -0
  23. data/application/js/field_types/file_field.js +2 -1
  24. data/application/js/field_types/image_field.js +35 -48
  25. data/application/js/field_types/long_string_field.js +12 -0
  26. data/application/js/field_types/markdown_field.js +2 -5
  27. data/application/js/field_types/string_field.js +5 -1
  28. data/application/js/field_types/webvideo_field.js +15 -0
  29. data/application/js/init.js +1 -0
  30. data/application/js/location.js +30 -19
  31. data/application/js/login.js +1 -2
  32. data/application/js/min/2a0c2962537a3181fedfff5c92596ba6d3122dc9.js +3 -0
  33. data/application/js/min/4cf1c493d3379ecba5287758c61238034c0893f9.js +2 -0
  34. data/application/js/min/78ac6b99d96750bb6b9f9aad4cb9cd91cd03f391.js +3 -0
  35. data/application/js/min/c8efb9b9f7c3f6613fcebc6be60f605b6570a382.js +90 -0
  36. data/application/js/page.js +1 -0
  37. data/application/js/page_entry.js +6 -40
  38. data/application/js/preview.js +2 -3
  39. data/application/js/top_bar.js +76 -31
  40. data/application/js/upload.js +5 -2
  41. data/application/js/upload_manager.js +2 -81
  42. data/application/js/vendor/jquery-1.7.1.min.js +4 -0
  43. data/application/js/vendor/jquery-ui-1.8.16.custom.min.js +791 -0
  44. data/application/js/views.js +3 -3
  45. data/application/js/views/box_view.js +164 -83
  46. data/application/js/views/page_piece_view.js +2 -2
  47. data/application/js/views/page_view.js +46 -15
  48. data/application/js/views/piece_view.js +29 -8
  49. data/application/static/diagonal-texture.png +0 -0
  50. data/application/static/item-buttons-highlight.png +0 -0
  51. data/application/static/plus-box.png +0 -0
  52. data/application/static/plus_alt.svg +8 -0
  53. data/application/static/slot-up-arrow.png +0 -0
  54. data/application/views/{index.erubis → index.erb} +9 -5
  55. data/application/views/{login.erubis → login.erb} +14 -10
  56. data/application/views/schema_modification_error.html.erb +18 -6
  57. data/application/views/{unsupported.erubis → unsupported.erb} +0 -0
  58. data/bin/spot +15 -2
  59. data/db/migrations/20100610142136_init.rb +2 -2
  60. data/db/migrations/20101130104334_timestamps.rb +2 -2
  61. data/db/migrations/20110209152710_users_and_groups.rb +11 -11
  62. data/db/migrations/20120106171423_visibility_path.rb +19 -0
  63. data/db/migrations/20120107124541_owner_id.rb +19 -0
  64. data/docs/recipe-interface-screenshot.png +0 -0
  65. data/lib/cutaneous.rb +11 -10
  66. data/lib/cutaneous/context_helper.rb +119 -14
  67. data/lib/cutaneous/preview_context.rb +4 -3
  68. data/lib/cutaneous/preview_renderer.rb +1 -6
  69. data/lib/cutaneous/publish_context.rb +1 -0
  70. data/lib/cutaneous/{first_pass_renderer.rb → publish_renderer.rb} +2 -6
  71. data/lib/cutaneous/publish_template.rb +62 -0
  72. data/lib/cutaneous/publish_token_parser.rb +8 -0
  73. data/lib/cutaneous/renderer.rb +4 -2
  74. data/lib/cutaneous/request_context.rb +1 -0
  75. data/lib/cutaneous/{second_pass_renderer.rb → request_renderer.rb} +2 -5
  76. data/lib/cutaneous/request_template.rb +11 -0
  77. data/lib/cutaneous/request_token_parser.rb +9 -0
  78. data/lib/cutaneous/token_parser.rb +125 -0
  79. data/lib/sequel/plugins/content_table_inheritance.rb +19 -12
  80. data/lib/sequel/plugins/scoped_table_name.rb +45 -0
  81. data/lib/spontaneous.rb +123 -126
  82. data/lib/spontaneous/box.rb +127 -47
  83. data/lib/spontaneous/box_style.rb +39 -22
  84. data/lib/spontaneous/change.rb +2 -2
  85. data/lib/spontaneous/cli.rb +1 -0
  86. data/lib/spontaneous/cli/base.rb +12 -4
  87. data/lib/spontaneous/cli/console.rb +72 -0
  88. data/lib/spontaneous/cli/server.rb +36 -12
  89. data/lib/spontaneous/cli/site.rb +175 -1
  90. data/lib/spontaneous/collections/box_set.rb +19 -1
  91. data/lib/spontaneous/collections/entry_set.rb +70 -22
  92. data/lib/spontaneous/collections/field_set.rb +3 -3
  93. data/lib/spontaneous/collections/prototype_set.rb +28 -0
  94. data/lib/spontaneous/config.rb +14 -5
  95. data/lib/spontaneous/content.rb +23 -24
  96. data/lib/spontaneous/errors.rb +20 -4
  97. data/lib/spontaneous/extensions/array.rb +2 -2
  98. data/lib/spontaneous/extensions/json.rb +1 -1
  99. data/lib/spontaneous/extensions/nil.rb +13 -0
  100. data/lib/spontaneous/facet.rb +61 -7
  101. data/lib/spontaneous/field_types.rb +1 -3
  102. data/lib/spontaneous/field_types/date_field.rb +1 -0
  103. data/lib/spontaneous/field_types/field.rb +32 -15
  104. data/lib/spontaneous/field_types/image_field.rb +138 -69
  105. data/lib/spontaneous/field_types/location_field.rb +59 -0
  106. data/lib/spontaneous/field_types/long_string_field.rb +13 -0
  107. data/lib/spontaneous/field_types/markdown_field.rb +2 -1
  108. data/lib/spontaneous/field_types/string_field.rb +2 -2
  109. data/lib/spontaneous/field_types/webvideo_field.rb +255 -0
  110. data/lib/spontaneous/generators/page/inline.html.cut +1 -1
  111. data/lib/spontaneous/generators/page/page.html.cut.tt +5 -4
  112. data/lib/spontaneous/generators/page/page.rb.tt +1 -5
  113. data/lib/spontaneous/generators/site.rb +1 -0
  114. data/lib/spontaneous/generators/site/.gitignore +2 -0
  115. data/lib/spontaneous/generators/site/Gemfile.tt +3 -5
  116. data/lib/spontaneous/generators/site/config/environment.rb.tt +1 -0
  117. data/lib/spontaneous/generators/site/config/environments/development.rb.tt +3 -1
  118. data/lib/spontaneous/generators/site/config/environments/production.rb.tt +3 -1
  119. data/lib/spontaneous/generators/site/config/indexes.rb.tt +23 -0
  120. data/lib/spontaneous/generators/site/public/css/{site.css → site.scss} +0 -0
  121. data/lib/spontaneous/generators/site/schema/box.rb.tt +5 -0
  122. data/lib/spontaneous/generators/site/templates/layouts/standard.html.cut.tt +11 -5
  123. data/lib/spontaneous/image_size.rb +0 -1
  124. data/lib/spontaneous/loader.rb +335 -218
  125. data/lib/spontaneous/logger.rb +7 -2
  126. data/lib/spontaneous/media.rb +64 -59
  127. data/lib/spontaneous/media/file.rb +74 -0
  128. data/lib/spontaneous/page.rb +47 -18
  129. data/lib/spontaneous/page_piece.rb +29 -27
  130. data/lib/spontaneous/paths.rb +0 -1
  131. data/lib/spontaneous/permissions.rb +3 -21
  132. data/lib/spontaneous/permissions/user.rb +5 -1
  133. data/lib/spontaneous/permissions/user_level.rb +6 -7
  134. data/lib/spontaneous/piece.rb +12 -7
  135. data/lib/spontaneous/plugins/aliases.rb +76 -31
  136. data/lib/spontaneous/plugins/allowed_types.rb +36 -37
  137. data/lib/spontaneous/plugins/application/facets.rb +7 -10
  138. data/lib/spontaneous/plugins/application/features.rb +17 -0
  139. data/lib/spontaneous/plugins/application/paths.rb +25 -31
  140. data/lib/spontaneous/plugins/application/render.rb +1 -5
  141. data/lib/spontaneous/plugins/application/serialisation.rb +2 -0
  142. data/lib/spontaneous/plugins/application/state.rb +6 -9
  143. data/lib/spontaneous/plugins/boxes.rb +65 -40
  144. data/lib/spontaneous/plugins/controllers.rb +22 -22
  145. data/lib/spontaneous/plugins/entries.rb +149 -150
  146. data/lib/spontaneous/plugins/entry.rb +50 -35
  147. data/lib/spontaneous/plugins/field/editor_class.rb +13 -0
  148. data/lib/spontaneous/plugins/fields.rb +41 -33
  149. data/lib/spontaneous/plugins/instance_code.rb +9 -9
  150. data/lib/spontaneous/plugins/layouts.rb +31 -35
  151. data/lib/spontaneous/plugins/media.rb +31 -32
  152. data/lib/spontaneous/plugins/page/formats.rb +56 -31
  153. data/lib/spontaneous/plugins/page/request.rb +15 -9
  154. data/lib/spontaneous/plugins/page_search.rb +30 -31
  155. data/lib/spontaneous/plugins/page_tree.rb +59 -12
  156. data/lib/spontaneous/plugins/paths.rb +84 -79
  157. data/lib/spontaneous/plugins/permissions.rb +26 -28
  158. data/lib/spontaneous/plugins/prototypes.rb +42 -37
  159. data/lib/spontaneous/plugins/publishing.rb +50 -94
  160. data/lib/spontaneous/plugins/render.rb +8 -16
  161. data/lib/spontaneous/plugins/schema_hierarchy.rb +20 -21
  162. data/lib/spontaneous/plugins/schema_id.rb +33 -25
  163. data/lib/spontaneous/plugins/schema_title.rb +3 -4
  164. data/lib/spontaneous/plugins/serialisation.rb +32 -35
  165. data/lib/spontaneous/plugins/site/features.rb +32 -0
  166. data/lib/spontaneous/plugins/site/instance.rb +3 -2
  167. data/lib/spontaneous/plugins/site/level.rb +18 -0
  168. data/lib/spontaneous/plugins/site/map.rb +2 -1
  169. data/lib/spontaneous/plugins/site/publishing.rb +56 -59
  170. data/lib/spontaneous/plugins/site/revisions.rb +24 -19
  171. data/lib/spontaneous/plugins/site/schema.rb +33 -0
  172. data/lib/spontaneous/plugins/site/search.rb +43 -0
  173. data/lib/spontaneous/plugins/site/selectors.rb +35 -30
  174. data/lib/spontaneous/plugins/site/storage.rb +63 -0
  175. data/lib/spontaneous/plugins/site_map.rb +23 -25
  176. data/lib/spontaneous/plugins/styles.rb +35 -43
  177. data/lib/spontaneous/plugins/supertype.rb +3 -1
  178. data/lib/spontaneous/plugins/visibility.rb +90 -83
  179. data/lib/spontaneous/prototypes/box_prototype.rb +55 -25
  180. data/lib/spontaneous/prototypes/field_prototype.rb +66 -19
  181. data/lib/spontaneous/prototypes/style_prototype.rb +2 -2
  182. data/lib/spontaneous/publishing.rb +1 -1
  183. data/lib/spontaneous/publishing/immediate.rb +128 -49
  184. data/lib/spontaneous/publishing/simultaneous.rb +70 -0
  185. data/lib/spontaneous/rack.rb +38 -26
  186. data/lib/spontaneous/rack/around_back.rb +3 -23
  187. data/lib/spontaneous/rack/around_preview.rb +3 -8
  188. data/lib/spontaneous/rack/assets.rb +7 -6
  189. data/lib/spontaneous/rack/authentication.rb +21 -0
  190. data/lib/spontaneous/rack/back.rb +310 -331
  191. data/lib/spontaneous/rack/cacheable_file.rb +27 -0
  192. data/lib/spontaneous/rack/cookie_authentication.rb +38 -0
  193. data/lib/spontaneous/rack/css.rb +43 -0
  194. data/lib/spontaneous/rack/event_source.rb +31 -0
  195. data/lib/spontaneous/rack/front.rb +30 -8
  196. data/lib/spontaneous/rack/helpers.rb +31 -0
  197. data/lib/spontaneous/rack/media.rb +22 -21
  198. data/lib/spontaneous/rack/public.rb +2 -1
  199. data/lib/spontaneous/rack/query_authentication.rb +35 -0
  200. data/lib/spontaneous/rack/reloader.rb +6 -3
  201. data/lib/spontaneous/rack/user_helpers.rb +28 -0
  202. data/lib/spontaneous/render.rb +64 -23
  203. data/lib/spontaneous/render/context_base.rb +143 -0
  204. data/lib/spontaneous/render/format.rb +24 -19
  205. data/lib/spontaneous/render/helpers.rb +14 -0
  206. data/lib/spontaneous/render/helpers/html_helper.rb +69 -0
  207. data/lib/spontaneous/render/helpers/script_helper.rb +17 -0
  208. data/lib/spontaneous/render/helpers/stylesheet_helper.rb +16 -0
  209. data/lib/spontaneous/render/preview_context.rb +8 -0
  210. data/lib/spontaneous/render/preview_renderer.rb +6 -0
  211. data/lib/spontaneous/render/publish_context.rb +22 -0
  212. data/lib/spontaneous/render/published_renderer.rb +12 -13
  213. data/lib/spontaneous/render/publishing_renderer.rb +3 -0
  214. data/lib/spontaneous/render/render_cache.rb +26 -0
  215. data/lib/spontaneous/render/renderer.rb +5 -1
  216. data/lib/spontaneous/render/request_context.rb +8 -0
  217. data/lib/spontaneous/schema.rb +56 -24
  218. data/lib/spontaneous/schema/schema_modification.rb +2 -2
  219. data/lib/spontaneous/schema/uid.rb +22 -106
  220. data/lib/spontaneous/schema/uid_map.rb +120 -0
  221. data/lib/spontaneous/search.rb +11 -0
  222. data/lib/spontaneous/search/compound_indexer.rb +26 -0
  223. data/lib/spontaneous/search/database.rb +72 -0
  224. data/lib/spontaneous/search/field.rb +95 -0
  225. data/lib/spontaneous/search/index.rb +184 -0
  226. data/lib/spontaneous/search/results.rb +34 -0
  227. data/lib/spontaneous/server.rb +5 -1
  228. data/lib/spontaneous/site.rb +56 -16
  229. data/lib/spontaneous/storage.rb +22 -0
  230. data/lib/spontaneous/storage/backend.rb +10 -0
  231. data/lib/spontaneous/storage/cloud.rb +104 -0
  232. data/lib/spontaneous/storage/local.rb +52 -0
  233. data/lib/spontaneous/style.rb +27 -9
  234. data/lib/spontaneous/version.rb +1 -1
  235. data/spontaneous.gemspec +719 -0
  236. data/test/disabled/test_slots.rb +1 -1
  237. data/test/experimental/test_cutaneous.rb +309 -0
  238. data/test/experimental/test_features.rb +129 -0
  239. data/test/fixtures/application/views/{index.erubis → index.erb} +0 -0
  240. data/test/fixtures/back/public/css/sass_include.scss +5 -0
  241. data/test/fixtures/back/public/css/sass_template.scss +4 -0
  242. data/test/fixtures/example_application/templates/client_project.html.cut +4 -4
  243. data/test/fixtures/example_application/templates/client_project/images.html.cut +1 -1
  244. data/test/fixtures/example_application/templates/client_projects.html.cut +2 -2
  245. data/test/fixtures/example_application/templates/inline_image.html.cut +1 -1
  246. data/test/fixtures/example_application/templates/layouts/home.html.cut +7 -7
  247. data/test/fixtures/example_application/templates/layouts/info.html.cut +1 -1
  248. data/test/fixtures/example_application/templates/layouts/project.html.cut +4 -4
  249. data/test/fixtures/example_application/templates/layouts/projects.html.cut +2 -2
  250. data/test/fixtures/example_application/templates/project.html.cut +2 -2
  251. data/test/fixtures/example_application/templates/project/inline.html.cut +3 -3
  252. data/test/fixtures/example_application/templates/project_image.html.cut +1 -1
  253. data/test/fixtures/example_application/templates/text.html.cut +1 -1
  254. data/test/fixtures/fields/youtube_api_response.xml +102 -0
  255. data/test/fixtures/layouts/layouts/custom4.html.cut +0 -0
  256. data/test/fixtures/plugins/schema_plugin/public/css/plugin.css +1 -0
  257. data/test/fixtures/plugins/schema_plugin/public/js/plugin.js +1 -0
  258. data/test/fixtures/plugins/schema_plugin/public/static.html +1 -0
  259. data/test/fixtures/plugins/schema_plugin/public/subdir/image.gif +1 -0
  260. data/test/fixtures/plugins/schema_plugin/public/subdir/include1.scss +3 -0
  261. data/test/fixtures/plugins/schema_plugin/public/subdir/sass.scss +4 -0
  262. data/test/fixtures/plugins/schema_plugin/public/subdir/sass/include2.scss +4 -0
  263. data/test/fixtures/public/templates/layouts/default.html.cut +1 -1
  264. data/test/fixtures/public/templates/layouts/default.pdf.cut +1 -1
  265. data/test/fixtures/public/templates/layouts/default.rss.cut +1 -1
  266. data/test/fixtures/public/templates/layouts/dynamic.html.cut +1 -1
  267. data/test/fixtures/schema_modification/config/database.yml +0 -0
  268. data/test/fixtures/schema_modification/config/environment.rb +2 -0
  269. data/test/fixtures/schema_modification/schema/box.rb +1 -0
  270. data/test/fixtures/schema_modification/schema/custom_box.rb +1 -0
  271. data/test/fixtures/schema_modification/schema/page.rb +6 -0
  272. data/test/fixtures/schema_modification/schema/piece.rb +1 -0
  273. data/test/fixtures/search/config/database.yml +1 -0
  274. data/test/fixtures/search/config/indexes.rb +4 -0
  275. data/test/fixtures/serialisation/root_hash.yaml.erb +16 -20
  276. data/test/fixtures/storage/cloud/environment.rb +12 -0
  277. data/test/fixtures/storage/default/environment.rb +1 -0
  278. data/test/fixtures/templates/aliases/layouts/c_alias.html.cut +1 -1
  279. data/test/fixtures/templates/content/include.html.cut +1 -1
  280. data/test/fixtures/templates/content/include_dir.html.cut +1 -1
  281. data/test/fixtures/templates/content/included.epub.cut +1 -1
  282. data/test/fixtures/templates/content/included.html.cut +1 -1
  283. data/test/fixtures/templates/content/partial/included.html.cut +1 -1
  284. data/test/fixtures/templates/content/preprocess.html.cut +1 -1
  285. data/test/fixtures/templates/content/second.html.cut +1 -1
  286. data/test/fixtures/templates/content/template.epub.cut +1 -1
  287. data/test/fixtures/templates/content/template.html.cut +1 -1
  288. data/test/fixtures/templates/default_style_class.html.cut +1 -1
  289. data/test/fixtures/templates/engine/braces.html.cut +6 -0
  290. data/test/fixtures/templates/engine/multiline.html.cut +5 -0
  291. data/test/fixtures/templates/extended/grandparent.html.cut +7 -7
  292. data/test/fixtures/templates/extended/main.html.cut +5 -5
  293. data/test/fixtures/templates/extended/parent.html.cut +10 -8
  294. data/test/fixtures/templates/extended/partial.html.cut +1 -0
  295. data/test/fixtures/templates/extended/partial_with_locals.html.cut +1 -0
  296. data/test/fixtures/templates/extended/with_includes.html.cut +9 -0
  297. data/test/fixtures/templates/extended/with_includes_and_locals.html.cut +9 -0
  298. data/test/fixtures/templates/layouts/entries.html.cut +7 -7
  299. data/test/fixtures/templates/layouts/page_style.html.cut +1 -1
  300. data/test/fixtures/templates/layouts/params.html.cut +1 -1
  301. data/test/fixtures/templates/layouts/preview_render.html.cut +2 -2
  302. data/test/fixtures/templates/layouts/standard_page.html.cut +1 -1
  303. data/test/fixtures/templates/layouts/subdir_style.html.cut +1 -1
  304. data/test/fixtures/templates/layouts/template_params.html.cut +1 -1
  305. data/test/fixtures/templates/layouts/variables.html.cut +7 -0
  306. data/test/fixtures/templates/page_class/inline_style.html.cut +1 -1
  307. data/test/fixtures/templates/preview_render/variables.html.cut +1 -0
  308. data/test/fixtures/templates/publishing/templates/layouts/dynamic.html.cut +1 -0
  309. data/test/fixtures/templates/publishing/templates/layouts/dynamic.rtf.cut +1 -0
  310. data/test/fixtures/templates/publishing/templates/layouts/static.html.cut +1 -0
  311. data/test/fixtures/templates/template_class/anonymous_style.html.cut +2 -2
  312. data/test/fixtures/templates/template_class/complex_template.html.cut +4 -4
  313. data/test/fixtures/templates/template_class/complex_template.pdf.cut +4 -4
  314. data/test/fixtures/templates/template_class/default_template_style.html.cut +2 -2
  315. data/test/fixtures/templates/template_class/images_with_template.html.cut +3 -3
  316. data/test/fixtures/templates/template_class/slots_template.html.cut +3 -3
  317. data/test/fixtures/templates/template_class/slots_template.pdf.cut +3 -3
  318. data/test/fixtures/templates/template_class/this_template.epub.cut +1 -1
  319. data/test/fixtures/templates/template_class/this_template.html.cut +1 -1
  320. data/test/fixtures/templates/template_class/this_template.pdf.cut +1 -1
  321. data/test/fixtures/templates/with_default_style_class.html.cut +1 -1
  322. data/test/functional/test_application.rb +19 -15
  323. data/test/functional/test_back.rb +130 -98
  324. data/test/functional/test_front.rb +72 -28
  325. data/test/javascript/test_dom.rb +1 -1
  326. data/test/javascript/test_markdown.rb +1 -1
  327. data/test/slow/test_publishing.rb +94 -75
  328. data/test/slow/test_visibility.rb +47 -14
  329. data/test/test_helper.rb +30 -3
  330. data/test/ui/test_page_editing.rb +1 -1
  331. data/test/unit/test_alias.rb +200 -16
  332. data/test/unit/test_authentication.rb +26 -28
  333. data/test/unit/test_boxes.rb +146 -75
  334. data/test/unit/test_config.rb +42 -20
  335. data/test/unit/test_content.rb +156 -37
  336. data/test/unit/test_content_inheritance.rb +4 -2
  337. data/test/unit/test_extensions.rb +7 -1
  338. data/test/unit/test_fields.rb +558 -290
  339. data/test/{experimental → unit}/test_formats.rb +42 -3
  340. data/test/unit/test_generators.rb +3 -2
  341. data/test/unit/test_helpers.rb +54 -0
  342. data/test/unit/test_image_size.rb +1 -1
  343. data/test/unit/test_images.rb +51 -40
  344. data/test/unit/test_layouts.rb +21 -4
  345. data/test/unit/test_logger.rb +1 -1
  346. data/test/unit/test_media.rb +165 -7
  347. data/test/unit/test_page.rb +158 -27
  348. data/test/unit/test_permissions.rb +170 -187
  349. data/test/unit/test_piece.rb +27 -9
  350. data/test/unit/test_plugins.rb +153 -0
  351. data/test/unit/test_prototype_set.rb +60 -2
  352. data/test/unit/test_prototypes.rb +81 -20
  353. data/test/unit/test_render.rb +97 -9
  354. data/test/unit/test_schema.rb +167 -120
  355. data/test/unit/test_search.rb +588 -0
  356. data/test/unit/test_serialisation.rb +26 -10
  357. data/test/unit/test_site.rb +42 -25
  358. data/test/unit/test_storage.rb +88 -0
  359. data/test/unit/test_structure.rb +11 -5
  360. data/test/unit/test_styles.rb +64 -3
  361. data/test/unit/test_table_scoping.rb +76 -0
  362. data/test/unit/test_templates.rb +69 -31
  363. data/test/unit/test_type_hierarchy.rb +9 -1
  364. metadata +274 -208
  365. data/Gemfile.lock +0 -146
  366. data/application/css/min/54ee0ed3c7fac7632bd5c020d69e9a2503e0c88c.css +0 -1
  367. data/application/css/min/c256adc144e2bdd0b0539356b04eb62db01e1dc3.css +0 -1
  368. data/application/js/edit_dialogue.js +0 -137
  369. data/application/js/min/492a209de8ee955fa9c729a765377495001e11b1.js +0 -17
  370. data/application/js/min/80f684d77c940887a1d4a63e3a96102e993baa98.js +0 -88
  371. data/application/js/min/c7140ec9475e5bf868b901e0621338d7d162358b.js +0 -3
  372. data/application/js/min/f07f2bd6630ee31e1c2288ec223383d8f0658ba6.js +0 -2
  373. data/application/js/vendor/.DS_Store +0 -0
  374. data/application/js/vendor/JS.Class-2.1.5/src/command.js +0 -93
  375. data/application/js/vendor/JS.Class-2.1.5/src/comparable.js +0 -37
  376. data/application/js/vendor/JS.Class-2.1.5/src/constant_scope.js +0 -48
  377. data/application/js/vendor/JS.Class-2.1.5/src/core.js +0 -1060
  378. data/application/js/vendor/JS.Class-2.1.5/src/decorator.js +0 -50
  379. data/application/js/vendor/JS.Class-2.1.5/src/enumerable.js +0 -505
  380. data/application/js/vendor/JS.Class-2.1.5/src/forwardable.js +0 -22
  381. data/application/js/vendor/JS.Class-2.1.5/src/hash.js +0 -334
  382. data/application/js/vendor/JS.Class-2.1.5/src/linked_list.js +0 -114
  383. data/application/js/vendor/JS.Class-2.1.5/src/loader.js +0 -553
  384. data/application/js/vendor/JS.Class-2.1.5/src/method_chain.js +0 -172
  385. data/application/js/vendor/JS.Class-2.1.5/src/observable.js +0 -55
  386. data/application/js/vendor/JS.Class-2.1.5/src/package.js +0 -472
  387. data/application/js/vendor/JS.Class-2.1.5/src/proxy.js +0 -58
  388. data/application/js/vendor/JS.Class-2.1.5/src/ruby.js +0 -44
  389. data/application/js/vendor/JS.Class-2.1.5/src/set.js +0 -332
  390. data/application/js/vendor/JS.Class-2.1.5/src/stack_trace.js +0 -151
  391. data/application/js/vendor/JS.Class-2.1.5/src/state.js +0 -95
  392. data/application/js/vendor/JS.Class-2.1.5/src/stdlib.js +0 -2612
  393. data/application/js/vendor/jquery-1.4.2.min.js +0 -154
  394. data/application/js/vendor/jquery-1.4.3.min.js +0 -166
  395. data/application/js/vendor/jquery-1.5.1.min.js +0 -16
  396. data/application/js/vendor/jquery-1.5.1rc1.min.js +0 -24
  397. data/application/js/vendor/jquery-ui-1.8.6.custom.min.js +0 -265
  398. data/application/js/vendor/jquery-ui-1.8.custom.min.js +0 -106
  399. data/application/js/vendor/jquery.hotkeys-0.7.9.js +0 -248
  400. data/application/js/vendor/jquery.hotkeys-0.7.9.min.js +0 -19
  401. data/application/js/vendor/jsdiff.js +0 -169
  402. data/lib/cutaneous/first_pass_parser.rb +0 -23
  403. data/lib/cutaneous/parser_core.rb +0 -18
  404. data/lib/cutaneous/second_pass_parser.rb +0 -23
  405. data/lib/sequel/plugins/yajl_serialization.rb +0 -154
  406. data/lib/spontaneous/plugins.rb +0 -20
  407. data/lib/spontaneous/proxy_object.rb +0 -12
  408. data/lib/spontaneous/publishing/fire_and_forget.rb +0 -57
  409. data/lib/spontaneous/render/context.rb +0 -100
  410. data/spontaneous.gemspec.tmpl +0 -66
  411. data/test/experimental/test_plugins.rb +0 -64
  412. data/test/fixtures/templates/publishing/layouts/dynamic.html.cut +0 -1
  413. data/test/fixtures/templates/publishing/layouts/static.html.cut +0 -1
@@ -0,0 +1,70 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'simultaneous'
4
+
5
+ module Spontaneous
6
+ module Publishing
7
+ class Simultaneous
8
+
9
+ def self.task_name
10
+ # TODO: add site name to this to make it unique on a server
11
+ :publish
12
+ end
13
+
14
+ def self.register_task
15
+ publish_binary = (Pathname.new(Spontaneous.gem_dir) + "bin/spot site:publish").expand_path.to_s
16
+ site_root = Pathname.new(Spontaneous.root).expand_path.to_s
17
+ niceness = S.config.publish_niceness || 15
18
+ logfile = "#{site_root}/log/publish.log"
19
+ # TODO: make nice value configurable
20
+ task_options = {
21
+ :niceness => niceness,
22
+ :logfile => logfile,
23
+ :pwd => site_root
24
+ }
25
+ task_params = {
26
+ "site" => site_root,
27
+ "logfile" => logfile,
28
+ "environment" => Spontaneous.env
29
+ }
30
+ ::Simultaneous.add_task(task_name, publish_binary, task_options, task_params, {})
31
+ end
32
+
33
+ def self.simultaneous_setup
34
+ ::Simultaneous.connection = ::Spontaneous.config.simultaneous_connection
35
+ ::Simultaneous.domain = ::Spontaneous.config.site_domain
36
+ end
37
+
38
+ simultaneous_setup
39
+ register_task
40
+
41
+ # def self.status
42
+ # FAF.get_status(task_name)
43
+ # end
44
+
45
+ # def self.status=(status)
46
+ # FAF.set_status(task_name, status)
47
+ # end
48
+
49
+ attr_reader :revision
50
+
51
+ def initialize(revision)
52
+ @revision = revision
53
+ end
54
+
55
+ def task_name
56
+ self.class.task_name
57
+ end
58
+
59
+ def publish_changes(change_list)
60
+ ::Simultaneous.fire(task_name, {"changes" => change_list})
61
+ end
62
+
63
+ def publish_all
64
+ ::Simultaneous.fire(task_name)
65
+ end
66
+ end # Simultaneous
67
+ end # Publishing
68
+ end # Spontaneous
69
+
70
+
@@ -6,6 +6,14 @@ require 'sinatra/base'
6
6
 
7
7
  module Spontaneous
8
8
  module Rack
9
+ NAMESPACE = "/@spontaneous".freeze
10
+ ACTIVE_USER = "SPONTANEOUS_USER".freeze
11
+ ACTIVE_KEY = "SPONTANEOUS_KEY".freeze
12
+ AUTH_COOKIE = "spontaneous_api_key".freeze
13
+ KEY_PARAM = "__key".freeze
14
+
15
+ EXPIRES_MAX = DateTime.parse("Thu, 31 Dec 2037 23:55:55 GMT").httpdate
16
+
9
17
  class << self
10
18
  def application
11
19
  case Spontaneous.mode
@@ -19,37 +27,41 @@ module Spontaneous
19
27
  def port
20
28
  Site.config.port
21
29
  end
30
+
31
+ def make_front_controller(controller_class)
32
+ controller_class.use(Spontaneous::Rack::AroundFront)
33
+ end
34
+
35
+ def make_back_controller(controller_class)
36
+ controller_class.helpers Spontaneous::Rack::Helpers
37
+ controller_class.helpers Spontaneous::Rack::UserHelpers
38
+ controller_class.use Spontaneous::Rack::CookieAuthentication
39
+ controller_class.use Spontaneous::Rack::AroundBack
40
+ controller_class.register Spontaneous::Rack::Authentication
41
+ end
22
42
  end
23
43
 
24
44
  class ServerBase < ::Sinatra::Base
25
45
  set :environment, Proc.new { Spontaneous.environment }
26
-
27
- # serve static files from the app's public dir
28
-
29
- ## removed these as sinatra now sets utf-8 by default
30
- # mime_type :js, 'text/javascript; charset=utf-8'
31
- # mime_type :css, 'text/css; charset=utf-8'
32
-
33
- # before do
34
- # ## globally setting this screws up auto content type setting by send_file
35
- # # content_type 'text/html', :charset => 'utf-8'
36
- # if Spontaneous.development?
37
- # # Templates.clear_cache!
38
- # end
39
- # end
40
46
  end
41
47
 
42
- autoload :HTTP, 'spontaneous/rack/http'
43
- autoload :Assets, 'spontaneous/rack/assets'
44
- autoload :Back, 'spontaneous/rack/back'
45
- autoload :Front, 'spontaneous/rack/front'
46
- autoload :Public, 'spontaneous/rack/public'
47
- autoload :Media, 'spontaneous/rack/media'
48
- autoload :Static, 'spontaneous/rack/static'
49
- autoload :AroundBack, 'spontaneous/rack/around_back'
50
- autoload :AroundFront, 'spontaneous/rack/around_front'
51
- autoload :AroundPreview, 'spontaneous/rack/around_preview'
52
- autoload :Reloader, 'spontaneous/rack/reloader'
48
+ autoload :HTTP, 'spontaneous/rack/http'
49
+ autoload :Assets, 'spontaneous/rack/assets'
50
+ autoload :Back, 'spontaneous/rack/back'
51
+ autoload :Front, 'spontaneous/rack/front'
52
+ autoload :Public, 'spontaneous/rack/public'
53
+ autoload :Authentication, 'spontaneous/rack/authentication'
54
+ autoload :CacheableFile, 'spontaneous/rack/cacheable_file'
55
+ autoload :Static, 'spontaneous/rack/static'
56
+ autoload :UserHelpers, 'spontaneous/rack/user_helpers'
57
+ autoload :Helpers, 'spontaneous/rack/helpers'
58
+ autoload :CookieAuthentication, 'spontaneous/rack/cookie_authentication'
59
+ autoload :QueryAuthentication, 'spontaneous/rack/query_authentication'
60
+ autoload :AroundBack, 'spontaneous/rack/around_back'
61
+ autoload :AroundFront, 'spontaneous/rack/around_front'
62
+ autoload :AroundPreview, 'spontaneous/rack/around_preview'
63
+ autoload :Reloader, 'spontaneous/rack/reloader'
64
+ autoload :EventSource, 'spontaneous/rack/event_source'
65
+ autoload :CSS, 'spontaneous/rack/css'
53
66
  end
54
67
  end
55
-
@@ -1,7 +1,5 @@
1
1
  # encoding: UTF-8
2
2
 
3
- require 'less'
4
-
5
3
  module Spontaneous
6
4
  module Rack
7
5
  class AroundBack
@@ -9,36 +7,18 @@ module Spontaneous
9
7
  @app = app
10
8
  end
11
9
 
12
- def user(env)
13
- if login = Site.config.auto_login
14
- user = Spontaneous::Permissions::User[:login => login]
15
- else
16
- request = ::Rack::Request.new(env)
17
- api_key = request.cookies[Spontaneous::Rack::Back::AUTH_COOKIE]
18
- if api_key && key = Spontaneous::Permissions::AccessKey.authenticate(api_key)
19
- key.user
20
- else
21
- nil
22
- end
23
- end
24
- end
25
10
 
26
11
  def call(env)
27
12
  response = nil
28
13
  Content.with_identity_map do
29
- Spontaneous::Permissions.with_user(user(env)) do
30
- S::Render.with_preview_renderer do
31
- Change.record do
32
- response = @app.call(env)
33
- end
14
+ S::Render.with_preview_renderer do
15
+ Change.record do
16
+ response = @app.call(env)
34
17
  end
35
18
  end
36
19
  end
37
20
  response
38
21
  end
39
-
40
22
  end
41
-
42
23
  end
43
24
  end
44
-
@@ -1,9 +1,8 @@
1
1
  # encoding: UTF-8
2
2
 
3
-
4
3
  module Spontaneous
5
4
  module Rack
6
- class AroundPreview < AroundBack
5
+ class AroundPreview
7
6
  def initialize(app)
8
7
  @app = app
9
8
  end
@@ -11,16 +10,12 @@ module Spontaneous
11
10
  def call(env)
12
11
  response = nil
13
12
  Content.with_identity_map do
14
- Spontaneous::Permissions.with_user(user(env)) do
15
- S::Render.with_preview_renderer do
16
- response = @app.call(env)
17
- end
13
+ S::Render.with_preview_renderer do
14
+ response = @app.call(env)
18
15
  end
19
16
  end
20
17
  response
21
18
  end
22
-
23
19
  end
24
20
  end
25
21
  end
26
-
@@ -68,11 +68,11 @@ module Spontaneous
68
68
  compress_js(filelist)
69
69
  end
70
70
 
71
- JQUERY = %w(min/492a209de8ee955fa9c729a765377495001e11b1)
72
- COMPATIBILITY = %w(min/f07f2bd6630ee31e1c2288ec223383d8f0658ba6)
71
+ JQUERY = %w(min/2a0c2962537a3181fedfff5c92596ba6d3122dc9)
72
+ COMPATIBILITY = %w(min/4cf1c493d3379ecba5287758c61238034c0893f9)
73
73
  REQUIRE = %w(min/b8abf302a824c35385ff517b34111e1710ff3b37)
74
- LOGIN_JS = %w(min/c7140ec9475e5bf868b901e0621338d7d162358b)
75
- EDITING_JS = %w(min/80f684d77c940887a1d4a63e3a96102e993baa98)
74
+ LOGIN_JS = %w(min/78ac6b99d96750bb6b9f9aad4cb9cd91cd03f391)
75
+ EDITING_JS = %w(min/c8efb9b9f7c3f6613fcebc6be60f605b6570a382)
76
76
  end
77
77
 
78
78
  module CSS
@@ -89,8 +89,9 @@ module Spontaneous
89
89
  compress_css(filelist)
90
90
  end
91
91
 
92
- LOGIN_CSS = %w(min/54ee0ed3c7fac7632bd5c020d69e9a2503e0c88c)
93
- EDITING_CSS = %w(min/c256adc144e2bdd0b0539356b04eb62db01e1dc3)
92
+ LOGIN_CSS = %w(min/565d4c25e82148acb01c45c8d675b37a08676d77)
93
+ EDITING_CSS = %w(min/d1b54ff4847c613618267ca1c15658e2aee0a4e5)
94
+ SCHEMA_MODIFICATION_CSS = %w(min/84dbe894ea96eafd321c30823d630817bfc4b03b)
94
95
  end
95
96
 
96
97
  end # Assets
@@ -0,0 +1,21 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'rack'
4
+
5
+ module Spontaneous::Rack
6
+ module Authentication
7
+
8
+ def requires_authentication!(options = {})
9
+ first_level_exceptions = (options[:except_all] || []).concat(["#{NAMESPACE}/login", "#{NAMESPACE}/reauthenticate"] )
10
+ second_level_exceptions = (options[:except_key] || [])
11
+ before {
12
+ unless first_level_exceptions.any? { |e| e === request.path }
13
+ ignore_key = second_level_exceptions.any? { |e| e === request.path }
14
+ valid_key = ignore_key || Spontaneous::Permissions::AccessKey.valid?(params[KEY_PARAM], user)
15
+ halt(401, erb(:login, :views => Spontaneous.application_dir('/views'), :locals => { :login => '' })) unless (user and valid_key)
16
+ show_login_page( :login => '' ) unless (user and valid_key)
17
+ end
18
+ }
19
+ end
20
+ end
21
+ end
@@ -8,103 +8,87 @@ module Spontaneous
8
8
  module Back
9
9
  include Assets
10
10
 
11
- NAMESPACE = "/@spontaneous".freeze
12
- AUTH_COOKIE = "spontaneous_api_key".freeze
13
-
14
-
15
- module Authentication
16
- module Helpers
17
- def authorised?
18
- if cookie = request.cookies[AUTH_COOKIE]
19
- true
20
- else
21
- false
22
- end
23
- end
24
-
25
- def unauthorised!
26
- halt 401#, "You do not have the necessary permissions to update the '#{name}' field"
27
- end
28
-
29
- def api_key
30
- request.cookies[AUTH_COOKIE]
31
- end
32
-
33
- def user
34
- @user ||= load_user
35
- end
36
-
37
- def load_user
38
- Spontaneous::Permissions.active_user
39
- end
40
- end
41
-
42
- def self.registered(app)
43
- app.helpers Authentication::Helpers
44
-
45
- app.post "/reauthenticate" do
46
- if key = Spot::Permissions::AccessKey.authenticate(params[:api_key])
47
- response.set_cookie(AUTH_COOKIE, {
48
- :value => key.key_id,
49
- :path => '/'
50
- })
51
- redirect NAMESPACE, 302
52
- else
53
- halt(401, erubis(:login, :locals => { :invalid_key => true }))
54
- end
55
- end
56
-
57
- app.post "/login" do
58
- login = params[:user][:login]
59
- password = params[:user][:password]
60
- if key = Spontaneous::Permissions::User.authenticate(login, password)
61
- response.set_cookie(AUTH_COOKIE, {
62
- :value => key.key_id,
63
- :path => '/'
64
- })
65
- if request.xhr?
66
- json({
67
- :key => key.key_id,
68
- :redirect => NAMESPACE
69
- })
70
- else
71
- redirect NAMESPACE, 302
72
- end
73
- else
74
- halt(401, erubis(:login, :locals => { :login => login, :failed => true }))
75
- end
76
- end
77
- end
78
-
79
- KEY_PARAM = "__key".freeze
80
-
81
- def requires_authentication!(options = {})
82
- first_level_exceptions = (options[:except_all] || []).concat(["#{NAMESPACE}/login", "#{NAMESPACE}/reauthenticate"] )
83
- second_level_exceptions = (options[:except_key] || [])
84
- before do
85
- unless first_level_exceptions.any? { |e| e === request.path }
86
- ignore_key = second_level_exceptions.any? { |e| e === request.path }
87
- valid_key = ignore_key || Spontaneous::Permissions::AccessKey.valid?(params[KEY_PARAM], user)
88
- unless (user and valid_key)
89
- halt(401, erubis(:login, :locals => { :login => '' }))
90
- end
91
- end
92
- end
93
- end
11
+ def self.messenger
12
+ @messenger ||= ::Spontaneous::Rack::EventSource.new
13
+ # Find a way to move this into a more de-centralised place
14
+ # at some point we are going to want to have some configurable, extendable
15
+ # list of event handlers
16
+ Simultaneous.on_event("publish_progress") { |event|
17
+ @messenger.deliver_event(event)
18
+ }
19
+ @messenger
94
20
  end
95
21
 
96
-
97
22
  def self.application
23
+ messenger = self.messenger
98
24
  app = ::Rack::Builder.new do
99
- use ::Rack::Lint
100
- use ::Rack::ShowExceptions if Spontaneous.development?
25
+ # use ::Rack::ShowExceptions if Spontaneous.development?
26
+ # AFAIK the only non-thread-safe part of the stack are the renderer calls
27
+ # because they rely on the global values of renderer and also Content.with_visible
28
+ # I'm not sure that using Rack::Lock here would fix any problems that this causes,
29
+ # or even if there are any problems that would be caused
30
+ #
31
+ # use ::Rack::Lock
32
+ # ###################
33
+ # Looking at the three Around* middlewares, there shouldn't actually be a problem with
34
+ # the renderers, as the only conflict would come from the back server which provides two
35
+ # outputs: the preview and the editing interface. Luckily both the preview and the editing
36
+ # interface share the same renderer.
37
+ # The real problem is the Content::with_visible wrapper as the editing interface and the preview
38
+ # renderer use different values for this. As the only way to solve this would be using a global
39
+ # to replace the model class (as we need to be able to issue thread save Model.select calls)
40
+ # I don't know how to fix this.
41
+ # One solution would be to always use the Content::_unfiltered_dataset call within the editing interface
42
+ # and then we'd be free (I think) to wrap it in the with_visible call, though I don't know how this would
43
+ # affect the loading of content within the page.
44
+ #
45
+ # Needs testing...
46
+ # ###################
101
47
 
102
48
  use Spontaneous::Rack::Static, :root => Spontaneous.root / "public",
103
49
  :urls => %w[/],
104
50
  :try => ['.html', 'index.html', '/index.html']
105
51
 
52
+ ################### REMOVE THIS
53
+ # map "#{NAMESPACE}/lock" do
54
+ # run proc {
55
+ # Spontaneous.database.transaction do
56
+ # Spontaneous.database.run("LOCK TABLES content WRITE, spontaneous_access_keys READ")
57
+ # puts S::Content.first
58
+ # Spontaneous.database.run("SELECT SLEEP(10)")
59
+ # Spontaneous.database.run("UNLOCK TABLES")
60
+ # end
61
+ # }
62
+ # end
63
+ # map "#{NAMESPACE}/unlock" do
64
+ # run proc { Spontaneous.database.run("UNLOCK TABLES") }
65
+ # end
66
+ ################### END REMOVE THIS
67
+
68
+ Spontaneous.instance.back_controllers.each do |namespace, controller_class|
69
+ map namespace do
70
+ run controller_class
71
+ end
72
+ end if Spontaneous.instance
73
+
74
+ # Make all the files available under plugin_name/public/**
75
+ # available under the URL /plugin_name/**
76
+ Spontaneous.instance.plugins.each do |plugin|
77
+ root = plugin.paths.expanded(:public)
78
+ map "/#{plugin.file_namespace}" do
79
+ use Spontaneous::Rack::CSS, :root => root
80
+ run ::Rack::File.new(root)
81
+ end
82
+ end if Spontaneous.instance
83
+
84
+ map "#{NAMESPACE}/events" do
85
+ use CookieAuthentication
86
+ use QueryAuthentication
87
+ run messenger.app
88
+ end
106
89
 
107
90
  map NAMESPACE do
91
+ use ::Rack::Lint
108
92
  use Spontaneous::Rack::Static, :root => Spontaneous.application_dir, :urls => %W(/static /js)
109
93
  use AssetsHandler
110
94
  use UnsupportedBrowserHandler
@@ -113,10 +97,13 @@ module Spontaneous
113
97
  end
114
98
 
115
99
  map "/media" do
116
- run Spontaneous::Rack::Media.new
100
+ use ::Rack::Lint
101
+ run Spontaneous::Rack::CacheableFile.new(Spontaneous.media_dir)
117
102
  end
118
103
 
119
104
  map "/" do
105
+ use ::Rack::Lint
106
+ use Spontaneous::Rack::CSS, :root => Spontaneous.instance.paths.expanded(:public)
120
107
  run Preview
121
108
  end
122
109
  end
@@ -124,56 +111,40 @@ module Spontaneous
124
111
 
125
112
  class EditingBase < ServerBase
126
113
  set :views, Proc.new { Spontaneous.application_dir + '/views' }
114
+ set :environment, Proc.new { Spontaneous.env }
115
+ enable :dump_errors, :raise_errors, :show_exceptions if Spontaneous.development?
127
116
 
128
- helpers do
129
-
130
- def style_url(style)
131
- "#{NAMESPACE}/css/#{style}.css"
132
- end
133
-
134
- def script_url(script)
135
- "#{NAMESPACE}/js/#{script}.js"
136
- end
137
-
138
- def script_list(scripts)
139
- if Spontaneous.development?
140
- scripts.map do |script|
141
- src = "/js/#{script}.js"
142
- path = Spontaneous.application_dir(src)
143
- size = File.size(path)
144
- ["#{NAMESPACE}#{src}", size]
145
- # %(<script src="#{NAMESPACE}/js/#{script}.js" type="text/javascript"></script>)
146
- end.to_json
147
- else
148
- # script bundling + compression
149
- end
150
- end
151
- end
117
+ helpers Spontaneous::Rack::UserHelpers
118
+ helpers Spontaneous::Rack::Helpers
152
119
 
153
120
  def json(response)
154
121
  content_type 'application/json', :charset => 'utf-8'
155
- response.serialise_http
122
+ response.serialise_http(user)
156
123
  end
157
124
  end
158
125
 
159
126
  class UnsupportedBrowserHandler < EditingBase
160
127
  get '/unsupported' do
161
- erubis :unsupported
128
+ erb :unsupported
162
129
  end
163
130
  end
164
131
 
165
- class AuthenticatedHandler < EditingBase
132
+ class BackControllerBase < EditingBase
133
+ # use CookieAuthentication
134
+ # use AroundBack
135
+ # register Authentication
136
+ end
137
+ Spontaneous::Rack.make_back_controller(BackControllerBase)
166
138
 
167
- use AroundBack
168
- register Authentication
169
- requires_authentication! :except_all => [%r(^#{NAMESPACE}/unsupported)], :except_key => [%r(^#{NAMESPACE}/?$)]
139
+ class AuthenticatedHandler < BackControllerBase
140
+ requires_authentication! :except_all => [%r(^#{NAMESPACE}/unsupported)], :except_key => [%r(^#{NAMESPACE}/?(/\d+/?.*)?$)]
170
141
  end
171
142
 
172
143
  class SchemaModification < AuthenticatedHandler
173
144
 
174
145
  post "/schema/delete" do
175
146
  begin
176
- Spontaneous::Schema.apply_fix(:delete, params[:uid])
147
+ Spontaneous.schema.apply_fix(:delete, params[:uid])
177
148
  rescue Spot::SchemaModificationError # ignore remaining errors - they will be fixed later
178
149
  end
179
150
  redirect(params[:origin])
@@ -181,8 +152,8 @@ module Spontaneous
181
152
 
182
153
  post "/schema/rename" do
183
154
  begin
184
- Spontaneous::Schema.apply_fix(:rename, params[:uid], params[:ref])
185
- rescue Spot::SchemaModificationError # ignore remaining errors - they will be fixed later
155
+ Spontaneous.schema.apply_fix(:rename, params[:uid], params[:ref])
156
+ rescue Spot::SchemaModificationError => e # ignore remaining errors - they will be fixed later
186
157
  end
187
158
  redirect(params[:origin])
188
159
  end
@@ -200,10 +171,10 @@ module Spontaneous
200
171
  if field_data
201
172
  field_data.each do |id, values|
202
173
  field = model.fields.sid(id)
203
- if model.field_writable?(field.name.to_sym)
174
+ if model.field_writable?(user, field.name.to_sym)
204
175
  # version = values.delete("version").to_i
205
176
  # if version == field.version
206
- field.update(values)
177
+ field.update(values)
207
178
  # else
208
179
  # conflicts << [field, values]
209
180
  # end
@@ -224,20 +195,61 @@ module Spontaneous
224
195
  end
225
196
  end
226
197
 
227
- def content_for_request
228
- content = Content[params[:id]]
229
- halt 404 if content.nil?
230
- if box_id = Spontaneous::Schema::UID[params[:box_id]]
231
- box = content.boxes.detect { |b| b.schema_id == box_id }
232
- [content, box]
198
+ def content_for_request(lock = false)
199
+ Content.db.transaction {
200
+ dataset = lock ? Content.for_update : Content
201
+ content = dataset.first(:id => params[:id])
202
+ halt 404 if content.nil?
203
+ if box_id = Spontaneous.schema.uids[params[:box_id]]
204
+ box = content.boxes.detect { |b| b.schema_id == box_id }
205
+ yield(content, box)
206
+ else
207
+ yield(content)
208
+ end
209
+ }
210
+ end
211
+
212
+ post "/reauthenticate" do
213
+ if key = Spot::Permissions::AccessKey.authenticate(params[:api_key])
214
+ response.set_cookie(AUTH_COOKIE, {
215
+ :value => key.key_id,
216
+ :path => '/'
217
+ })
218
+ origin = "#{NAMESPACE}#{params[:origin]}"
219
+ redirect origin, 302
233
220
  else
234
- content
221
+ show_login_page( :invalid_key => true )
235
222
  end
236
223
  end
237
224
 
225
+ post "/login" do
226
+ login = params[:user][:login]
227
+ password = params[:user][:password]
228
+ origin = "#{NAMESPACE}#{params[:origin]}"
229
+ if key = Spontaneous::Permissions::User.authenticate(login, password)
230
+ response.set_cookie(AUTH_COOKIE, {
231
+ :value => key.key_id,
232
+ :path => '/'
233
+ })
234
+ if request.xhr?
235
+ json({
236
+ :key => key.key_id,
237
+ :redirect => origin
238
+ })
239
+ else
240
+ redirect origin, 302
241
+ end
242
+ else
243
+ show_login_page( :login => login, :failed => true )
244
+ end
245
+ end
238
246
 
239
247
  get '/?' do
240
- erubis :index
248
+ erb :index
249
+ end
250
+
251
+ get %r{^/(\d+/?.*)?$} do
252
+ erb :index
241
253
  end
242
254
 
243
255
  get '/root' do
@@ -250,11 +262,11 @@ module Spontaneous
250
262
  # TODO: check for perms on the particular bit of content
251
263
  # and pass user level into returned JSON
252
264
  get '/page/:id' do
253
- json(content_for_request)
265
+ content_for_request { |content| json(content)}
254
266
  end
255
267
 
256
268
  get '/types' do
257
- json Schema
269
+ json Site.schema
258
270
  end
259
271
 
260
272
  # get '/type/:type' do
@@ -287,7 +299,7 @@ module Spontaneous
287
299
 
288
300
  post '/root' do
289
301
  if Site.root.nil?
290
- type = Spontaneous::Schema[params[:type]]
302
+ type = Spontaneous.schema[params[:type]]
291
303
  root = type.create(:title => "Home")
292
304
  Spontaneous::Change.push(root)
293
305
  json({:id => root.id})
@@ -296,14 +308,10 @@ module Spontaneous
296
308
  end
297
309
  end
298
310
 
299
- post '/version/:id' do
300
- content = content_for_request
301
- generate_conflict_list(content)
302
- end
303
-
304
- post '/version/:id/:box_id' do
305
- content, box = content_for_request
306
- generate_conflict_list(box)
311
+ post '/version/:id/?:box_id?' do
312
+ content_for_request(true) do |content, box|
313
+ generate_conflict_list(box || content)
314
+ end
307
315
  end
308
316
 
309
317
  def generate_conflict_list(content)
@@ -326,109 +334,101 @@ module Spontaneous
326
334
  end
327
335
 
328
336
  post '/save/:id' do
329
- update_fields(content_for_request, params[:field])
337
+ content_for_request(true) do |content|
338
+ update_fields(content, params[:field])
339
+ end
330
340
  end
331
341
 
332
342
  post '/savebox/:id/:box_id' do
333
- content, box = content_for_request
334
- if box.writable?
335
- update_fields(box, params[:field])
336
- else
337
- unauthorised!
343
+ content_for_request(true) do |content, box|
344
+ if box.writable?(user)
345
+ update_fields(box, params[:field])
346
+ else
347
+ unauthorised!
348
+ end
338
349
  end
339
350
  end
340
351
 
341
352
 
342
353
  post '/content/:id/position/:position' do
343
- content = content_for_request
344
- if content.box.writable?
345
- content.update_position(params[:position].to_i)
346
- json( {:message => 'OK'} )
347
- else
348
- unauthorised!
354
+ content_for_request(true) do |content|
355
+ if content.box.writable?(user)
356
+ content.update_position(params[:position].to_i)
357
+ json( {:message => 'OK'} )
358
+ else
359
+ unauthorised!
360
+ end
349
361
  end
350
362
  end
351
363
 
352
364
  post '/toggle/:id' do
353
- content = content_for_request
354
- if content.box && content.box.writable?
355
- content.toggle_visibility!
356
- json({:id => content.id, :hidden => (content.hidden? ? true : false) })
357
- else
358
- unauthorised!
359
- end
360
- end
361
-
362
-
363
- # Don't think this is actually used
364
- post '/file/upload/:id' do
365
- file = params['file']
366
- media_file = Spontaneous::Media.upload_path(file[:filename])
367
- FileUtils.mkdir_p(File.dirname(media_file))
368
- FileUtils.mv(file[:tempfile].path, media_file)
369
- json({ :id => params[:id], :src => Spontaneous::Media.to_urlpath(media_file), :path => media_file})
370
- end
371
-
372
-
373
- # TODO: DRY this up
374
- post '/file/replace/:id' do
375
- target = content_for_request
376
- file = params['file']
377
- field = target.fields.sid(params['field'])
378
- if target.field_writable?(field.name)
379
- # version = params[:version].to_i
380
- # if version == field.version
381
- field.unprocessed_value = file
382
- target.save
383
- json({ :id => target.id, :src => field.src, :version => field.version})
384
- # else
385
- # errors = [[field.schema_id.to_s, [field.version, field.conflicted_value]]]
386
- # [409, json(Hash[errors])]
387
- # end
388
- else
389
- unauthorised!
365
+ content_for_request(true) do |content|
366
+ if content.box && content.box.writable?(user)
367
+ content.toggle_visibility!
368
+ json({:id => content.id, :hidden => (content.hidden? ? true : false) })
369
+ else
370
+ unauthorised!
371
+ end
390
372
  end
391
373
  end
392
374
 
393
- post '/file/replace/:id/:box_id' do
394
- content, box = content_for_request
395
- target = box || content
396
- file = params[:file]
397
- field = target.fields.sid(params['field'])
398
- if target.field_writable?(field.name)
399
- # version = params[:version].to_i
400
- # if version == field.version
375
+ post '/file/replace/:id/?:box_id?' do
376
+ content_for_request(true) do |content, box|
377
+ target = box || content
378
+ file = params[:file]
379
+ field = target.fields.sid(params['field'])
380
+ if target.field_writable?(user, field.name)
381
+ # version = params[:version].to_i
382
+ # if version == field.version
401
383
  field.unprocessed_value = file
402
384
  content.save
403
- json({ :id => content.id, :src => field.src, :version => field.version})
404
- # else
405
- # errors = [[field.schema_id.to_s, [field.version, field.conflicted_value]]]
406
- # [409, json(Hash[errors])]
407
- # end
408
- else
409
- unauthorised!
385
+ json(field.export(user))
386
+ # else
387
+ # errors = [[field.schema_id.to_s, [field.version, field.conflicted_value]]]
388
+ # [409, json(Hash[errors])]
389
+ # end
390
+ else
391
+ unauthorised!
392
+ end
410
393
  end
411
394
  end
412
395
 
413
396
 
414
397
  post '/file/wrap/:id/:box_id' do
415
- content, box = content_for_request
416
- file = params['file']
417
- type = box.type_for_mime_type(file[:type])
418
- if type
419
- if box.writable?(type)
420
- position = 0
398
+ content_for_request(true) do |content, box|
399
+ file = params['file']
400
+ type = box.type_for_mime_type(file[:type])
401
+ if type
402
+ if box.writable?(user, type)
403
+ position = 0
404
+ instance = type.new
405
+ box.insert(position, instance)
406
+ field = instance.field_for_mime_type(file[:type])
407
+ field.unprocessed_value = file
408
+ instance.save
409
+ content.save
410
+ json({
411
+ :position => position,
412
+ :entry => instance.entry.export(user)
413
+ })
414
+ else
415
+ unauthorised!
416
+ end
417
+ end
418
+ end
419
+ end
420
+
421
+ post '/add/:id/:box_id/:type_name' do
422
+ content_for_request(true) do |content, box|
423
+ position = (params[:position] || 0).to_i
424
+ type = Spontaneous.schema[params[:type_name]]#.constantize
425
+ if box.writable?(user, type)
421
426
  instance = type.new
422
427
  box.insert(position, instance)
423
- field = instance.field_for_mime_type(file[:type])
424
- media_file = Spontaneous::Media.upload_path(file[:filename])
425
- FileUtils.mkdir_p(File.dirname(media_file))
426
- FileUtils.mv(file[:tempfile].path, media_file)
427
- field.unprocessed_value = media_file
428
428
  content.save
429
429
  json({
430
430
  :position => position,
431
- :entry => instance.entry.export
431
+ :entry => instance.entry.export(user)
432
432
  })
433
433
  else
434
434
  unauthorised!
@@ -436,95 +436,85 @@ module Spontaneous
436
436
  end
437
437
  end
438
438
 
439
- post '/add/:id/:box_id/:type_name' do
440
- content, box = content_for_request
441
- position = 0
442
- type = Spontaneous::Schema[params[:type_name]]#.constantize
443
- if box.writable?(type)
444
- instance = type.new
445
- box.insert(position, instance)
446
- content.save
447
- json({
448
- :position => position,
449
- :entry => instance.entry.export
450
- })
451
- else
452
- unauthorised!
453
- end
454
- end
455
-
456
439
  post '/destroy/:id' do
457
- content = content_for_request
458
- if content.box.writable?
459
- content.destroy
460
- json({})
461
- else
462
- unauthorised!
440
+ content_for_request(true) do |content|
441
+ if content.box.writable?(user)
442
+ content.destroy
443
+ json({})
444
+ else
445
+ unauthorised!
446
+ end
463
447
  end
464
448
  end
465
449
 
466
450
  post '/slug/:id' do
467
- content = content_for_request
468
- if params[:slug].nil? or params[:slug].empty?
469
- 406 # Not Acceptable
470
- else
471
- content.slug = params[:slug]
472
- if content.siblings.detect { |s| s.slug == content.slug }
473
- 409 # Conflict
451
+ content_for_request(true) do |content|
452
+ if params[:slug].nil? or params[:slug].empty?
453
+ 406 # Not Acceptable
474
454
  else
475
- content.save
476
- json({:path => content.path })
455
+ content.slug = params[:slug]
456
+ if content.siblings.detect { |s| s.slug == content.slug }
457
+ 409 # Conflict
458
+ else
459
+ content.save
460
+ json({:path => content.path, :slug => content.slug })
461
+ end
477
462
  end
478
463
  end
479
464
  end
480
465
 
481
466
  get '/slug/:id/unavailable' do
482
- content = content_for_request
483
- json(content.siblings.map { |c| c.slug })
467
+ content_for_request do |content|
468
+ json(content.siblings.map { |c| c.slug })
469
+ end
484
470
  end
485
471
 
486
472
  post '/uid/:id' do
487
473
  if user.developer?
488
- content = content_for_request
489
- content.uid = params[:uid]
490
- content.save
491
- json({:uid => content.uid })
474
+ content_for_request(true) do |content|
475
+ content.uid = params[:uid]
476
+ content.save
477
+ json({:uid => content.uid })
478
+ end
492
479
  else
493
480
  unauthorised!
494
481
  end
495
482
  end
496
483
 
497
- get '/targets/:schema_id' do
498
- klass = Spontaneous::Schema[params[:schema_id]]
484
+ get '/targets/:schema_id/:id/:box_id' do
485
+ klass = Spontaneous.schema[params[:schema_id]]
499
486
  if klass.alias?
500
- targets = klass.targets.map do |t|
501
- {
502
- :id => t.id,
503
- :title => t.alias_title,
504
- :icon => t.alias_icon_field.export
505
- }
487
+ content_for_request do |content, box|
488
+ targets = klass.targets(content, box).map do |t|
489
+ {
490
+ :id => t.id,
491
+ :title => t.alias_title,
492
+ :icon => t.exported_alias_icon
493
+ }
494
+ end
495
+ json(targets)
506
496
  end
507
- json(targets)
508
497
  end
509
498
  end
510
499
 
511
500
  post '/alias/:id/:box_id' do
512
- content, box = content_for_request
513
- type = Spontaneous::Schema[params[:alias_id]]
514
- position = 0
515
- if box.writable?(type)
516
- target = Spontaneous::Content[params[:target_id]]
517
- if target
518
- instance = type.create(:target => target)
519
- box.insert(position, instance)
520
- content.save
521
- json({
522
- :position => position,
523
- :entry => instance.entry.export
524
- })
501
+ content_for_request(true) do |content, box|
502
+ type = Spontaneous.schema[params[:alias_id]]
503
+ position = (params[:position] || 0).to_i
504
+ if box.writable?(user, type)
505
+ target = Spontaneous::Content[params[:target_id]]
506
+ if target
507
+ instance = type.create(:target => target)
508
+ box.insert(position, instance)
509
+ content.save
510
+ json({
511
+ :position => position,
512
+ :entry => instance.entry.export(user)
513
+ })
514
+ end
515
+ else
516
+ unauthorised!
525
517
  end
526
- else
527
- unauthorised!
528
518
  end
529
519
  end
530
520
 
@@ -552,10 +542,6 @@ module Spontaneous
552
542
  end
553
543
  end
554
544
 
555
- get '/publish/status' do
556
- json(Spontaneous::Site.publishing_status)
557
- end
558
-
559
545
  get '/shard/:sha1' do
560
546
  shard = Spontaneous.shard_path(params[:sha1])
561
547
  if ::File.file?(shard)
@@ -580,29 +566,26 @@ module Spontaneous
580
566
  end
581
567
  end
582
568
 
583
- post '/shard/replace/:id' do
584
- content = content_for_request
585
- replace_with_shard(content, content.id)
586
- end
587
-
588
- post '/shard/replace/:id/:box_id' do
589
- content, box = content_for_request
590
- replace_with_shard(box, content.id)
569
+ post '/shard/replace/:id/?:box_id?' do
570
+ content_for_request(true) do |content, box|
571
+ target = box || content
572
+ replace_with_shard(target, content.id)
573
+ end
591
574
  end
592
575
 
593
576
  def replace_with_shard(target, target_id)
594
577
  field = target.fields.sid(params[:field])
595
- if target.field_writable?(field.name)
578
+ if target.field_writable?(user, field.name)
596
579
  # version = params[:version].to_i
597
580
  # if version == field.version
598
- Spontaneous::Media.combine_shards(params[:shards]) do |combined|
599
- field.unprocessed_value = {
600
- :filename => params[:filename],
601
- :tempfile => combined
602
- }
603
- target.save
604
- end
605
- json({ :id => target_id, :src => field.src, :version => field.version})
581
+ Spontaneous::Media.combine_shards(params[:shards]) do |combined|
582
+ field.unprocessed_value = {
583
+ :filename => params[:filename],
584
+ :tempfile => combined
585
+ }
586
+ target.save
587
+ end
588
+ json(field.export(user))
606
589
  # else
607
590
  # errors = [[field.schema_id.to_s, [field.version, field.conflicted_value]]]
608
591
  # [409, json(Hash[errors])]
@@ -614,27 +597,28 @@ module Spontaneous
614
597
 
615
598
  # TODO: remove duplication here
616
599
  post '/shard/wrap/:id/:box_id' do
617
- content, box = content_for_request
618
- type = box.type_for_mime_type(params[:mime_type])
619
- if type
620
- if box.writable?(type)
621
- position = 0
622
- instance = type.new
623
- box.insert(position, instance)
624
- field = instance.field_for_mime_type(params[:mime_type])
625
- Spontaneous::Media.combine_shards(params[:shards]) do |combined|
626
- field.unprocessed_value = {
627
- :filename => params[:filename],
628
- :tempfile => combined
629
- }
630
- content.save
600
+ content_for_request(true) do |content, box|
601
+ type = box.type_for_mime_type(params[:mime_type])
602
+ if type
603
+ if box.writable?(user, type)
604
+ position = 0
605
+ instance = type.new
606
+ box.insert(position, instance)
607
+ field = instance.field_for_mime_type(params[:mime_type])
608
+ Spontaneous::Media.combine_shards(params[:shards]) do |combined|
609
+ field.unprocessed_value = {
610
+ :filename => params[:filename],
611
+ :tempfile => combined
612
+ }
613
+ content.save
614
+ end
615
+ json({
616
+ :position => position,
617
+ :entry => instance.entry.export(user)
618
+ })
619
+ else
620
+ unauthorised!
631
621
  end
632
- json({
633
- :position => position,
634
- :entry => instance.entry.export
635
- })
636
- else
637
- unauthorised!
638
622
  end
639
623
  end
640
624
  end
@@ -689,23 +673,19 @@ module Spontaneous
689
673
  end
690
674
 
691
675
  class Preview < Sinatra::Base
676
+ use Reloader if Site.config.reload_classes
692
677
  include Spontaneous::Rack::Public
678
+ helpers Spontaneous::Rack::UserHelpers
693
679
 
680
+ use CookieAuthentication
694
681
  use AroundPreview
695
- register Authentication
696
682
 
697
683
  set :views, Proc.new { Spontaneous.application_dir + '/views' }
698
684
 
699
- # I don't want this because I'm redirecting everything to /@spontaneous unless
700
- # we're logged in
701
- # requires_authentication! :except => ['/', '/favicon.ico']
702
-
703
685
  # redirect to /@spontaneous unless we're logged in
704
- before do
705
- unless user
706
- redirect NAMESPACE, 302
707
- end
708
- end
686
+ before {
687
+ redirect NAMESPACE, 302 unless user
688
+ }
709
689
 
710
690
 
711
691
  get '*' do
@@ -726,4 +706,3 @@ module Spontaneous
726
706
  end
727
707
  end
728
708
  end
729
-