spontaneous 0.1.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 (556) hide show
  1. data/Gemfile +49 -0
  2. data/Gemfile.lock +146 -0
  3. data/LICENSE +0 -0
  4. data/README +0 -0
  5. data/Rakefile +284 -0
  6. data/Readme.markdown +7 -0
  7. data/application/css/add_alias_dialogue.scss +27 -0
  8. data/application/css/definitions.scss +249 -0
  9. data/application/css/developer.scss +9 -0
  10. data/application/css/editing.scss +649 -0
  11. data/application/css/login.scss +91 -0
  12. data/application/css/min/54ee0ed3c7fac7632bd5c020d69e9a2503e0c88c.css +1 -0
  13. data/application/css/min/c256adc144e2bdd0b0539356b04eb62db01e1dc3.css +1 -0
  14. data/application/css/popover.scss +335 -0
  15. data/application/css/schema_error.scss +90 -0
  16. data/application/css/spontaneous.scss +111 -0
  17. data/application/css/unsupported.scss +16 -0
  18. data/application/css/v2.scss +1606 -0
  19. data/application/css/variables.scss +80 -0
  20. data/application/js/add_alias_dialogue.js +59 -0
  21. data/application/js/add_home_dialogue.js +59 -0
  22. data/application/js/ajax.js +99 -0
  23. data/application/js/authentication.js +22 -0
  24. data/application/js/box.js +104 -0
  25. data/application/js/box_container.js +82 -0
  26. data/application/js/compatibility.js +132 -0
  27. data/application/js/conflicted_field_dialogue.js +92 -0
  28. data/application/js/content.js +224 -0
  29. data/application/js/content_area.js +44 -0
  30. data/application/js/dialogue.js +196 -0
  31. data/application/js/dom.js +71 -0
  32. data/application/js/edit_dialogue.js +137 -0
  33. data/application/js/edit_panel.js +232 -0
  34. data/application/js/editing.js +42 -0
  35. data/application/js/entry.js +13 -0
  36. data/application/js/extensions.js +104 -0
  37. data/application/js/field.js +4 -0
  38. data/application/js/field_preview.js +55 -0
  39. data/application/js/field_types/date_field.js +16 -0
  40. data/application/js/field_types/file_field.js +71 -0
  41. data/application/js/field_types/image_field.js +358 -0
  42. data/application/js/field_types/markdown_field.js +656 -0
  43. data/application/js/field_types/string_field.js +185 -0
  44. data/application/js/image.js +72 -0
  45. data/application/js/init.js +34 -0
  46. data/application/js/load.js +4 -0
  47. data/application/js/location.js +157 -0
  48. data/application/js/login.js +53 -0
  49. data/application/js/min/492a209de8ee955fa9c729a765377495001e11b1.js +17 -0
  50. data/application/js/min/80f684d77c940887a1d4a63e3a96102e993baa98.js +88 -0
  51. data/application/js/min/b8abf302a824c35385ff517b34111e1710ff3b37.js +2 -0
  52. data/application/js/min/c7140ec9475e5bf868b901e0621338d7d162358b.js +3 -0
  53. data/application/js/min/f07f2bd6630ee31e1c2288ec223383d8f0658ba6.js +2 -0
  54. data/application/js/page.js +43 -0
  55. data/application/js/page_browser.js +147 -0
  56. data/application/js/page_entry.js +47 -0
  57. data/application/js/popover.js +99 -0
  58. data/application/js/popover_view.js +56 -0
  59. data/application/js/preview.js +64 -0
  60. data/application/js/progress.js +358 -0
  61. data/application/js/properties.js +90 -0
  62. data/application/js/publish.js +187 -0
  63. data/application/js/require.js +129 -0
  64. data/application/js/sharded_upload.js +206 -0
  65. data/application/js/side_bar.js +30 -0
  66. data/application/js/spontaneous.js +6 -0
  67. data/application/js/state.js +64 -0
  68. data/application/js/status_bar.js +47 -0
  69. data/application/js/top_bar.js +368 -0
  70. data/application/js/types.js +98 -0
  71. data/application/js/upload.js +88 -0
  72. data/application/js/upload_manager.js +319 -0
  73. data/application/js/user.js +37 -0
  74. data/application/js/vendor/.DS_Store +0 -0
  75. data/application/js/vendor/JS.Class-2.1.5/CHANGELOG +283 -0
  76. data/application/js/vendor/JS.Class-2.1.5/MIT-LICENSE +30 -0
  77. data/application/js/vendor/JS.Class-2.1.5/README +30 -0
  78. data/application/js/vendor/JS.Class-2.1.5/min/command.js +1 -0
  79. data/application/js/vendor/JS.Class-2.1.5/min/comparable.js +1 -0
  80. data/application/js/vendor/JS.Class-2.1.5/min/constant_scope.js +1 -0
  81. data/application/js/vendor/JS.Class-2.1.5/min/core.js +1 -0
  82. data/application/js/vendor/JS.Class-2.1.5/min/decorator.js +1 -0
  83. data/application/js/vendor/JS.Class-2.1.5/min/enumerable.js +1 -0
  84. data/application/js/vendor/JS.Class-2.1.5/min/forwardable.js +1 -0
  85. data/application/js/vendor/JS.Class-2.1.5/min/hash.js +1 -0
  86. data/application/js/vendor/JS.Class-2.1.5/min/linked_list.js +1 -0
  87. data/application/js/vendor/JS.Class-2.1.5/min/loader.js +1 -0
  88. data/application/js/vendor/JS.Class-2.1.5/min/method_chain.js +1 -0
  89. data/application/js/vendor/JS.Class-2.1.5/min/observable.js +1 -0
  90. data/application/js/vendor/JS.Class-2.1.5/min/package.js +1 -0
  91. data/application/js/vendor/JS.Class-2.1.5/min/proxy.js +1 -0
  92. data/application/js/vendor/JS.Class-2.1.5/min/ruby.js +1 -0
  93. data/application/js/vendor/JS.Class-2.1.5/min/set.js +1 -0
  94. data/application/js/vendor/JS.Class-2.1.5/min/stack_trace.js +1 -0
  95. data/application/js/vendor/JS.Class-2.1.5/min/state.js +1 -0
  96. data/application/js/vendor/JS.Class-2.1.5/min/stdlib.js +16 -0
  97. data/application/js/vendor/JS.Class-2.1.5/src/command.js +93 -0
  98. data/application/js/vendor/JS.Class-2.1.5/src/comparable.js +37 -0
  99. data/application/js/vendor/JS.Class-2.1.5/src/constant_scope.js +48 -0
  100. data/application/js/vendor/JS.Class-2.1.5/src/core.js +1060 -0
  101. data/application/js/vendor/JS.Class-2.1.5/src/decorator.js +50 -0
  102. data/application/js/vendor/JS.Class-2.1.5/src/enumerable.js +505 -0
  103. data/application/js/vendor/JS.Class-2.1.5/src/forwardable.js +22 -0
  104. data/application/js/vendor/JS.Class-2.1.5/src/hash.js +334 -0
  105. data/application/js/vendor/JS.Class-2.1.5/src/linked_list.js +114 -0
  106. data/application/js/vendor/JS.Class-2.1.5/src/loader.js +553 -0
  107. data/application/js/vendor/JS.Class-2.1.5/src/method_chain.js +172 -0
  108. data/application/js/vendor/JS.Class-2.1.5/src/observable.js +55 -0
  109. data/application/js/vendor/JS.Class-2.1.5/src/package.js +472 -0
  110. data/application/js/vendor/JS.Class-2.1.5/src/proxy.js +58 -0
  111. data/application/js/vendor/JS.Class-2.1.5/src/ruby.js +44 -0
  112. data/application/js/vendor/JS.Class-2.1.5/src/set.js +332 -0
  113. data/application/js/vendor/JS.Class-2.1.5/src/stack_trace.js +151 -0
  114. data/application/js/vendor/JS.Class-2.1.5/src/state.js +95 -0
  115. data/application/js/vendor/JS.Class-2.1.5/src/stdlib.js +2612 -0
  116. data/application/js/vendor/crypto-2.3.0-crypto.js +160 -0
  117. data/application/js/vendor/crypto-2.3.0-sha1.js +91 -0
  118. data/application/js/vendor/diff_match_patch.js +2153 -0
  119. data/application/js/vendor/jquery-1.4.2.min.js +154 -0
  120. data/application/js/vendor/jquery-1.4.3.min.js +166 -0
  121. data/application/js/vendor/jquery-1.5.1.min.js +16 -0
  122. data/application/js/vendor/jquery-1.5.1rc1.min.js +24 -0
  123. data/application/js/vendor/jquery-1.6.2.min.js +18 -0
  124. data/application/js/vendor/jquery-ui-1.8.6.custom.min.js +265 -0
  125. data/application/js/vendor/jquery-ui-1.8.9.custom.min.js +415 -0
  126. data/application/js/vendor/jquery-ui-1.8.custom.min.js +106 -0
  127. data/application/js/vendor/jquery.hotkeys-0.7.9.js +248 -0
  128. data/application/js/vendor/jquery.hotkeys-0.7.9.min.js +19 -0
  129. data/application/js/vendor/jsdiff.js +169 -0
  130. data/application/js/views/box_view.js +229 -0
  131. data/application/js/views/page_piece_view.js +45 -0
  132. data/application/js/views/page_view.js +238 -0
  133. data/application/js/views/piece_view.js +178 -0
  134. data/application/js/views.js +110 -0
  135. data/application/static/editing-0-noise.png +0 -0
  136. data/application/static/editing-1-noise.png +0 -0
  137. data/application/static/editing-texture-1.png +0 -0
  138. data/application/static/editing-texture.png +0 -0
  139. data/application/static/editing-toolbar-shadow-bottom.png +0 -0
  140. data/application/static/editing-toolbar-shadow-top.png +0 -0
  141. data/application/static/favicon.ico +0 -0
  142. data/application/static/inner-glow.png +0 -0
  143. data/application/static/item-buttons.png +0 -0
  144. data/application/static/location-arrow.png +0 -0
  145. data/application/static/logo-400px-transparent.png +0 -0
  146. data/application/static/missing.png +0 -0
  147. data/application/static/orange-down-arrow.png +0 -0
  148. data/application/static/page-browser-next.png +0 -0
  149. data/application/static/paper-texture-dark.png +0 -0
  150. data/application/static/px.gif +0 -0
  151. data/application/static/select-arrow-root.png +0 -0
  152. data/application/static/select-arrow.png +0 -0
  153. data/application/static/slot-down-arrow.png +0 -0
  154. data/application/static/splash.png +0 -0
  155. data/application/static/spontaneous.png +0 -0
  156. data/application/static/spot.png +0 -0
  157. data/application/static/spot.svg +40 -0
  158. data/application/static/texture.png +0 -0
  159. data/application/views/index.erubis +46 -0
  160. data/application/views/login.erubis +69 -0
  161. data/application/views/schema_modification_error.html.erb +61 -0
  162. data/application/views/unsupported.erubis +23 -0
  163. data/bin/limit-upload +5 -0
  164. data/bin/spot +10 -0
  165. data/bin/unlimit-upload +3 -0
  166. data/config/nginx.conf +60 -0
  167. data/db/migrations/20100610142136_init.rb +66 -0
  168. data/db/migrations/20101130104334_timestamps.rb +44 -0
  169. data/db/migrations/20101202113205_site_publishing_flags.rb +12 -0
  170. data/db/migrations/20101206124543_aliases.rb +16 -0
  171. data/db/migrations/20110201133550_visibility.rb +27 -0
  172. data/db/migrations/20110209152710_users_and_groups.rb +58 -0
  173. data/db/migrations/20110215133910_boxes.rb +25 -0
  174. data/db/migrations/20110521114145_remove_slots_and_entries.rb +21 -0
  175. data/db/migrations/20110604192145_rename_schema_id_columns.rb +22 -0
  176. data/db/migrations/20110805141925_rename_site_to_state.rb +11 -0
  177. data/lib/cutaneous/context_helper.rb +82 -0
  178. data/lib/cutaneous/first_pass_parser.rb +23 -0
  179. data/lib/cutaneous/first_pass_renderer.rb +18 -0
  180. data/lib/cutaneous/parser_core.rb +18 -0
  181. data/lib/cutaneous/preview_context.rb +31 -0
  182. data/lib/cutaneous/preview_renderer.rb +15 -0
  183. data/lib/cutaneous/publish_context.rb +9 -0
  184. data/lib/cutaneous/renderer.rb +122 -0
  185. data/lib/cutaneous/request_context.rb +8 -0
  186. data/lib/cutaneous/second_pass_parser.rb +23 -0
  187. data/lib/cutaneous/second_pass_renderer.rb +18 -0
  188. data/lib/cutaneous.rb +47 -0
  189. data/lib/sequel/plugins/content_table_inheritance.rb +196 -0
  190. data/lib/sequel/plugins/yajl_serialization.rb +154 -0
  191. data/lib/spontaneous/application/feature.rb +9 -0
  192. data/lib/spontaneous/application/plugin.rb +13 -0
  193. data/lib/spontaneous/application.rb +8 -0
  194. data/lib/spontaneous/box.rb +232 -0
  195. data/lib/spontaneous/box_style.rb +64 -0
  196. data/lib/spontaneous/change.rb +107 -0
  197. data/lib/spontaneous/cli/adapter.rb +13 -0
  198. data/lib/spontaneous/cli/base.rb +184 -0
  199. data/lib/spontaneous/cli/console.rb +0 -0
  200. data/lib/spontaneous/cli/media.rb +13 -0
  201. data/lib/spontaneous/cli/server.rb +50 -0
  202. data/lib/spontaneous/cli/site.rb +46 -0
  203. data/lib/spontaneous/cli/sync.rb +42 -0
  204. data/lib/spontaneous/cli/tasks.rb +9 -0
  205. data/lib/spontaneous/cli.rb +83 -0
  206. data/lib/spontaneous/collections/box_set.rb +56 -0
  207. data/lib/spontaneous/collections/change_set.rb +43 -0
  208. data/lib/spontaneous/collections/entry_set.rb +83 -0
  209. data/lib/spontaneous/collections/field_set.rb +53 -0
  210. data/lib/spontaneous/collections/prototype_set.rb +131 -0
  211. data/lib/spontaneous/collections/style_set.rb +13 -0
  212. data/lib/spontaneous/config.rb +156 -0
  213. data/lib/spontaneous/constants.rb +24 -0
  214. data/lib/spontaneous/content.rb +113 -0
  215. data/lib/spontaneous/content_query.rb +17 -0
  216. data/lib/spontaneous/errors.rb +48 -0
  217. data/lib/spontaneous/extensions/array.rb +18 -0
  218. data/lib/spontaneous/extensions/class.rb +17 -0
  219. data/lib/spontaneous/extensions/hash.rb +18 -0
  220. data/lib/spontaneous/extensions/json.rb +26 -0
  221. data/lib/spontaneous/extensions/kernel.rb +7 -0
  222. data/lib/spontaneous/extensions/object.rb +30 -0
  223. data/lib/spontaneous/extensions/object_space.rb +12 -0
  224. data/lib/spontaneous/extensions/string.rb +44 -0
  225. data/lib/spontaneous/facet.rb +47 -0
  226. data/lib/spontaneous/field_types/date_field.rb +12 -0
  227. data/lib/spontaneous/field_types/field.rb +252 -0
  228. data/lib/spontaneous/field_types/image_field.rb +329 -0
  229. data/lib/spontaneous/field_types/markdown_field.rb +37 -0
  230. data/lib/spontaneous/field_types/string_field.rb +14 -0
  231. data/lib/spontaneous/field_types.rb +40 -0
  232. data/lib/spontaneous/generators/page/inline.html.cut +1 -0
  233. data/lib/spontaneous/generators/page/page.html.cut.tt +4 -0
  234. data/lib/spontaneous/generators/page/page.rb.tt +9 -0
  235. data/lib/spontaneous/generators/page.rb +38 -0
  236. data/lib/spontaneous/generators/site/.gitignore +4 -0
  237. data/lib/spontaneous/generators/site/Gemfile.tt +31 -0
  238. data/lib/spontaneous/generators/site/Rakefile.tt +6 -0
  239. data/lib/spontaneous/generators/site/config/back.ru +7 -0
  240. data/lib/spontaneous/generators/site/config/boot.rb +19 -0
  241. data/lib/spontaneous/generators/site/config/database.yml.tt +21 -0
  242. data/lib/spontaneous/generators/site/config/deploy.rb.tt +0 -0
  243. data/lib/spontaneous/generators/site/config/environment.rb.tt +8 -0
  244. data/lib/spontaneous/generators/site/config/environments/development.rb.tt +15 -0
  245. data/lib/spontaneous/generators/site/config/environments/production.rb.tt +5 -0
  246. data/lib/spontaneous/generators/site/config/front.ru +8 -0
  247. data/lib/spontaneous/generators/site/config/user_levels.yml +22 -0
  248. data/lib/spontaneous/generators/site/lib/site.rb.tt +4 -0
  249. data/lib/spontaneous/generators/site/lib/tasks/site.rake.tt +8 -0
  250. data/lib/spontaneous/generators/site/public/css/site.css +0 -0
  251. data/lib/spontaneous/generators/site/public/favicon.ico +0 -0
  252. data/lib/spontaneous/generators/site/public/js/.empty_directory +0 -0
  253. data/lib/spontaneous/generators/site/public/js/site.js +0 -0
  254. data/lib/spontaneous/generators/site/public/robots.txt +0 -0
  255. data/lib/spontaneous/generators/site/schema/.map +1 -0
  256. data/lib/spontaneous/generators/site/schema/page.rb.tt +8 -0
  257. data/lib/spontaneous/generators/site/schema/piece.rb.tt +4 -0
  258. data/lib/spontaneous/generators/site/templates/layouts/standard.html.cut.tt +13 -0
  259. data/lib/spontaneous/generators/site.rb +77 -0
  260. data/lib/spontaneous/generators.rb +23 -0
  261. data/lib/spontaneous/image_size.rb +117 -0
  262. data/lib/spontaneous/json.rb +33 -0
  263. data/lib/spontaneous/layout.rb +15 -0
  264. data/lib/spontaneous/loader.rb +280 -0
  265. data/lib/spontaneous/logger.rb +369 -0
  266. data/lib/spontaneous/media.rb +84 -0
  267. data/lib/spontaneous/page.rb +92 -0
  268. data/lib/spontaneous/page_controller.rb +18 -0
  269. data/lib/spontaneous/page_piece.rb +77 -0
  270. data/lib/spontaneous/paths.rb +30 -0
  271. data/lib/spontaneous/permissions/access_group.rb +50 -0
  272. data/lib/spontaneous/permissions/access_key.rb +35 -0
  273. data/lib/spontaneous/permissions/user.rb +167 -0
  274. data/lib/spontaneous/permissions/user_level.rb +177 -0
  275. data/lib/spontaneous/permissions.rb +55 -0
  276. data/lib/spontaneous/piece.rb +30 -0
  277. data/lib/spontaneous/plugins/aliases.rb +128 -0
  278. data/lib/spontaneous/plugins/allowed_types.rb +173 -0
  279. data/lib/spontaneous/plugins/application/facets.rb +25 -0
  280. data/lib/spontaneous/plugins/application/paths.rb +137 -0
  281. data/lib/spontaneous/plugins/application/render.rb +29 -0
  282. data/lib/spontaneous/plugins/application/serialisation.rb +16 -0
  283. data/lib/spontaneous/plugins/application/state.rb +86 -0
  284. data/lib/spontaneous/plugins/boxes.rb +84 -0
  285. data/lib/spontaneous/plugins/controllers.rb +52 -0
  286. data/lib/spontaneous/plugins/entries.rb +193 -0
  287. data/lib/spontaneous/plugins/entry.rb +51 -0
  288. data/lib/spontaneous/plugins/fields.rb +103 -0
  289. data/lib/spontaneous/plugins/instance_code.rb +18 -0
  290. data/lib/spontaneous/plugins/layouts.rb +87 -0
  291. data/lib/spontaneous/plugins/media.rb +41 -0
  292. data/lib/spontaneous/plugins/page/formats.rb +67 -0
  293. data/lib/spontaneous/plugins/page/request.rb +89 -0
  294. data/lib/spontaneous/plugins/page_search.rb +64 -0
  295. data/lib/spontaneous/plugins/page_tree.rb +25 -0
  296. data/lib/spontaneous/plugins/paths.rb +125 -0
  297. data/lib/spontaneous/plugins/permissions.rb +63 -0
  298. data/lib/spontaneous/plugins/prototypes.rb +84 -0
  299. data/lib/spontaneous/plugins/publishing.rb +255 -0
  300. data/lib/spontaneous/plugins/render.rb +24 -0
  301. data/lib/spontaneous/plugins/schema_hierarchy.rb +76 -0
  302. data/lib/spontaneous/plugins/schema_id.rb +60 -0
  303. data/lib/spontaneous/plugins/schema_title.rb +33 -0
  304. data/lib/spontaneous/plugins/serialisation.rb +67 -0
  305. data/lib/spontaneous/plugins/site/instance.rb +22 -0
  306. data/lib/spontaneous/plugins/site/map.rb +19 -0
  307. data/lib/spontaneous/plugins/site/publishing.rb +74 -0
  308. data/lib/spontaneous/plugins/site/revisions.rb +28 -0
  309. data/lib/spontaneous/plugins/site/selectors.rb +41 -0
  310. data/lib/spontaneous/plugins/site_map.rb +34 -0
  311. data/lib/spontaneous/plugins/styles.rb +119 -0
  312. data/lib/spontaneous/plugins/supertype.rb +11 -0
  313. data/lib/spontaneous/plugins/visibility.rb +151 -0
  314. data/lib/spontaneous/plugins.rb +20 -0
  315. data/lib/spontaneous/prototypes/box_prototype.rb +168 -0
  316. data/lib/spontaneous/prototypes/field_prototype.rb +112 -0
  317. data/lib/spontaneous/prototypes/layout_prototype.rb +17 -0
  318. data/lib/spontaneous/prototypes/style_prototype.rb +42 -0
  319. data/lib/spontaneous/proxy_object.rb +12 -0
  320. data/lib/spontaneous/publishing/fire_and_forget.rb +57 -0
  321. data/lib/spontaneous/publishing/immediate.rb +197 -0
  322. data/lib/spontaneous/publishing/threaded.rb +25 -0
  323. data/lib/spontaneous/publishing.rb +10 -0
  324. data/lib/spontaneous/rack/around_back.rb +44 -0
  325. data/lib/spontaneous/rack/around_front.rb +29 -0
  326. data/lib/spontaneous/rack/around_preview.rb +26 -0
  327. data/lib/spontaneous/rack/assets.rb +98 -0
  328. data/lib/spontaneous/rack/back.rb +729 -0
  329. data/lib/spontaneous/rack/front.rb +41 -0
  330. data/lib/spontaneous/rack/http.rb +18 -0
  331. data/lib/spontaneous/rack/media.rb +29 -0
  332. data/lib/spontaneous/rack/public.rb +232 -0
  333. data/lib/spontaneous/rack/reloader.rb +42 -0
  334. data/lib/spontaneous/rack/static.rb +25 -0
  335. data/lib/spontaneous/rack.rb +55 -0
  336. data/lib/spontaneous/render/context.rb +100 -0
  337. data/lib/spontaneous/render/development_renderer.rb +14 -0
  338. data/lib/spontaneous/render/engine.rb +19 -0
  339. data/lib/spontaneous/render/format/html.rb +5 -0
  340. data/lib/spontaneous/render/format.rb +70 -0
  341. data/lib/spontaneous/render/preview_renderer.rb +18 -0
  342. data/lib/spontaneous/render/published_renderer.rb +54 -0
  343. data/lib/spontaneous/render/publishing_renderer.rb +13 -0
  344. data/lib/spontaneous/render/renderer.rb +46 -0
  345. data/lib/spontaneous/render.rb +173 -0
  346. data/lib/spontaneous/revision.rb +7 -0
  347. data/lib/spontaneous/schema/schema_modification.rb +260 -0
  348. data/lib/spontaneous/schema/uid.rb +221 -0
  349. data/lib/spontaneous/schema.rb +295 -0
  350. data/lib/spontaneous/server.rb +65 -0
  351. data/lib/spontaneous/site.rb +87 -0
  352. data/lib/spontaneous/state.rb +53 -0
  353. data/lib/spontaneous/style.rb +144 -0
  354. data/lib/spontaneous/tasks/database.rake +9 -0
  355. data/lib/spontaneous/tasks.rb +5 -0
  356. data/lib/spontaneous/version.rb +6 -0
  357. data/lib/spontaneous.rb +179 -0
  358. data/spontaneous.gemspec.tmpl +66 -0
  359. data/test/disabled/test_slots.rb +287 -0
  360. data/test/experimental/test_formats.rb +92 -0
  361. data/test/experimental/test_plugins.rb +64 -0
  362. data/test/fixtures/application/css/test.less +5 -0
  363. data/test/fixtures/application/js/test.js +1 -0
  364. data/test/fixtures/application/static/favicon.ico +1 -0
  365. data/test/fixtures/application/static/test.html +1 -0
  366. data/test/fixtures/application/views/index.erubis +1 -0
  367. data/test/fixtures/back/public/test.html +1 -0
  368. data/test/fixtures/back/templates/layouts/standard.html.cut +1 -0
  369. data/test/fixtures/config/config/environment.rb +4 -0
  370. data/test/fixtures/config/config/environments/development.rb +13 -0
  371. data/test/fixtures/config/config/environments/production.rb +22 -0
  372. data/test/fixtures/config/config/environments/staging.rb +2 -0
  373. data/test/fixtures/example_application/Gemfile +6 -0
  374. data/test/fixtures/example_application/Gemfile.lock +76 -0
  375. data/test/fixtures/example_application/Rakefile +6 -0
  376. data/test/fixtures/example_application/config/back.rb +15 -0
  377. data/test/fixtures/example_application/config/back.ru +8 -0
  378. data/test/fixtures/example_application/config/back.yml +8 -0
  379. data/test/fixtures/example_application/config/boot.rb +16 -0
  380. data/test/fixtures/example_application/config/database.yml +24 -0
  381. data/test/fixtures/example_application/config/environment.rb +4 -0
  382. data/test/fixtures/example_application/config/environments/development.rb +16 -0
  383. data/test/fixtures/example_application/config/environments/production.rb +21 -0
  384. data/test/fixtures/example_application/config/environments/staging.rb +1 -0
  385. data/test/fixtures/example_application/config/front.rb +8 -0
  386. data/test/fixtures/example_application/config/front.ru +8 -0
  387. data/test/fixtures/example_application/config/front.yml +8 -0
  388. data/test/fixtures/example_application/config/schema.yml +48 -0
  389. data/test/fixtures/example_application/config/unicorn.rb +1 -0
  390. data/test/fixtures/example_application/config/user_levels.yml +19 -0
  391. data/test/fixtures/example_application/public/css/test.css +0 -0
  392. data/test/fixtures/example_application/public/favicon.ico +1 -0
  393. data/test/fixtures/example_application/public/js/test.js +0 -0
  394. data/test/fixtures/example_application/public/test.html +1 -0
  395. data/test/fixtures/example_application/schema/client_project.rb +18 -0
  396. data/test/fixtures/example_application/schema/client_projects.rb +8 -0
  397. data/test/fixtures/example_application/schema/home_page.rb +22 -0
  398. data/test/fixtures/example_application/schema/info_page.rb +13 -0
  399. data/test/fixtures/example_application/schema/inline_image.rb +11 -0
  400. data/test/fixtures/example_application/schema/page.rb +4 -0
  401. data/test/fixtures/example_application/schema/piece.rb +3 -0
  402. data/test/fixtures/example_application/schema/project.rb +21 -0
  403. data/test/fixtures/example_application/schema/project_image.rb +18 -0
  404. data/test/fixtures/example_application/schema/projects_page.rb +12 -0
  405. data/test/fixtures/example_application/schema/text.rb +8 -0
  406. data/test/fixtures/example_application/templates/client_project/images.html.cut +1 -0
  407. data/test/fixtures/example_application/templates/client_project.html.cut +4 -0
  408. data/test/fixtures/example_application/templates/client_projects.html.cut +6 -0
  409. data/test/fixtures/example_application/templates/info_page/inline.html.cut +0 -0
  410. data/test/fixtures/example_application/templates/inline_image.html.cut +1 -0
  411. data/test/fixtures/example_application/templates/layouts/home.html.cut +15 -0
  412. data/test/fixtures/example_application/templates/layouts/info.html.cut +3 -0
  413. data/test/fixtures/example_application/templates/layouts/project.html.cut +13 -0
  414. data/test/fixtures/example_application/templates/layouts/projects.html.cut +11 -0
  415. data/test/fixtures/example_application/templates/layouts/standard.html.cut +0 -0
  416. data/test/fixtures/example_application/templates/project/inline.html.cut +5 -0
  417. data/test/fixtures/example_application/templates/project.html.cut +5 -0
  418. data/test/fixtures/example_application/templates/project_image.html.cut +1 -0
  419. data/test/fixtures/example_application/templates/text.html.cut +1 -0
  420. data/test/fixtures/images/rose.greyscale.jpg +0 -0
  421. data/test/fixtures/images/rose.jpg +0 -0
  422. data/test/fixtures/images/size.gif +0 -0
  423. data/test/fixtures/images/size.jpg +0 -0
  424. data/test/fixtures/images/size.png24 +0 -0
  425. data/test/fixtures/images/size.png8 +0 -0
  426. data/test/fixtures/layouts/layouts/custom1.html.cut +1 -0
  427. data/test/fixtures/layouts/layouts/custom1.pdf.cut +0 -0
  428. data/test/fixtures/layouts/layouts/custom1.xml.cut +0 -0
  429. data/test/fixtures/layouts/layouts/custom2.html.cut +1 -0
  430. data/test/fixtures/layouts/layouts/custom3.html.cut +0 -0
  431. data/test/fixtures/layouts/layouts/standard.html.cut +1 -0
  432. data/test/fixtures/media/101/003/rose.jpg +0 -0
  433. data/test/fixtures/permissions/config/user_levels.yml +9 -0
  434. data/test/fixtures/permissions/media/image.jpg +0 -0
  435. data/test/fixtures/plugins/schema_plugin/init.rb +1 -0
  436. data/test/fixtures/plugins/schema_plugin/schema/external.rb +5 -0
  437. data/test/fixtures/plugins/schema_plugin/templates/external.html.cut +1 -0
  438. data/test/fixtures/plugins/schema_plugin/templates/from_plugin.html.cut +0 -0
  439. data/test/fixtures/plugins/schema_plugin/templates/layouts/from_plugin.html.cut +0 -0
  440. data/test/fixtures/public/templates/layouts/default.html.cut +1 -0
  441. data/test/fixtures/public/templates/layouts/default.pdf.cut +1 -0
  442. data/test/fixtures/public/templates/layouts/default.rss.cut +1 -0
  443. data/test/fixtures/public/templates/layouts/dynamic.html.cut +1 -0
  444. data/test/fixtures/public/templates/layouts/standard.html.cut +0 -0
  445. data/test/fixtures/schema/before.yml +24 -0
  446. data/test/fixtures/schema/resolvable.yml +12 -0
  447. data/test/fixtures/schema/schema.yml +7 -0
  448. data/test/fixtures/serialisation/class_hash.yaml.erb +53 -0
  449. data/test/fixtures/serialisation/root_hash.yaml.erb +184 -0
  450. data/test/fixtures/sharding/rose.jpg +0 -0
  451. data/test/fixtures/sharding/xaa +0 -0
  452. data/test/fixtures/sharding/xab +0 -0
  453. data/test/fixtures/sharding/xac +0 -0
  454. data/test/fixtures/sharding/xad +0 -0
  455. data/test/fixtures/sharding/xae +0 -0
  456. data/test/fixtures/sharding/xaf +0 -0
  457. data/test/fixtures/sharding/xag +0 -0
  458. data/test/fixtures/styles/box_a/runny.html.cut +0 -0
  459. data/test/fixtures/styles/box_a.html.cut +1 -0
  460. data/test/fixtures/styles/named2.html.cut +1 -0
  461. data/test/fixtures/styles/orange/apple.html.cut +1 -0
  462. data/test/fixtures/styles/template_class/named1.html.cut +1 -0
  463. data/test/fixtures/styles/template_class/results.html.cut +1 -0
  464. data/test/fixtures/styles/template_class/walky.html.cut +0 -0
  465. data/test/fixtures/styles/template_class.epub.cut +0 -0
  466. data/test/fixtures/styles/template_class.html.cut +1 -0
  467. data/test/fixtures/styles/template_class.pdf.cut +0 -0
  468. data/test/fixtures/styles/template_sub_class1.html.cut +1 -0
  469. data/test/fixtures/templates/aliases/a/a_style.html.cut +0 -0
  470. data/test/fixtures/templates/aliases/a/page.html.cut +0 -0
  471. data/test/fixtures/templates/aliases/a_alias/a_alias_style.html.cut +0 -0
  472. data/test/fixtures/templates/aliases/layouts/b.html.cut +1 -0
  473. data/test/fixtures/templates/aliases/layouts/b_alias.html.cut +1 -0
  474. data/test/fixtures/templates/aliases/layouts/c_alias.html.cut +1 -0
  475. data/test/fixtures/templates/boxes/blank_content/things.html.cut +1 -0
  476. data/test/fixtures/templates/boxes/my_box_class/christy.html.cut +1 -0
  477. data/test/fixtures/templates/boxes/thangs.html.cut +1 -0
  478. data/test/fixtures/templates/boxes/with_template_box.html.cut +1 -0
  479. data/test/fixtures/templates/content/include.html.cut +1 -0
  480. data/test/fixtures/templates/content/include_dir.html.cut +1 -0
  481. data/test/fixtures/templates/content/included.epub.cut +1 -0
  482. data/test/fixtures/templates/content/included.html.cut +1 -0
  483. data/test/fixtures/templates/content/partial/included.html.cut +1 -0
  484. data/test/fixtures/templates/content/preprocess.html.cut +1 -0
  485. data/test/fixtures/templates/content/second.html.cut +1 -0
  486. data/test/fixtures/templates/content/template.epub.cut +1 -0
  487. data/test/fixtures/templates/content/template.html.cut +1 -0
  488. data/test/fixtures/templates/default_style_class.html.cut +1 -0
  489. data/test/fixtures/templates/direct.html.cut +1 -0
  490. data/test/fixtures/templates/extended/grandparent.html.cut +10 -0
  491. data/test/fixtures/templates/extended/main.html.cut +6 -0
  492. data/test/fixtures/templates/extended/parent.html.cut +10 -0
  493. data/test/fixtures/templates/layouts/entries.html.cut +7 -0
  494. data/test/fixtures/templates/layouts/page_style.html.cut +1 -0
  495. data/test/fixtures/templates/layouts/params.html.cut +1 -0
  496. data/test/fixtures/templates/layouts/preview_render.html.cut +2 -0
  497. data/test/fixtures/templates/layouts/standard_page.html.cut +1 -0
  498. data/test/fixtures/templates/layouts/subdir_style.html.cut +1 -0
  499. data/test/fixtures/templates/layouts/template_params.html.cut +1 -0
  500. data/test/fixtures/templates/page_class/inline_style.html.cut +1 -0
  501. data/test/fixtures/templates/preview_render/inline.html.cut +0 -0
  502. data/test/fixtures/templates/publishing/layouts/dynamic.html.cut +1 -0
  503. data/test/fixtures/templates/publishing/layouts/static.html.cut +1 -0
  504. data/test/fixtures/templates/template_class/anonymous_style.html.cut +4 -0
  505. data/test/fixtures/templates/template_class/another_template.html.cut +0 -0
  506. data/test/fixtures/templates/template_class/complex_template.html.cut +6 -0
  507. data/test/fixtures/templates/template_class/complex_template.pdf.cut +6 -0
  508. data/test/fixtures/templates/template_class/default_template_style.html.cut +4 -0
  509. data/test/fixtures/templates/template_class/images_with_template.html.cut +5 -0
  510. data/test/fixtures/templates/template_class/slots_template.html.cut +5 -0
  511. data/test/fixtures/templates/template_class/slots_template.pdf.cut +5 -0
  512. data/test/fixtures/templates/template_class/this_template.epub.cut +1 -0
  513. data/test/fixtures/templates/template_class/this_template.html.cut +1 -0
  514. data/test/fixtures/templates/template_class/this_template.pdf.cut +1 -0
  515. data/test/fixtures/templates/with_default_style_class.html.cut +1 -0
  516. data/test/functional/test_application.rb +176 -0
  517. data/test/functional/test_back.rb +902 -0
  518. data/test/functional/test_front.rb +571 -0
  519. data/test/javascript/test_dom.rb +94 -0
  520. data/test/javascript/test_markdown.rb +97 -0
  521. data/test/slow/test_publishing.rb +987 -0
  522. data/test/slow/test_visibility.rb +250 -0
  523. data/test/support/custom_matchers.rb +77 -0
  524. data/test/support/timing.rb +23 -0
  525. data/test/test_helper.rb +164 -0
  526. data/test/test_javascript.rb +34 -0
  527. data/test/ui/test_page_editing.rb +167 -0
  528. data/test/ui_helper.rb +114 -0
  529. data/test/unit/test_alias.rb +254 -0
  530. data/test/unit/test_authentication.rb +510 -0
  531. data/test/unit/test_boxes.rb +497 -0
  532. data/test/unit/test_config.rb +156 -0
  533. data/test/unit/test_content.rb +221 -0
  534. data/test/unit/test_content_inheritance.rb +103 -0
  535. data/test/unit/test_extensions.rb +14 -0
  536. data/test/unit/test_fields.rb +392 -0
  537. data/test/unit/test_generators.rb +97 -0
  538. data/test/unit/test_image_size.rb +25 -0
  539. data/test/unit/test_images.rb +265 -0
  540. data/test/unit/test_layouts.rb +111 -0
  541. data/test/unit/test_logger.rb +80 -0
  542. data/test/unit/test_media.rb +70 -0
  543. data/test/unit/test_page.rb +244 -0
  544. data/test/unit/test_permissions.rb +834 -0
  545. data/test/unit/test_piece.rb +80 -0
  546. data/test/unit/test_prototype_set.rb +192 -0
  547. data/test/unit/test_prototypes.rb +102 -0
  548. data/test/unit/test_render.rb +359 -0
  549. data/test/unit/test_schema.rb +1009 -0
  550. data/test/unit/test_serialisation.rb +215 -0
  551. data/test/unit/test_site.rb +145 -0
  552. data/test/unit/test_structure.rb +85 -0
  553. data/test/unit/test_styles.rb +417 -0
  554. data/test/unit/test_templates.rb +224 -0
  555. data/test/unit/test_type_hierarchy.rb +28 -0
  556. metadata +1017 -0
