spontaneous 0.1.0.alpha1 → 0.2.0.alpha1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
-