spontaneous 0.2.0.alpha2 → 0.2.0.alpha3

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 (404) hide show
  1. data/Gemfile +3 -40
  2. data/Rakefile +29 -41
  3. data/Readme.markdown +21 -8
  4. data/application/css/add_alias_dialogue.css.scss +79 -0
  5. data/application/css/{v2.scss → core.css.scss} +537 -550
  6. data/application/css/{definitions.scss → definitions.css.scss} +121 -33
  7. data/application/css/dialogue.css.scss +409 -0
  8. data/application/css/{editing.scss → editing.css.scss} +25 -262
  9. data/application/css/font.css.scss +8 -0
  10. data/application/css/jquery-ui-1.8.21.custom/images/ui-bg_diagonals-thick_18_b81900_40x40.png +0 -0
  11. data/application/css/jquery-ui-1.8.21.custom/images/ui-bg_diagonals-thick_20_666666_40x40.png +0 -0
  12. data/application/css/jquery-ui-1.8.21.custom/images/ui-bg_flat_10_000000_40x100.png +0 -0
  13. data/application/css/jquery-ui-1.8.21.custom/images/ui-bg_glass_100_f6f6f6_1x400.png +0 -0
  14. data/application/css/jquery-ui-1.8.21.custom/images/ui-bg_glass_100_fdf5ce_1x400.png +0 -0
  15. data/application/css/jquery-ui-1.8.21.custom/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
  16. data/application/css/jquery-ui-1.8.21.custom/images/ui-bg_gloss-wave_35_f6a828_500x100.png +0 -0
  17. data/application/css/jquery-ui-1.8.21.custom/images/ui-bg_highlight-soft_100_eeeeee_1x100.png +0 -0
  18. data/application/css/jquery-ui-1.8.21.custom/images/ui-bg_highlight-soft_75_ffe45c_1x100.png +0 -0
  19. data/application/css/jquery-ui-1.8.21.custom/images/ui-icons_222222_256x240.png +0 -0
  20. data/application/css/jquery-ui-1.8.21.custom/images/ui-icons_228ef1_256x240.png +0 -0
  21. data/application/css/jquery-ui-1.8.21.custom/images/ui-icons_ef8c08_256x240.png +0 -0
  22. data/application/css/jquery-ui-1.8.21.custom/images/ui-icons_ffd27a_256x240.png +0 -0
  23. data/application/css/jquery-ui-1.8.21.custom/images/ui-icons_ffffff_256x240.png +0 -0
  24. data/application/css/jquery-ui-1.8.21.custom/ui-lightness.css.scss +565 -0
  25. data/application/css/{login.scss → login.css.scss} +4 -4
  26. data/application/css/meta.css.scss +331 -0
  27. data/application/css/{popover.scss → popover.css.scss} +9 -8
  28. data/application/css/{schema_error.scss → schema_error.css.scss} +4 -4
  29. data/application/css/spontaneous.css +5 -0
  30. data/application/css/unsupported.css.scss +42 -0
  31. data/application/js/add_alias_dialogue.js +72 -10
  32. data/application/js/ajax.js +50 -16
  33. data/application/js/compatibility.js +19 -15
  34. data/application/js/content.js +39 -36
  35. data/application/js/content_area.js +53 -5
  36. data/application/js/dialogue.js +17 -0
  37. data/application/js/dom.js +1 -1
  38. data/application/js/edit_panel.js +23 -8
  39. data/application/js/extensions.js +7 -2
  40. data/application/js/field_preview.js +2 -1
  41. data/application/js/field_types/date_field.js +11 -8
  42. data/application/js/field_types/file_field.js +188 -9
  43. data/application/js/field_types/image_field.js +46 -29
  44. data/application/js/field_types/markdown_field.js +57 -14
  45. data/application/js/field_types/select_field.js +72 -0
  46. data/application/js/field_types/string_field.js +8 -0
  47. data/application/js/field_types/webvideo_field.js +8 -0
  48. data/application/js/init.js +1 -7
  49. data/application/js/location.js +41 -13
  50. data/application/js/login.js +3 -0
  51. data/application/js/meta_view/user_admin.js +543 -0
  52. data/application/js/meta_view.js +1 -0
  53. data/application/js/metadata.js +20 -0
  54. data/application/js/page.js +17 -3
  55. data/application/js/page_browser.js +40 -30
  56. data/application/js/page_entry.js +4 -0
  57. data/application/js/panel/root_menu.js +66 -0
  58. data/application/js/popover.js +2 -2
  59. data/application/js/preview.js +1 -1
  60. data/application/js/publish.js +138 -30
  61. data/application/js/services.js +43 -0
  62. data/application/js/sharded_upload.js +2 -2
  63. data/application/js/spontaneous.js +64 -5
  64. data/application/js/state.js +20 -9
  65. data/application/js/top_bar.js +273 -150
  66. data/application/js/types.js +20 -15
  67. data/application/js/upload.js +1 -2
  68. data/application/js/user.js +18 -4
  69. data/application/js/vendor/date.js +104 -0
  70. data/application/js/vendor/jquery-ui-1.8.18.custom.min.js +356 -0
  71. data/application/js/views/box_view.js +15 -8
  72. data/application/js/views/page_piece_view.js +11 -8
  73. data/application/js/views/page_view.js +21 -3
  74. data/application/js/views/piece_view.js +19 -10
  75. data/application/js/views.js +3 -0
  76. data/application/static/chromelogo-200x52-45c7cbc93be080bea342df5fcda2ef8b.png +0 -0
  77. data/application/static/dot-texture-9eea29e4ac7ad9a772285252078ec127.png +0 -0
  78. data/application/static/editing-textarea-resize-s-b5af62365eb7e19ea4beccf7242d508e.png +0 -0
  79. data/application/static/editing-texture-1-42b06a3439752490988f30f2a06d5e7c.png +0 -0
  80. data/application/static/font/fontawesome-webfont-5c5c21100a346972a82c34c5e96ffcfe.ttf +0 -0
  81. data/application/static/loop_alt1-white-7894b458528e92216196cae26b4c2c96.svg +12 -0
  82. data/application/static/spontaneous-states-e91ec89db525c62da0105b97cdd781ce.png +0 -0
  83. data/application/views/index.erb +2 -2
  84. data/application/views/login.erb +4 -8
  85. data/application/views/schema_modification_error.html.erb +1 -4
  86. data/application/views/unsupported.erb +9 -7
  87. data/bin/spot +1 -1
  88. data/db/migrations/20110209152710_users_and_groups.rb +10 -10
  89. data/db/migrations/20120305112647_site_modification_time.rb +11 -0
  90. data/db/migrations/20120418153903_add_ownership_of_content.rb +14 -0
  91. data/db/migrations/20120423175416_add_pending_modifications.rb +21 -0
  92. data/db/migrations/20120525164947_add_field_versions.rb +27 -0
  93. data/lib/sequel/plugins/scoped_table_name.rb +10 -1
  94. data/lib/spontaneous/asset/file.rb +25 -0
  95. data/lib/spontaneous/asset/source.rb +28 -0
  96. data/lib/spontaneous/asset.rb +75 -0
  97. data/lib/spontaneous/box.rb +6 -4
  98. data/lib/spontaneous/capistrano/deploy.rb +47 -0
  99. data/lib/spontaneous/capistrano/sync.rb +81 -0
  100. data/lib/spontaneous/capistrano.rb +13 -0
  101. data/lib/spontaneous/change.rb +60 -68
  102. data/lib/spontaneous/cli/assets.rb +29 -0
  103. data/lib/spontaneous/cli/base.rb +0 -48
  104. data/lib/spontaneous/cli/console.rb +1 -1
  105. data/lib/spontaneous/cli/server.rb +6 -4
  106. data/lib/spontaneous/cli/site.rb +28 -17
  107. data/lib/spontaneous/cli/user.rb +107 -0
  108. data/lib/spontaneous/cli.rb +100 -4
  109. data/lib/spontaneous/collections/entry_set.rb +2 -2
  110. data/lib/spontaneous/collections/field_set.rb +48 -22
  111. data/lib/spontaneous/collections/prototype_set.rb +20 -13
  112. data/lib/spontaneous/constants.rb +3 -0
  113. data/lib/spontaneous/content.rb +22 -5
  114. data/lib/spontaneous/errors.rb +3 -3
  115. data/lib/spontaneous/extensions/array.rb +12 -0
  116. data/lib/spontaneous/extensions/enumerable.rb +30 -0
  117. data/lib/spontaneous/extensions/nil.rb +3 -1
  118. data/lib/spontaneous/extensions/string.rb +16 -0
  119. data/lib/spontaneous/field_types/date_field.rb +45 -2
  120. data/lib/spontaneous/field_types/field.rb +42 -9
  121. data/lib/spontaneous/field_types/file_field.rb +68 -0
  122. data/lib/spontaneous/field_types/image_field.rb +63 -0
  123. data/lib/spontaneous/field_types/select_field.rb +109 -0
  124. data/lib/spontaneous/field_types/string_field.rb +6 -1
  125. data/lib/spontaneous/field_types/webvideo_field.rb +85 -27
  126. data/lib/spontaneous/field_types.rb +1 -1
  127. data/lib/spontaneous/field_version.rb +8 -0
  128. data/lib/spontaneous/generators/page.rb +1 -1
  129. data/lib/spontaneous/generators/site/Capfile.tt +1 -4
  130. data/lib/spontaneous/generators/site/Gemfile.tt +13 -6
  131. data/lib/spontaneous/generators/site/config/deploy.rb.tt +28 -0
  132. data/lib/spontaneous/generators/site/config/environments/production.rb.tt +7 -2
  133. data/lib/spontaneous/generators/site.rb +1 -0
  134. data/lib/spontaneous/json.rb +34 -11
  135. data/lib/spontaneous/layout.rb +22 -0
  136. data/lib/spontaneous/media/file.rb +4 -0
  137. data/lib/spontaneous/output/assets/compression.rb +58 -0
  138. data/lib/spontaneous/output/assets.rb +32 -0
  139. data/lib/spontaneous/output/context.rb +179 -0
  140. data/lib/spontaneous/output/format/html.rb +5 -0
  141. data/lib/spontaneous/output/format/plain.rb +5 -0
  142. data/lib/spontaneous/output/format.rb +180 -0
  143. data/lib/spontaneous/output/helpers/classes_helper.rb +18 -0
  144. data/lib/spontaneous/{render/helpers/html_helper.rb → output/helpers/conditional_comment_helper.rb} +3 -12
  145. data/lib/spontaneous/output/helpers/script_helper.rb +73 -0
  146. data/lib/spontaneous/output/helpers/stylesheet_helper.rb +61 -0
  147. data/lib/spontaneous/output/helpers.rb +28 -0
  148. data/lib/spontaneous/output/template/engine.rb +59 -0
  149. data/lib/spontaneous/output/template/renderer.rb +153 -0
  150. data/lib/spontaneous/output/template.rb +21 -0
  151. data/lib/spontaneous/output.rb +163 -0
  152. data/lib/spontaneous/page.rb +2 -1
  153. data/lib/spontaneous/paths.rb +6 -1
  154. data/lib/spontaneous/permissions/access_key.rb +5 -5
  155. data/lib/spontaneous/permissions/user.rb +81 -35
  156. data/lib/spontaneous/permissions/user_level.rb +26 -22
  157. data/lib/spontaneous/plugins/aliases.rb +76 -12
  158. data/lib/spontaneous/plugins/allowed_types.rb +60 -18
  159. data/lib/spontaneous/plugins/application/paths.rb +0 -28
  160. data/lib/spontaneous/plugins/application/system.rb +35 -0
  161. data/lib/spontaneous/plugins/boxes.rb +4 -0
  162. data/lib/spontaneous/plugins/content_groups.rb +16 -0
  163. data/lib/spontaneous/plugins/entries.rb +50 -30
  164. data/lib/spontaneous/plugins/fields.rb +15 -0
  165. data/lib/spontaneous/plugins/layouts.rb +8 -2
  166. data/lib/spontaneous/plugins/modifications.rb +228 -0
  167. data/lib/spontaneous/plugins/page/formats.rb +67 -42
  168. data/lib/spontaneous/plugins/page/request.rb +10 -0
  169. data/lib/spontaneous/plugins/page/site_timestamps.rb +28 -0
  170. data/lib/spontaneous/plugins/paths.rb +15 -3
  171. data/lib/spontaneous/plugins/publishing.rb +124 -45
  172. data/lib/spontaneous/plugins/render.rb +13 -8
  173. data/lib/spontaneous/plugins/schema_hierarchy.rb +18 -6
  174. data/lib/spontaneous/plugins/site/helpers.rb +53 -0
  175. data/lib/spontaneous/plugins/site/hooks.rb +50 -0
  176. data/lib/spontaneous/plugins/site/map.rb +1 -1
  177. data/lib/spontaneous/plugins/site/paths.rb +13 -0
  178. data/lib/spontaneous/plugins/site/publishing.rb +6 -2
  179. data/lib/spontaneous/plugins/site/{revisions.rb → state.rb} +5 -1
  180. data/lib/spontaneous/plugins/site/storage.rb +4 -0
  181. data/lib/spontaneous/plugins/site/url.rb +15 -0
  182. data/lib/spontaneous/plugins/site_map.rb +13 -3
  183. data/lib/spontaneous/plugins/styles.rb +5 -1
  184. data/lib/spontaneous/plugins/visibility.rb +39 -12
  185. data/lib/spontaneous/prototypes/box_prototype.rb +4 -0
  186. data/lib/spontaneous/prototypes/field_prototype.rb +15 -10
  187. data/lib/spontaneous/publishing/event_client.rb +46 -0
  188. data/lib/spontaneous/publishing/immediate.rb +81 -44
  189. data/lib/spontaneous/publishing/simultaneous.rb +23 -6
  190. data/lib/spontaneous/publishing.rb +4 -3
  191. data/lib/spontaneous/rack/around_back.rb +1 -5
  192. data/lib/spontaneous/rack/around_front.rb +4 -4
  193. data/lib/spontaneous/rack/around_preview.rb +4 -3
  194. data/lib/spontaneous/rack/assets.rb +81 -54
  195. data/lib/spontaneous/rack/authentication.rb +0 -1
  196. data/lib/spontaneous/rack/back.rb +160 -72
  197. data/lib/spontaneous/rack/cookie_authentication.rb +2 -2
  198. data/lib/spontaneous/rack/css.rb +18 -15
  199. data/lib/spontaneous/rack/event_source.rb +9 -21
  200. data/lib/spontaneous/rack/fiber_pool.rb +26 -0
  201. data/lib/spontaneous/rack/front.rb +12 -4
  202. data/lib/spontaneous/rack/helpers.rb +35 -13
  203. data/lib/spontaneous/rack/js.rb +41 -0
  204. data/lib/spontaneous/rack/public.rb +34 -44
  205. data/lib/spontaneous/rack/sse.rb +18 -0
  206. data/lib/spontaneous/rack/static.rb +3 -0
  207. data/lib/spontaneous/rack/user_admin.rb +100 -0
  208. data/lib/spontaneous/rack/user_helpers.rb +1 -1
  209. data/lib/spontaneous/rack.rb +6 -2
  210. data/lib/spontaneous/schema.rb +31 -4
  211. data/lib/spontaneous/search/index.rb +19 -3
  212. data/lib/spontaneous/search/results.rb +1 -1
  213. data/lib/spontaneous/sequel.rb +17 -0
  214. data/lib/spontaneous/site.rb +11 -4
  215. data/lib/spontaneous/state.rb +11 -0
  216. data/lib/spontaneous/storage/cloud.rb +4 -0
  217. data/lib/spontaneous/storage/local.rb +11 -0
  218. data/lib/spontaneous/style.rb +2 -2
  219. data/lib/spontaneous/tasks/database.rake +21 -22
  220. data/lib/spontaneous/utils/database/mysql_dumper.rb +4 -0
  221. data/lib/spontaneous/utils/database/postgres_dumper.rb +78 -0
  222. data/lib/spontaneous/utils/database.rb +6 -3
  223. data/lib/spontaneous/utils/smart_quotes.rb +499 -0
  224. data/lib/spontaneous/utils/smush_it.rb +66 -0
  225. data/lib/spontaneous/utils.rb +3 -1
  226. data/lib/spontaneous/version.rb +2 -2
  227. data/lib/spontaneous.rb +25 -29
  228. data/spontaneous.gemspec +186 -106
  229. data/test/experimental/test_features.rb +1 -1
  230. data/test/fixtures/asset_pipeline/application/css/basic.css.scss +4 -0
  231. data/test/fixtures/asset_pipeline/application/css/complex.css.scss.erb +6 -0
  232. data/test/fixtures/asset_pipeline/application/css/simple.css +3 -0
  233. data/test/fixtures/asset_pipeline/application/css/spontaneous.css +7 -0
  234. data/test/fixtures/asset_pipeline/application/css/subdir/complex.css.scss.erb +6 -0
  235. data/test/fixtures/asset_pipeline/application/css/subdir/library.css.scss +1 -0
  236. data/test/fixtures/asset_pipeline/application/css/subdir/simple.css +3 -0
  237. data/test/fixtures/asset_pipeline/application/js/basic.js.coffee +1 -0
  238. data/test/fixtures/asset_pipeline/application/js/complex.js.coffee.erb +1 -0
  239. data/test/fixtures/asset_pipeline/application/js/login.js +1 -0
  240. data/test/fixtures/asset_pipeline/application/js/require.js +1 -0
  241. data/test/fixtures/asset_pipeline/application/js/simple.js +1 -0
  242. data/test/fixtures/asset_pipeline/application/js/spontaneous.js +5 -0
  243. data/test/fixtures/asset_pipeline/application/js/subdir/complex.js.coffee.erb +1 -0
  244. data/test/fixtures/asset_pipeline/application/js/subdir/library.js.coffee +1 -0
  245. data/test/fixtures/asset_pipeline/application/js/subdir/simple.js +1 -0
  246. data/test/fixtures/asset_pipeline/application/js/vendor/jquery.js +1 -0
  247. data/test/fixtures/asset_pipeline/application/static/dot-texture.png +0 -0
  248. data/test/fixtures/asset_pipeline/application/static/subdir/px.gif +0 -0
  249. data/test/fixtures/assets/public1/css/a.scss +1 -0
  250. data/test/fixtures/assets/public1/js/a.js +1 -0
  251. data/test/fixtures/assets/public1/js/m.coffee +1 -0
  252. data/test/fixtures/assets/public2/css/b.scss +1 -0
  253. data/test/fixtures/assets/public2/css/c.css +1 -0
  254. data/test/fixtures/assets/public2/js/b.js +1 -0
  255. data/test/fixtures/assets/public2/js/c.js +1 -0
  256. data/test/fixtures/assets/public2/js/n.coffee +1 -0
  257. data/test/fixtures/back/config/user_levels.yml +3 -0
  258. data/test/fixtures/back/public/js/coffeescript.coffee +1 -0
  259. data/test/fixtures/back/templates/layouts/standard.css.cut +1 -0
  260. data/test/fixtures/back/templates/layouts/standard.js.cut +1 -0
  261. data/test/fixtures/example_application/config/database.yml +4 -4
  262. data/test/fixtures/helpers/templates/layouts/standard.html.cut +1 -0
  263. data/test/fixtures/helpers/templates/layouts/standard.mobile.cut +1 -0
  264. data/test/fixtures/images/vimlogo.pdf +0 -0
  265. data/test/fixtures/outputs/templates/layouts/standard.atom.cut +1 -0
  266. data/test/fixtures/permissions/config/user_levels.yml +6 -3
  267. data/test/fixtures/serialisation/class_hash.yaml.erb +1 -0
  268. data/test/fixtures/serialisation/root_hash.yaml.erb +2 -2
  269. data/test/fixtures/templates/extended/grandparent.html.cut +7 -7
  270. data/test/fixtures/templates/extended/main.html.cut +5 -5
  271. data/test/fixtures/templates/extended/parent.html.cut +8 -8
  272. data/test/fixtures/templates/extended/partial_with_locals.html.cut +1 -1
  273. data/test/fixtures/templates/extended/with_includes.html.cut +6 -6
  274. data/test/fixtures/templates/extended/with_includes_and_locals.html.cut +6 -7
  275. data/test/fixtures/templates/layouts/entries.html.cut +4 -4
  276. data/test/fixtures/templates/layouts/standard.html.cut +1 -0
  277. data/test/fixtures/templates/layouts/variables.html.cut +1 -1
  278. data/test/fixtures/templates/publishing/templates/layouts/static.html.cut +1 -1
  279. data/test/fixtures/templates/publishing/templates/layouts/static.rtf.cut +1 -0
  280. data/test/fixtures/templates/template_class/complex_template.html.cut +2 -2
  281. data/test/fixtures/templates/template_class/complex_template.pdf.cut +2 -2
  282. data/test/fixtures/templates/template_class/default_template_style.html.cut +1 -1
  283. data/test/fixtures/templates/template_class/images_with_template.html.cut +2 -2
  284. data/test/fixtures/templates/template_class/slots_template.html.cut +2 -2
  285. data/test/fixtures/templates/template_class/slots_template.pdf.cut +2 -2
  286. data/test/fixtures/user_manager/config/user_levels.yml +9 -0
  287. data/test/functional/test_application.rb +7 -7
  288. data/test/functional/test_back.rb +352 -97
  289. data/test/functional/test_front.rb +71 -20
  290. data/test/functional/test_user_manager.rb +284 -0
  291. data/test/javascript/env.js +13505 -0
  292. data/test/javascript/test_markdown.rb +43 -3
  293. data/test/support/custom_matchers.rb +3 -2
  294. data/test/test_helper.rb +50 -9
  295. data/test/test_javascript.rb +13 -13
  296. data/test/ui_helper.rb +1 -1
  297. data/test/unit/test_alias.rb +79 -8
  298. data/test/unit/test_asset_bundler.rb +220 -0
  299. data/test/unit/test_assets.rb +213 -0
  300. data/test/unit/test_async.rb +55 -0
  301. data/test/unit/test_authentication.rb +52 -39
  302. data/test/unit/test_boxes.rb +20 -15
  303. data/test/unit/test_changesets.rb +261 -0
  304. data/test/unit/test_content.rb +73 -33
  305. data/test/unit/test_content_inheritance.rb +18 -18
  306. data/test/unit/test_extensions.rb +26 -0
  307. data/test/unit/test_fields.rb +303 -16
  308. data/test/unit/test_formats.rb +267 -34
  309. data/test/unit/test_helpers.rb +79 -2
  310. data/test/unit/test_images.rb +10 -0
  311. data/test/unit/test_layouts.rb +21 -13
  312. data/test/unit/test_modifications.rb +648 -0
  313. data/test/unit/test_page.rb +6 -2
  314. data/test/unit/test_permissions.rb +101 -27
  315. data/test/unit/test_piece.rb +4 -4
  316. data/test/unit/test_plugins.rb +3 -2
  317. data/test/unit/test_publishing.rb +332 -0
  318. data/test/unit/test_render.rb +118 -67
  319. data/test/unit/test_revisions.rb +546 -0
  320. data/test/unit/test_schema.rb +83 -1
  321. data/test/unit/test_search.rb +59 -0
  322. data/test/unit/test_serialisation.rb +2 -47
  323. data/test/unit/test_site.rb +217 -108
  324. data/test/unit/test_storage.rb +26 -0
  325. data/test/unit/test_structure.rb +2 -2
  326. data/test/unit/test_styles.rb +90 -75
  327. data/test/unit/test_table_scoping.rb +21 -17
  328. data/test/unit/test_templates.rb +75 -69
  329. data/test/{slow → unit}/test_visibility.rb +78 -22
  330. metadata +528 -221
  331. data/application/css/add_alias_dialogue.scss +0 -27
  332. data/application/css/min/14c2a35812c443001b15d8b12a4780f88c678035.css +0 -1
  333. data/application/css/min/565d4c25e82148acb01c45c8d675b37a08676d77.css +0 -1
  334. data/application/css/min/84dbe894ea96eafd321c30823d630817bfc4b03b.css +0 -1
  335. data/application/css/spontaneous.scss +0 -111
  336. data/application/css/unsupported.scss +0 -16
  337. data/application/css/variables.scss +0 -80
  338. data/application/js/min/2a0c2962537a3181fedfff5c92596ba6d3122dc9.js +0 -3
  339. data/application/js/min/4cf1c493d3379ecba5287758c61238034c0893f9.js +0 -2
  340. data/application/js/min/78ac6b99d96750bb6b9f9aad4cb9cd91cd03f391.js +0 -3
  341. data/application/js/min/b8abf302a824c35385ff517b34111e1710ff3b37.js +0 -2
  342. data/application/js/min/c8efb9b9f7c3f6613fcebc6be60f605b6570a382.js +0 -90
  343. data/application/static/diagonal-texture.png +0 -0
  344. data/application/static/editing-0-noise.png +0 -0
  345. data/application/static/editing-1-noise.png +0 -0
  346. data/application/static/editing-texture-1.png +0 -0
  347. data/application/static/editing-texture.png +0 -0
  348. data/application/static/editing-toolbar-shadow-bottom.png +0 -0
  349. data/application/static/editing-toolbar-shadow-top.png +0 -0
  350. data/application/static/inner-glow.png +0 -0
  351. data/application/static/item-buttons-highlight.png +0 -0
  352. data/application/static/item-buttons.png +0 -0
  353. data/application/static/paper-texture-dark.png +0 -0
  354. data/application/static/plus_alt.svg +0 -8
  355. data/application/static/select-arrow-root.png +0 -0
  356. data/application/static/slot-up-arrow.png +0 -0
  357. data/application/static/spontaneous.png +0 -0
  358. data/lib/cutaneous/context_helper.rb +0 -187
  359. data/lib/cutaneous/preview_context.rb +0 -32
  360. data/lib/cutaneous/preview_renderer.rb +0 -10
  361. data/lib/cutaneous/publish_context.rb +0 -10
  362. data/lib/cutaneous/publish_renderer.rb +0 -14
  363. data/lib/cutaneous/publish_template.rb +0 -62
  364. data/lib/cutaneous/publish_token_parser.rb +0 -8
  365. data/lib/cutaneous/renderer.rb +0 -124
  366. data/lib/cutaneous/request_context.rb +0 -9
  367. data/lib/cutaneous/request_renderer.rb +0 -15
  368. data/lib/cutaneous/request_template.rb +0 -11
  369. data/lib/cutaneous/request_token_parser.rb +0 -9
  370. data/lib/cutaneous/token_parser.rb +0 -125
  371. data/lib/cutaneous.rb +0 -48
  372. data/lib/spontaneous/render/context_base.rb +0 -143
  373. data/lib/spontaneous/render/development_renderer.rb +0 -14
  374. data/lib/spontaneous/render/engine.rb +0 -19
  375. data/lib/spontaneous/render/format/html.rb +0 -5
  376. data/lib/spontaneous/render/format.rb +0 -75
  377. data/lib/spontaneous/render/helpers/script_helper.rb +0 -17
  378. data/lib/spontaneous/render/helpers/stylesheet_helper.rb +0 -16
  379. data/lib/spontaneous/render/helpers.rb +0 -14
  380. data/lib/spontaneous/render/preview_context.rb +0 -8
  381. data/lib/spontaneous/render/preview_renderer.rb +0 -24
  382. data/lib/spontaneous/render/publish_context.rb +0 -22
  383. data/lib/spontaneous/render/published_renderer.rb +0 -53
  384. data/lib/spontaneous/render/publishing_renderer.rb +0 -16
  385. data/lib/spontaneous/render/render_cache.rb +0 -26
  386. data/lib/spontaneous/render/renderer.rb +0 -50
  387. data/lib/spontaneous/render/request_context.rb +0 -8
  388. data/lib/spontaneous/render.rb +0 -214
  389. data/test/experimental/test_cutaneous.rb +0 -309
  390. data/test/fixtures/application/css/test.less +0 -5
  391. data/test/slow/test_publishing.rb +0 -1006
  392. /data/application/css/{developer.scss → developer.css.scss} +0 -0
  393. /data/application/js/vendor/{jquery-1.7.1.min.js → jquery.js} +0 -0
  394. /data/application/static/{location-arrow.png → location-arrow-c1c1d3ca472481c61a0a5f8cb289d743.png} +0 -0
  395. /data/application/static/{missing.png → missing-911ce6dbd9f4602e0f3fbcd7e78846a2.png} +0 -0
  396. /data/application/static/{orange-down-arrow.png → orange-down-arrow-395ae1929e8662b2ff0977daf12c35bb.png} +0 -0
  397. /data/application/static/{page-browser-next.png → page-browser-next-ce781a242c23b980b99b9db7abc20b05.png} +0 -0
  398. /data/application/static/{plus-box.png → plus-box-dcde74a1e496f68298c9b443caa1c5d5.png} +0 -0
  399. /data/application/static/{select-arrow.png → select-arrow-6e7dd3745b00e934b0d7a3250c46558b.png} +0 -0
  400. /data/application/static/{slot-down-arrow.png → slot-down-arrow-59ad5f5ee5b52a7ebd00bca4b3104194.png} +0 -0
  401. /data/application/static/{splash.png → splash-65b493a714df9b8b3ab170103401b53d.png} +0 -0
  402. /data/application/static/{spot.png → spot-71fab1d2f065034f5fc15e7bea2ad36c.png} +0 -0
  403. /data/application/static/{spot.svg → spot-8505041082d8d3bbe8d34c29dddb3b7d.svg} +0 -0
  404. /data/application/static/{texture.png → texture-03f7627e8264b1d607113ab32c593a58.png} +0 -0