@@ -0,0 +1,729 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'sass'
4
+ require 'less'
5
+
6
+ module Spontaneous
7
+ module Rack
8
+ module Back
9
+ include Assets
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
94
+ end
95
+
96
+
97
+ def self.application
98
+ app = ::Rack::Builder.new do
99
+ use ::Rack::Lint
100
+ use ::Rack::ShowExceptions if Spontaneous.development?
101
+
102
+ use Spontaneous::Rack::Static, :root => Spontaneous.root / "public",
103
+ :urls => %w[/],
104
+ :try => ['.html', 'index.html', '/index.html']
105
+
106
+
107
+ map NAMESPACE do
108
+ use Spontaneous::Rack::Static, :root => Spontaneous.application_dir, :urls => %W(/static /js)
109
+ use AssetsHandler
110
+ use UnsupportedBrowserHandler
111
+ use SchemaModification
112
+ run EditingInterface
113
+ end
114
+
115
+ map "/media" do
116
+ run Spontaneous::Rack::Media.new
117
+ end
118
+
119
+ map "/" do
120
+ run Preview
121
+ end
122
+ end
123
+ end
124
+
125
+ class EditingBase < ServerBase
126
+ set :views, Proc.new { Spontaneous.application_dir + '/views' }
127
+
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
152
+
153
+ def json(response)
154
+ content_type 'application/json', :charset => 'utf-8'
155
+ response.serialise_http
156
+ end
157
+ end
158
+
159
+ class UnsupportedBrowserHandler < EditingBase
160
+ get '/unsupported' do
161
+ erubis :unsupported
162
+ end
163
+ end
164
+
165
+ class AuthenticatedHandler < EditingBase
166
+
167
+ use AroundBack
168
+ register Authentication
169
+ requires_authentication! :except_all => [%r(^#{NAMESPACE}/unsupported)], :except_key => [%r(^#{NAMESPACE}/?$)]
170
+ end
171
+
172
+ class SchemaModification < AuthenticatedHandler
173
+
174
+ post "/schema/delete" do
175
+ begin
176
+ Spontaneous::Schema.apply_fix(:delete, params[:uid])
177
+ rescue Spot::SchemaModificationError # ignore remaining errors - they will be fixed later
178
+ end
179
+ redirect(params[:origin])
180
+ end
181
+
182
+ post "/schema/rename" do
183
+ begin
184
+ Spontaneous::Schema.apply_fix(:rename, params[:uid], params[:ref])
185
+ rescue Spot::SchemaModificationError # ignore remaining errors - they will be fixed later
186
+ end
187
+ redirect(params[:origin])
188
+ end
189
+
190
+ end
191
+
192
+ class EditingInterface < AuthenticatedHandler
193
+ use Reloader if Site.config.reload_classes
194
+
195
+ set :views, Proc.new { Spontaneous.application_dir + '/views' }
196
+
197
+
198
+ def update_fields(model, field_data)
199
+ conflicts = []
200
+ if field_data
201
+ field_data.each do |id, values|
202
+ field = model.fields.sid(id)
203
+ if model.field_writable?(field.name.to_sym)
204
+ # version = values.delete("version").to_i
205
+ # if version == field.version
206
+ field.update(values)
207
+ # else
208
+ # conflicts << [field, values]
209
+ # end
210
+ else
211
+ unauthorised!
212
+ end
213
+ end
214
+ end
215
+ if conflicts.empty?
216
+ if model.save
217
+ json(model)
218
+ end
219
+ else
220
+ errors = conflicts.map do |field, new_value|
221
+ [field.schema_id.to_s, [field.version, field.conflicted_value, new_value["unprocessed_value"]]]
222
+ end
223
+ [409, json(Hash[errors])]
224
+ end
225
+ end
226
+
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]
233
+ else
234
+ content
235
+ end
236
+ end
237
+
238
+
239
+ get '/?' do
240
+ erubis :index
241
+ end
242
+
243
+ get '/root' do
244
+ json Site.root
245
+ end
246
+
247
+ get '/user' do
248
+ json(user)
249
+ end
250
+ # TODO: check for perms on the particular bit of content
251
+ # and pass user level into returned JSON
252
+ get '/page/:id' do
253
+ json(content_for_request)
254
+ end
255
+
256
+ get '/types' do
257
+ json Schema
258
+ end
259
+
260
+ # get '/type/:type' do
261
+ # klass = params[:type].gsub(/\\./, "::").constantize
262
+ # json klass
263
+ # end
264
+
265
+ get '/map' do
266
+ json Site.map
267
+ end
268
+
269
+ get '/map/:id' do
270
+ map = Site.map(params[:id])
271
+ if map
272
+ json(map)
273
+ else
274
+ 404
275
+ end
276
+ end
277
+
278
+ get '/location*' do
279
+ if Page.count == 0
280
+ 406
281
+ else
282
+ path = params[:splat].first
283
+ page = Site[path]
284
+ json Site.map(page.id)
285
+ end
286
+ end
287
+
288
+ post '/root' do
289
+ if Site.root.nil?
290
+ type = Spontaneous::Schema[params[:type]]
291
+ root = type.create(:title => "Home")
292
+ Spontaneous::Change.push(root)
293
+ json({:id => root.id})
294
+ else
295
+ 403
296
+ end
297
+ end
298
+
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)
307
+ end
308
+
309
+ def generate_conflict_list(content)
310
+ field_versions = params[:fields]
311
+ conflicts = []
312
+ field_versions.each do |schema_id, version|
313
+ field = content.fields.sid(schema_id)
314
+ unless field.version == version.to_i
315
+ conflicts << field
316
+ end
317
+ end
318
+ if conflicts.empty?
319
+ 200
320
+ else
321
+ errors = conflicts.map do |field|
322
+ [field.schema_id.to_s, [field.version, field.conflicted_value]]
323
+ end
324
+ [409, json(Hash[errors])]
325
+ end
326
+ end
327
+
328
+ post '/save/:id' do
329
+ update_fields(content_for_request, params[:field])
330
+ end
331
+
332
+ 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!
338
+ end
339
+ end
340
+
341
+
342
+ 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!
349
+ end
350
+ end
351
+
352
+ 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!
390
+ end
391
+ end
392
+
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
401
+ field.unprocessed_value = file
402
+ 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!
410
+ end
411
+ end
412
+
413
+
414
+ 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
421
+ instance = type.new
422
+ 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
+ content.save
429
+ json({
430
+ :position => position,
431
+ :entry => instance.entry.export
432
+ })
433
+ else
434
+ unauthorised!
435
+ end
436
+ end
437
+ end
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
+ post '/destroy/:id' do
457
+ content = content_for_request
458
+ if content.box.writable?
459
+ content.destroy
460
+ json({})
461
+ else
462
+ unauthorised!
463
+ end
464
+ end
465
+
466
+ 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
474
+ else
475
+ content.save
476
+ json({:path => content.path })
477
+ end
478
+ end
479
+ end
480
+
481
+ get '/slug/:id/unavailable' do
482
+ content = content_for_request
483
+ json(content.siblings.map { |c| c.slug })
484
+ end
485
+
486
+ post '/uid/:id' do
487
+ if user.developer?
488
+ content = content_for_request
489
+ content.uid = params[:uid]
490
+ content.save
491
+ json({:uid => content.uid })
492
+ else
493
+ unauthorised!
494
+ end
495
+ end
496
+
497
+ get '/targets/:schema_id' do
498
+ klass = Spontaneous::Schema[params[:schema_id]]
499
+ 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
+ }
506
+ end
507
+ json(targets)
508
+ end
509
+ end
510
+
511
+ 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
+ })
525
+ end
526
+ else
527
+ unauthorised!
528
+ end
529
+ end
530
+
531
+ get '/publish/changes' do
532
+ if user.level.can_publish?
533
+ json(Change)
534
+ else
535
+ unauthorised!
536
+ end
537
+ end
538
+
539
+ post '/publish/publish' do
540
+ ids = params[:change_set_ids]
541
+ ids = ids.blank? ? [] : ids
542
+ change_sets = ids.map(&:to_i)
543
+ if change_sets.empty?
544
+ 400
545
+ else
546
+ if user.level.can_publish?
547
+ Site.publish_changes(change_sets)
548
+ json({})
549
+ else
550
+ unauthorised!
551
+ end
552
+ end
553
+ end
554
+
555
+ get '/publish/status' do
556
+ json(Spontaneous::Site.publishing_status)
557
+ end
558
+
559
+ get '/shard/:sha1' do
560
+ shard = Spontaneous.shard_path(params[:sha1])
561
+ if ::File.file?(shard)
562
+ # touch the shard file so that clean up routines can delete unmodified files
563
+ # without affecting any uploads in progresss
564
+ FileUtils.touch(shard)
565
+ 200
566
+ else
567
+ 404
568
+ end
569
+ end
570
+
571
+ post '/shard/:sha1' do
572
+ file = params[:file]
573
+ uploaded_hash = Spontaneous::Media.digest(file[:tempfile].path)
574
+ if uploaded_hash == params[:sha1] # rand(10000) % 2 == 0 # use to test shard re-uploading
575
+ shard_path = Spontaneous.shard_path(params[:sha1])
576
+ FileUtils.mv(file[:tempfile].path, shard_path)
577
+ 200
578
+ else
579
+ ::Rack::Utils.status_code(:conflict) #409
580
+ end
581
+ end
582
+
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)
591
+ end
592
+
593
+ def replace_with_shard(target, target_id)
594
+ field = target.fields.sid(params[:field])
595
+ if target.field_writable?(field.name)
596
+ # version = params[:version].to_i
597
+ # 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})
606
+ # else
607
+ # errors = [[field.schema_id.to_s, [field.version, field.conflicted_value]]]
608
+ # [409, json(Hash[errors])]
609
+ # end
610
+ else
611
+ unauthorised!
612
+ end
613
+ end
614
+
615
+ # TODO: remove duplication here
616
+ 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
631
+ end
632
+ json({
633
+ :position => position,
634
+ :entry => instance.entry.export
635
+ })
636
+ else
637
+ unauthorised!
638
+ end
639
+ end
640
+ end
641
+ # get "/favicon.ico" do
642
+ # puts "Editing/favicon"
643
+ # send_file(Spontaneous.static_dir / "favicon.ico")
644
+ # end
645
+
646
+
647
+ end # EditingInterface
648
+
649
+
650
+
651
+ # Assets are separata from the main editing handlers so that I can still access them
652
+ # in the case of a Schema modification error
653
+ class AssetsHandler < ::Sinatra::Base
654
+ get '/static/*' do
655
+ send_file(Spontaneous.static_dir / params[:splat].first)
656
+ end
657
+
658
+
659
+ get '/js/*' do
660
+ content_type :js
661
+ File.read(Spontaneous.js_dir / params[:splat].first)
662
+ end
663
+
664
+ get '/css/*' do
665
+ # need to check for file existing and just send that
666
+ # though production server would handle that I suppose
667
+ # as long as I release pre-compliled CSS files
668
+ file = params[:splat].first
669
+ if file =~ /\.css$/
670
+ css_file = Spontaneous.css_dir / file
671
+ if File.exists?(css_file)
672
+ send_file(css_file)
673
+ else
674
+ content_type :css
675
+ sass_template = Spontaneous.css_dir / File.basename(file, ".css") + ".scss"
676
+ less_template = Spontaneous.css_dir / File.basename(file, ".css") + ".less"
677
+ if File.exists?(sass_template)
678
+ Sass::Engine.for_file(sass_template, :load_paths => [Spontaneous.css_dir], :filename => sass_template, :cache => false).render
679
+ elsif File.exists?(less_template)
680
+ Less::Engine.new(File.new(less_template)).to_css
681
+ else
682
+ raise Sinatra::NotFound
683
+ end
684
+ end
685
+ else
686
+ send_file(Spontaneous.css_dir / file)
687
+ end
688
+ end
689
+ end
690
+
691
+ class Preview < Sinatra::Base
692
+ include Spontaneous::Rack::Public
693
+
694
+ use AroundPreview
695
+ register Authentication
696
+
697
+ set :views, Proc.new { Spontaneous.application_dir + '/views' }
698
+
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
+ # redirect to /@spontaneous unless we're logged in
704
+ before do
705
+ unless user
706
+ redirect NAMESPACE, 302
707
+ end
708
+ end
709
+
710
+
711
+ get '*' do
712
+ path = params[:splat][0]
713
+
714
+ render_path(path)
715
+ end
716
+
717
+ def render_page(page, format = :html, local_params = {})
718
+ now = Time.now.to_formatted_s(:rfc822)
719
+ response.headers[HTTP_EXPIRES] = now
720
+ response.headers[HTTP_LAST_MODIFIED] = now
721
+ response.headers[HTTP_CACHE_CONTROL] = HTTP_NO_CACHE
722
+ super
723
+ end
724
+ end # Preview
725
+
726
+ end
727
+ end
728
+ end
729
+