@@ -15,10 +15,12 @@ class FrontTest < MiniTest::Spec
15
15
  def self.startup
16
16
  @site_root = Dir.mktmpdir
17
17
  FileUtils.cp_r(File.expand_path("../../fixtures/public/templates", __FILE__), @site_root)
18
+ Spontaneous::Output.write_compiled_scripts = true
18
19
  end
19
20
 
20
21
  def self.shutdown
21
22
  teardown_site
23
+ Spontaneous::Output.write_compiled_scripts = false
22
24
  end
23
25
 
24
26
  def setup
@@ -58,6 +60,10 @@ class FrontTest < MiniTest::Spec
58
60
  last_request.env['rack.session']
59
61
  end
60
62
 
63
+ def formats(format_list)
64
+ format_list.map { |f| Page.format_for(f) }
65
+ end
66
+
61
67
  context "Public pages" do
62
68
  setup do
63
69
 
@@ -65,7 +71,6 @@ class FrontTest < MiniTest::Spec
65
71
  Site.publishing_method = :immediate
66
72
  State.delete
67
73
  Content.delete
68
- Change.delete
69
74
 
70
75
  class ::SitePage < Spontaneous::Page
71
76
  layout :default
@@ -142,7 +147,7 @@ class FrontTest < MiniTest::Spec
142
147
  end
143
148
 
144
149
  should "honor the format of the request" do
145
- @about.class.stubs(:formats).returns([:html, :pdf])
150
+ @about.class.outputs :html, :pdf
146
151
  get '/about.pdf'
147
152
  assert last_response.ok?
148
153
  last_response.body.should == "/about.pdf\n"
@@ -150,7 +155,7 @@ class FrontTest < MiniTest::Spec
150
155
  end
151
156
 
152
157
  should "provide the default format of the page if none is explicitly given" do
153
- @about.class.stubs(:formats).returns([:rss, :html])
158
+ @about.class.outputs :rss, :html
154
159
  get '/about'
155
160
  assert last_response.ok?
156
161
  last_response.content_type.should == ::Rack::Mime.mime_type('.rss') + ";charset=utf-8"
@@ -158,7 +163,7 @@ class FrontTest < MiniTest::Spec
158
163
  end
159
164
 
160
165
  should "return a custom content type if one is defined" do
161
- @about.class.formats [{:html => "application/xhtml+xml"}]
166
+ @about.class.outputs [:html, {:mimetype => "application/xhtml+xml"}]
162
167
  get '/about'
163
168
  assert last_response.ok?
164
169
  last_response.content_type.should == "application/xhtml+xml;charset=utf-8"
@@ -171,10 +176,16 @@ class FrontTest < MiniTest::Spec
171
176
  assert last_response.status == 404
172
177
  end
173
178
 
179
+ should "raise a 404 when accessing a private format" do
180
+ @about.class.outputs [:html, {:mimetype => "application/xhtml+xml"}], [:rss, {:private => true}]
181
+ get '/about.rss'
182
+ assert last_response.status == 404
183
+ end
184
+
174
185
  context "Dynamic pages" do
175
186
  setup do
176
- Page.stubs(:path).with("/about").returns(about)
177
- Page.stubs(:path).with("/news").returns(news)
187
+ Spontaneous::Page.stubs(:path).with("/about").returns(about)
188
+ Spontaneous::Page.stubs(:path).with("/news").returns(news)
178
189
  end
179
190
 
180
191
  should "default to static behaviour" do
@@ -254,7 +265,7 @@ class FrontTest < MiniTest::Spec
254
265
  show "#news"
255
266
  end
256
267
  post '/about'
257
- assert last_response.status == 200
268
+ assert last_response.status == 200, "Expected status 200 but recieved #{last_response.status}"
258
269
  last_response.body.should == "/news.html\n"
259
270
  end
260
271
 
@@ -276,6 +287,30 @@ class FrontTest < MiniTest::Spec
276
287
  last_response.body.should == "/about.html\n"
277
288
  last_response.headers["X-Works"].should == "Yes"
278
289
  end
290
+
291
+ should "allow passing of template params & a page to the render call" do
292
+ SitePage.layout do
293
+ "{{ teeth }}"
294
+ end
295
+ SitePage.request do
296
+ render page, :teeth => "white"
297
+ end
298
+ get '/about'
299
+ assert last_response.status == 200
300
+ last_response.body.should == "white"
301
+ end
302
+
303
+ should "give access to the request params within the controller" do
304
+ SitePage.layout { "{{ params[:horse] }}*{{ equine }}" }
305
+ SitePage.request :post do
306
+ value = params[:horse]
307
+ render page, :equine => value
308
+ end
309
+ post '/about', :horse => "dancing"
310
+ assert last_response.status == 200
311
+ last_response.body.should == "dancing*dancing"
312
+ end
313
+
279
314
  # should "handle anything that responds to #render(format)" do
280
315
  # show = mock()
281
316
  # show.stubs(:render).returns("mocked")
@@ -367,38 +402,44 @@ class FrontTest < MiniTest::Spec
367
402
 
368
403
  context "caching" do
369
404
  setup do
370
- Spontaneous::Render.cache_templates = true
371
- @cache_file = "#{Spontaneous.revision_dir(1)}/html/dynamic/index.html.rb"
405
+ Spontaneous::Output.cache_templates = true
406
+ @cache_file = "#{Spontaneous.revision_dir(1)}/dynamic/dynamic.html.rb"
407
+ Spontaneous::Output.write_compiled_scripts = true
372
408
  end
373
409
 
374
410
  teardown do
375
- Spontaneous::Render.cache_templates = false
411
+ Spontaneous::Output.cache_templates = true
376
412
  end
377
413
 
378
414
  should "use pre-rendered versions of the templates" do
379
415
  dummy_content = 'cached-version/#{session[\'user_id\']}'
380
- dummy_template = File.join(@site.revision_root, "dummy.html.cut")
416
+ dummy_template = File.join(@site.revision_root, "current/dynamic/dynamic.html.cut")
381
417
  File.open(dummy_template, 'w') { |f| f.write(dummy_content) }
382
- Spontaneous::Render.stubs(:output_path).returns(dummy_template)
418
+ # Spontaneous::Render.stubs(:output_path).returns(dummy_template)
383
419
  get '/dynamic', {'wendy' => 'peter'}, 'rack.session' => { 'user_id' => 42 }
384
420
  last_response.body.should == "cached-version/42"
385
421
  end
386
422
 
387
423
  should "cache templates as ruby files" do
388
- @cache_file = "#{Spontaneous.revision_dir(1)}/dynamic/dynamic/index.html.rb"
424
+ dummy_content = 'cached-version/#{session[\'user_id\']}'
425
+ dummy_template = File.join(@site.revision_root, "current/dynamic/index.html.cut")
426
+ Spontaneous::Output.renderer.write_compiled_scripts = true
427
+ File.open(dummy_template, 'w') { |f| f.write(dummy_content) }
389
428
  FileUtils.rm(@cache_file) if File.exists?(@cache_file)
390
429
  File.exists?(@cache_file).should be_false
391
430
  get '/dynamic', {'wendy' => 'peter'}, 'rack.session' => { 'user_id' => 42 }
392
- # puts `ls -l #{File.dirname(@cache_file)}`
431
+
393
432
  File.exists?(@cache_file).should be_true
394
- File.open(@cache_file, 'w') { |f| f.write('_buf << %Q`@cache_filed-version/#{params[\'wendy\']}`;')}
433
+ File.open(@cache_file, 'w') { |f| f.write('@__buf << %Q`@cache_filed-version/#{params[\'wendy\']}`;')}
434
+ # Force compiled file to have a later timestamp
435
+ File.utime(Time.now, Time.now + 1, @cache_file)
395
436
  get '/dynamic', {'wendy' => 'peter'}, 'rack.session' => { 'user_id' => 42 }
396
437
  last_response.body.should == "@cache_filed-version/peter"
397
438
  FileUtils.rm(@cache_file)
398
439
  end
399
440
 
400
441
  should "not cache templates if caching turned off" do
401
- Spontaneous::Render.cache_templates = false
442
+ Spontaneous::Output.cache_templates = false
402
443
  FileUtils.rm(@cache_file) if File.exists?(@cache_file)
403
444
  File.exists?(@cache_file).should be_false
404
445
  get '/dynamic', {'wendy' => 'peter'}, 'rack.session' => { 'user_id' => 42 }
@@ -447,6 +488,7 @@ class FrontTest < MiniTest::Spec
447
488
  end
448
489
  end
449
490
 
491
+ Page.stubs(:path).with("/").returns(root)
450
492
  Page.stubs(:path).with("/about").returns(about)
451
493
  Page.stubs(:path).with("/about/now").returns(subpage)
452
494
  end
@@ -464,6 +506,13 @@ class FrontTest < MiniTest::Spec
464
506
  last_response.body.should == about.render
465
507
  end
466
508
 
509
+ should "work on the homepage" do
510
+ get "/@comments"
511
+ assert last_response.ok?
512
+ last_response.body.should == "Success"
513
+ end
514
+
515
+
467
516
  should "be recognised" do
468
517
  get "/about/@comments"
469
518
  assert last_response.ok?
@@ -512,15 +561,17 @@ class FrontTest < MiniTest::Spec
512
561
  end
513
562
 
514
563
  should "pass the format onto the page if the action returns it to the render call" do
515
- about.stubs(:provides_format?).with(:'xml', anything).returns(true)
516
- about.expects(:render).with(:'xml', anything).returns("/about.xml")
517
- about.expects(:render).with(:'html', anything).never
564
+ about.class.outputs :html, :xml
565
+ about.class.layout do
566
+ "${path}.${__format}"
567
+ end
518
568
  get "/about/@comments/page.xml"
519
569
  assert last_response.ok?
520
570
  last_response.body.should == "/about.xml"
521
571
  end
522
572
 
523
573
  should "use the format within the action if required" do
574
+ about.class.outputs :html, :xml
524
575
  get "/about/@comments/format.xml"
525
576
  assert last_response.ok?
526
577
  last_response.body.should == "xml"
@@ -612,4 +663,4 @@ class FrontTest < MiniTest::Spec
612
663
  end
613
664
  end
614
665
  end
615
- end
666
+ end
@@ -0,0 +1,284 @@
1
+ # encoding: UTF-8
2
+
3
+ require File.expand_path('../../test_helper', __FILE__)
4
+
5
+ # set :environment, :test
6
+
7
+
8
+ class UserAdminTest < MiniTest::Spec
9
+ include ::Rack::Test::Methods
10
+
11
+ def self.site_root
12
+ @site_root
13
+ end
14
+
15
+ def self.startup
16
+ S::Permissions::User.delete
17
+ @site_root = Dir.mktmpdir
18
+ app_root = File.expand_path('../../fixtures/user_manager', __FILE__)
19
+ FileUtils.cp_r(app_root + "/", @site_root)
20
+ @site_root += "/user_manager"
21
+ end
22
+
23
+ def self.shutdown
24
+ teardown_site
25
+ end
26
+
27
+ def app
28
+ Spontaneous::Rack::Back.application
29
+ end
30
+
31
+ def setup
32
+ @site = setup_site(self.class.site_root)
33
+ Spot::Permissions::UserLevel.reset!
34
+ Spot::Permissions::UserLevel.init!
35
+ @editor_user = create_user("editor", S::Permissions[:editor])
36
+ @admin_user = create_user("admin", S::Permissions[:admin])
37
+ @root_user = create_user("root", S::Permissions[:root])
38
+
39
+ assert @editor_user.level == S::Permissions[:editor]
40
+ assert @admin_user.level == S::Permissions[:admin]
41
+ assert @root_user.level == S::Permissions[:root]
42
+ end
43
+
44
+ def teardown
45
+ S::Permissions::User.delete
46
+ end
47
+
48
+ def create_user(name, level)
49
+ user = Permissions::User.create({
50
+ :name => "#{name.capitalize}",
51
+ :email => "#{name}@example.org",
52
+ :login => name,
53
+ :password => "#{name}_password"
54
+ })
55
+ user.update(:level => level)
56
+ user
57
+ end
58
+
59
+ def login_user(user, params={})
60
+ post "/@spontaneous/login", {"user[login]" => user.login, "user[password]" => user.password}.merge(params)
61
+ @user = user
62
+ end
63
+
64
+ def auth_post(path, params={})
65
+ key = @user.access_keys.first
66
+ post(path, params.merge("__key" => key.key_id))
67
+ end
68
+
69
+ def auth_put(path, params={})
70
+ key = @user.access_keys.first
71
+ put(path, params.merge("__key" => key.key_id))
72
+ end
73
+
74
+ def auth_get(path, params={})
75
+ key = @user.access_keys.first
76
+ get(path, params.merge("__key" => key.key_id))
77
+ end
78
+
79
+ def auth_del(path, params = {})
80
+ key = @user.access_keys.first
81
+ delete(path, params.merge("__key" => key.key_id))
82
+ end
83
+
84
+
85
+ context "Access to User admin" do
86
+ should "be denied to unauthorised requests" do
87
+ get "/@spontaneous/users"
88
+ assert last_response.status == 401, "Expected 401 but got #{ last_response.status }"
89
+ end
90
+
91
+ should "be denied to users without the admin flag" do
92
+ login_user(@editor_user)
93
+ auth_get "/@spontaneous/users"
94
+ assert last_response.status == 403, "Expected 403 but got #{ last_response.status }"
95
+ end
96
+
97
+ should "be granted to users with the admin flag" do
98
+ login_user(@admin_user)
99
+ auth_get "/@spontaneous/users"
100
+ assert last_response.status == 200, "Expected 200 but got #{ last_response.status }"
101
+ end
102
+
103
+ should "be granted to root users" do
104
+ login_user(@root_user)
105
+ auth_get "/@spontaneous/users"
106
+ assert last_response.status == 200, "Expected 200 but got #{ last_response.status }"
107
+ end
108
+ end
109
+
110
+ context "User application" do
111
+ setup do
112
+ login_user(@admin_user)
113
+ end
114
+
115
+ should "return serialised list of current users" do
116
+ auth_get "/@spontaneous/users"
117
+ result = S::JSON.parse last_response.body
118
+ result.should == S::Permissions::User.export(@user)
119
+ end
120
+
121
+ should "allow user details to be updated" do
122
+ auth_put "/@spontaneous/users/#{@editor_user.id}", { "user[name]" => "Robert Something", "user[login]" => "robert", "user[email]" => "robert@example.org" }
123
+ assert last_response.ok?, "Recieved a #{ last_response.status } instead of a 200"
124
+ @editor_user.reload.login.should == "robert"
125
+ end
126
+
127
+ should "allow a users level to be changed" do
128
+ auth_put "/@spontaneous/users/#{@editor_user.id}", { "user[name]" => "Robert Something", "user[login]" => "robert", "user[email]" => "robert@example.org", "user[level]" => "admin" }
129
+ assert last_response.ok?, "Recieved a #{ last_response.status } instead of a 200"
130
+ @editor_user.reload.level.should == @admin_user.level
131
+ end
132
+
133
+ should "reject updates that change the level above the current user's" do
134
+ level = @editor_user.level
135
+ auth_put "/@spontaneous/users/#{@editor_user.id}", { "user[name]" => "Robert Something", "user[login]" => "robert", "user[email]" => "robert@example.org", "user[level]" => "root" }
136
+ assert last_response.status == 403, "Recieved a #{ last_response.status } instead of a 403"
137
+ @editor_user.reload.level.should == level
138
+ end
139
+
140
+ should "reject an update with a conflicting login" do
141
+ auth_put "/@spontaneous/users/#{@editor_user.id}", { "user[name]" => "Robert Something", "user[login]" => "admin", "user[email]" => "robert@example.org" }
142
+ assert last_response.status == 422, "Recieved a #{ last_response.status } instead of a 422"
143
+ @editor_user.reload.login.should == "editor"
144
+ result = S::JSON.parse last_response.body
145
+ result.must_be_instance_of Hash
146
+ result[:login].should_not be_nil
147
+ end
148
+
149
+ should "reject an update with an invalid email" do
150
+ email = @editor_user.email
151
+ auth_put "/@spontaneous/users/#{@editor_user.id}", { "user[name]" => "Robert Something", "user[login]" => "editor", "user[email]" => "robert" }
152
+ assert last_response.status == 422, "Recieved a #{ last_response.status } instead of a 422"
153
+ @editor_user.reload.email.should == email
154
+ result = S::JSON.parse last_response.body
155
+ result.must_be_instance_of Hash
156
+ result[:email].should_not be_nil
157
+ end
158
+
159
+ should "ignore changes to the salt" do
160
+ salt = @editor_user.salt
161
+ auth_put "/@spontaneous/users/#{@editor_user.id}", { "user[name]" => "Robert Something", "user[login]" => "editor", "user[email]" => "robert@example.com", "user[salt]" => "123456" }
162
+ assert last_response.ok?
163
+ @editor_user.reload.salt.should == salt
164
+ end
165
+
166
+ should "allow updating of the user's password" do
167
+ new_pass = "123467890"
168
+ auth_put "/@spontaneous/users/password/#{@editor_user.id}", { "password" => new_pass }
169
+ assert last_response.ok?, "Recieved status #{ last_response.status} instead of 200"
170
+ key = Spontaneous::Permissions::User.authenticate(@editor_user.login, new_pass, "127.0.0.1")
171
+ key.should_not be_nil
172
+ end
173
+
174
+ should "reject & return error for invalid passwords" do
175
+ new_pass = "1234"
176
+ auth_put "/@spontaneous/users/password/#{@editor_user.id}", { "password" => new_pass }
177
+ assert last_response.status == 422, "Recieved a #{ last_response.status } instead of a 422"
178
+ key = Spontaneous::Permissions::User.authenticate(@editor_user.login, new_pass, "127.0.0.1")
179
+ key.should be_nil
180
+ result = S::JSON.parse last_response.body
181
+ result.must_be_instance_of Hash
182
+ result[:password].should_not be_nil
183
+ end
184
+
185
+ should "not allow changing details of users with higher level" do
186
+ auth_put "/@spontaneous/users/#{@root_user.id}", { "user[name]" => "Robert Something", "user[login]" => "robert", "user[email]" => "robert@example.org" }
187
+ assert last_response.status == 403, "Recieved a #{ last_response.status } instead of a 403"
188
+ @root_user.reload.login.should == "root"
189
+ end
190
+
191
+ # should "allow admins to reset their password"
192
+ should "enable a force-logout of a user by deleting their keys" do
193
+ 3.times do |n|
194
+ @editor_user.generate_access_key("203.99.33.#{n}")
195
+ end
196
+ @editor_user.access_keys.length.should == 3
197
+ auth_del "/@spontaneous/users/keys/#{@editor_user.id}"
198
+ assert last_response.ok?, "Recieved status #{ last_response.status} instead of 200"
199
+ @editor_user.reload.access_keys.length.should == 0
200
+ end
201
+
202
+ should "not allow logging out of user with higher level" do
203
+ 3.times do |n|
204
+ @root_user.generate_access_key("203.99.33.#{n}")
205
+ end
206
+ @root_user.access_keys.length.should == 3
207
+ auth_del "/@spontaneous/users/keys/#{@root_user.id}"
208
+ assert last_response.status == 403, "Recieved a #{ last_response.status } instead of a 403"
209
+ @root_user.reload.access_keys.length.should == 3
210
+ end
211
+
212
+ should "allow you to disable a user" do
213
+ @editor_user.disabled?.should be_false
214
+ auth_put "/@spontaneous/users/disable/#{@editor_user.id}"
215
+ assert last_response.ok?
216
+ @editor_user.reload.disabled?.should be_true
217
+ end
218
+
219
+ should "allow you to re-enable a user" do
220
+ @editor_user.disabled?.should be_false
221
+ @editor_user.disable!
222
+ auth_put "/@spontaneous/users/enable/#{@editor_user.id}"
223
+ assert last_response.ok?
224
+ @editor_user.reload.disabled?.should be_false
225
+ end
226
+
227
+ should "not allow you to disable a user with a higher user level" do
228
+ @root_user.disabled?.should be_false
229
+ auth_put "/@spontaneous/users/disable/#{@root_user.id}"
230
+ assert last_response.status == 403, "Recieved a #{ last_response.status } instead of a 403"
231
+ @root_user.reload.disabled?.should be_false
232
+ end
233
+
234
+ should "allow for the creation of new users" do
235
+ auth_post "/@spontaneous/users", { "user[name]" => "Robert Something", "user[login]" => "robert", "user[email]" => "robert@example.org", "user[password]" => "robertpass", "user[level]" => "admin" }
236
+ assert last_response.ok?, "Recieved status #{ last_response.status} instead of 200"
237
+ user = S::Permissions::User[:login => "robert"]
238
+ user.must_be_instance_of S::Permissions::User
239
+ user.name.should == "Robert Something"
240
+ user.email.should == "robert@example.org"
241
+ user.level.should == Spontaneous::Permissions[:admin]
242
+ key = Spontaneous::Permissions::User.authenticate("robert", "robertpass", "127.0.0.1")
243
+ key.must_be_instance_of S::Permissions::AccessKey
244
+ key.user.id.should == user.id
245
+ end
246
+
247
+ should "return user info on account creation" do
248
+ auth_post "/@spontaneous/users", { "user[name]" => "Robert Something", "user[login]" => "robert", "user[email]" => "robert@example.org", "user[password]" => "robertpass", "user[level]" => "admin" }
249
+ assert last_response.ok?, "Recieved status #{ last_response.status} instead of 200"
250
+ result = S::JSON.parse last_response.body
251
+ result[:id].should_not be_nil
252
+ user = S::Permissions::User[result[:id]]
253
+ result.should == S::Permissions::User.export_user(user)
254
+ end
255
+
256
+ should "not allow creation of users with higher level" do
257
+ users = S::Permissions::User.count
258
+ auth_post "/@spontaneous/users", { "user[name]" => "Robert Something", "user[login]" => "robert", "user[email]" => "robert@example.org", "user[password]" => "robertpass", "user[level]" => "root" }
259
+ assert last_response.status == 403, "Recieved a #{ last_response.status } instead of a 403"
260
+ S::Permissions::User.count.should == users
261
+ end
262
+
263
+ should "return an error if passed invalid user attributes" do
264
+ users = S::Permissions::User.count
265
+ auth_post "/@spontaneous/users", { "user[name]" => "Robert Something", "user[login]" => "admin", "user[email]" => "robert", "user[password]" => "pass", "user[level]" => "editor" }
266
+ assert last_response.status == 422, "Recieved a #{ last_response.status } instead of a 422"
267
+ S::Permissions::User.count.should == users
268
+ end
269
+
270
+ should "allow us to delete a user" do
271
+ auth_del "/@spontaneous/users/#{@editor_user.id}"
272
+ assert last_response.status == 200, "Recieved a #{ last_response.status } instead of a 200"
273
+ user = Spontaneous::Permissions::User[:login => @editor_user.login]
274
+ user.should be_nil
275
+ end
276
+
277
+ should "not allow us to delete a user with a higher user level" do
278
+ auth_del "/@spontaneous/users/#{@root_user.id}"
279
+ assert last_response.status == 403, "Recieved a #{ last_response.status } instead of a 403"
280
+ user = Spontaneous::Permissions::User[:login => @root_user.login]
281
+ user.should_not be_nil
282
+ end
283
+ end
284
+ end