browsercms 3.4.2 → 3.5.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (367) hide show
  1. data/README.markdown +1 -0
  2. data/app/assets/images/cms/file-uploading.gif +0 -0
  3. data/app/assets/javascripts/cms/application.js +1 -0
  4. data/app/assets/javascripts/cms/attachment_manager.js.erb +87 -0
  5. data/app/assets/javascripts/cms/core_library.js.erb +38 -25
  6. data/app/assets/stylesheets/cms/application.css.erb +1 -0
  7. data/app/assets/stylesheets/cms/attachment_manager.css.scss +28 -0
  8. data/app/controllers/cms/application_controller.rb +1 -1
  9. data/app/controllers/cms/attachments_controller.rb +45 -10
  10. data/app/controllers/cms/cache_controller.rb +1 -1
  11. data/app/controllers/cms/content_block_controller.rb +134 -122
  12. data/app/controllers/cms/content_controller.rb +143 -155
  13. data/app/controllers/cms/dashboard_controller.rb +11 -9
  14. data/app/controllers/cms/error_handling.rb +19 -7
  15. data/app/controllers/cms/file_blocks_controller.rb +2 -2
  16. data/app/controllers/cms/home_controller.rb +3 -0
  17. data/app/controllers/cms/section_nodes_controller.rb +52 -44
  18. data/app/controllers/cms/sections_controller.rb +4 -2
  19. data/app/controllers/cms/toolbar_controller.rb +14 -10
  20. data/app/helpers/cms/application_helper.rb +23 -19
  21. data/app/helpers/cms/form_builder.rb +65 -18
  22. data/app/helpers/cms/mobile_helper.rb +19 -0
  23. data/app/helpers/cms/path_helper.rb +30 -4
  24. data/app/helpers/cms/rendering_helper.rb +9 -1
  25. data/app/models/cms/abstract_file_block.rb +6 -6
  26. data/app/models/cms/attachment.rb +196 -107
  27. data/app/models/cms/category.rb +3 -0
  28. data/app/models/cms/category_type.rb +2 -0
  29. data/app/models/cms/connector.rb +3 -0
  30. data/app/models/cms/content_type.rb +3 -0
  31. data/app/models/cms/content_type_group.rb +2 -0
  32. data/app/models/cms/dynamic_view.rb +4 -0
  33. data/app/models/cms/email_message.rb +2 -0
  34. data/app/models/cms/file_block.rb +9 -2
  35. data/app/models/cms/group.rb +5 -2
  36. data/app/models/cms/group_permission.rb +2 -0
  37. data/app/models/cms/group_section.rb +3 -0
  38. data/app/models/cms/group_type.rb +2 -0
  39. data/app/models/cms/group_type_permission.rb +2 -0
  40. data/app/models/cms/html_block.rb +3 -2
  41. data/app/models/cms/image_block.rb +13 -2
  42. data/app/models/cms/page.rb +14 -3
  43. data/app/models/cms/page_route.rb +4 -0
  44. data/app/models/cms/page_route_condition.rb +1 -0
  45. data/app/models/cms/page_route_option.rb +2 -0
  46. data/app/models/cms/page_route_requirement.rb +1 -0
  47. data/app/models/cms/permission.rb +3 -0
  48. data/app/models/cms/portlet.rb +2 -2
  49. data/app/models/cms/redirect.rb +2 -0
  50. data/app/models/cms/section.rb +15 -1
  51. data/app/models/cms/section_node.rb +1 -0
  52. data/app/models/cms/site.rb +3 -0
  53. data/app/models/cms/tag.rb +2 -0
  54. data/app/models/cms/tagging.rb +3 -0
  55. data/app/models/cms/task.rb +5 -1
  56. data/app/models/cms/user.rb +1 -1
  57. data/app/models/cms/user_group_membership.rb +3 -0
  58. data/app/views/cms/attachments/_attachment.html.erb +14 -0
  59. data/app/views/cms/attachments/_attachment_table.html.erb +17 -0
  60. data/app/views/cms/attachments/_attachment_wrapper.html.erb +4 -0
  61. data/app/views/cms/blocks/index.html.erb +2 -3
  62. data/app/views/cms/blocks/show.html.erb +1 -1
  63. data/app/views/cms/dynamic_views/index.html.erb +1 -1
  64. data/app/views/cms/email_messages/index.html.erb +1 -1
  65. data/app/views/cms/file_blocks/_form.html.erb +1 -27
  66. data/app/views/cms/file_blocks/_section_selector.html.erb +13 -0
  67. data/app/views/cms/file_blocks/render.html.erb +3 -3
  68. data/app/views/cms/form_builder/_cms_attachment_manager.html.erb +26 -0
  69. data/app/views/cms/form_builder/_cms_file_field.html.erb +27 -35
  70. data/app/views/cms/groups/index.html.erb +1 -1
  71. data/app/views/cms/image_blocks/_form.html.erb +1 -27
  72. data/app/views/cms/image_blocks/render.html.erb +4 -1
  73. data/app/views/cms/page_routes/index.html.erb +3 -0
  74. data/app/views/cms/shared/error.xml.erb +8 -0
  75. data/app/views/cms/tags/render.html.erb +1 -1
  76. data/app/views/cms/toolbar/_mobile_toggle.html.erb +33 -0
  77. data/app/views/cms/users/index.html.erb +1 -1
  78. data/app/views/layouts/_page_toolbar.html.erb +2 -1
  79. data/bin/bcms +21 -26
  80. data/config/routes.rb +3 -2
  81. data/db/browsercms.seeds.rb +2 -1
  82. data/db/migrate/20111130221145_browsercms340.rb +5 -4
  83. data/db/migrate/20120329144406_browsercms350.rb +32 -0
  84. data/{test/dummy/db → db}/schema.rb +97 -128
  85. data/{performance_tuning_notes.md → doc/performance_tuning_notes.md} +0 -0
  86. data/doc/release_notes.md +74 -0
  87. data/lib/browsercms.rb +3 -0
  88. data/lib/cms/acts/content_block.rb +10 -2
  89. data/lib/cms/addressable.rb +8 -0
  90. data/lib/cms/attachments/attachment_serving.rb +59 -0
  91. data/lib/cms/attachments/configuration.rb +88 -0
  92. data/lib/cms/behaviors/attaching.rb +305 -136
  93. data/lib/cms/behaviors/connecting.rb +3 -4
  94. data/lib/cms/behaviors/dynamic_attributes.rb +121 -118
  95. data/lib/cms/behaviors/flush_cache_on_change.rb +1 -3
  96. data/lib/cms/behaviors/naming.rb +16 -0
  97. data/lib/cms/behaviors/pagination.rb +4 -1
  98. data/lib/cms/behaviors/publishing.rb +9 -3
  99. data/lib/cms/behaviors/searching.rb +3 -8
  100. data/lib/cms/behaviors/soft_deleting.rb +1 -0
  101. data/lib/cms/behaviors/taggable.rb +2 -0
  102. data/lib/cms/behaviors/versioning.rb +73 -120
  103. data/lib/cms/caching.rb +53 -11
  104. data/lib/cms/commands/actions.rb +19 -2
  105. data/lib/cms/configuration.rb +44 -0
  106. data/lib/cms/content_rendering_support.rb +9 -6
  107. data/lib/cms/default_accessible.rb +13 -0
  108. data/lib/cms/domain_support.rb +22 -0
  109. data/lib/cms/engine.rb +40 -19
  110. data/lib/cms/engine_helper.rb +54 -0
  111. data/lib/cms/extensions/active_record/connection_adapters/abstract/schema_statements.rb +14 -2
  112. data/lib/cms/mobile_aware.rb +67 -0
  113. data/lib/cms/route_extensions.rb +3 -0
  114. data/lib/cms/upgrades/v3_5_0.rb +155 -0
  115. data/lib/cms/version.rb +6 -1
  116. data/lib/generators/cms/content_block/content_block_generator.rb +14 -9
  117. data/lib/generators/cms/content_block/templates/_form.html.erb +17 -6
  118. data/lib/generators/cms/content_block/templates/render.html.erb +12 -5
  119. data/lib/generators/cms/template/template_generator.rb +11 -2
  120. data/lib/tasks/cms.rake +23 -0
  121. data/lib/templates/active_record/model/model.rb +6 -0
  122. metadata +127 -517
  123. data/.gitignore +0 -24
  124. data/.rvmrc +0 -2
  125. data/Gemfile +0 -29
  126. data/Gemfile.lock +0 -196
  127. data/Rakefile +0 -97
  128. data/app/assets/images/browsercms/.gitkeep +0 -0
  129. data/app/controllers/browsercms/application_controller.rb +0 -4
  130. data/browsercms.gemspec +0 -35
  131. data/config/cucumber.yml +0 -8
  132. data/config/database.jdbcmysql.yml +0 -30
  133. data/config/database.mysql.yml +0 -27
  134. data/config/database.postgres.yml +0 -25
  135. data/config/database.sqlite3.yml +0 -11
  136. data/config/environment.rb +0 -6
  137. data/config/initializers/query_reviewer_patch.rb +0 -12
  138. data/config/initializers/rack_1_2_1_patch.rb +0 -12
  139. data/config/locales/en.yml +0 -5
  140. data/features/acts_as_content_page.feature +0 -62
  141. data/features/add_content_to_pages.feature +0 -45
  142. data/features/caching.feature +0 -13
  143. data/features/ckeditor.feature +0 -11
  144. data/features/commands/confirm_aruba_works.feature +0 -24
  145. data/features/commands/generate_module.feature +0 -54
  146. data/features/commands/install_browsercms.feature +0 -21
  147. data/features/commands/new_demo_project.feature +0 -30
  148. data/features/commands/new_projects.feature +0 -50
  149. data/features/commands/upgrade_modules_to_3_4_0_from_3_1_x.feature +0 -19
  150. data/features/commands/upgrade_project_to_3_4_0_from_3_3_x.feature +0 -52
  151. data/features/commands/upgrading_modules.feature +0 -67
  152. data/features/content_blocks/manage_custom_blocks.feature +0 -67
  153. data/features/content_blocks/manage_html_blocks.feature +0 -48
  154. data/features/content_blocks/manage_image_blocks.feature +0 -41
  155. data/features/content_files.feature +0 -37
  156. data/features/content_pages.feature +0 -21
  157. data/features/generators/content_blocks_for_modules.feature +0 -58
  158. data/features/generators/content_blocks_for_projects.feature +0 -109
  159. data/features/install_content.feature +0 -25
  160. data/features/jquery-testplan.txt +0 -12
  161. data/features/manage_groups.feature +0 -33
  162. data/features/manage_page_routes.feature +0 -72
  163. data/features/manage_redirects.feature +0 -20
  164. data/features/manage_sections.feature +0 -12
  165. data/features/manage_tasks.feature +0 -25
  166. data/features/manage_users.feature +0 -38
  167. data/features/page_templates.feature +0 -49
  168. data/features/portlets/email_friend_portlet.feature +0 -29
  169. data/features/portlets/portlets.feature +0 -100
  170. data/features/portlets/tag_cloud_portlet.feature +0 -28
  171. data/features/sitemap/create_pages.feature +0 -15
  172. data/features/sitemap/manage_links.feature +0 -29
  173. data/features/sitemap/sitemap.feature +0 -18
  174. data/features/step_definitions/acts_as_content_page_steps.rb.rb +0 -3
  175. data/features/step_definitions/ckeditor_steps.rb +0 -13
  176. data/features/step_definitions/command_line_steps.rb +0 -212
  177. data/features/step_definitions/content_pages_steps.rb +0 -170
  178. data/features/step_definitions/data_steps.rb +0 -48
  179. data/features/step_definitions/edit_page_templates_steps.rb +0 -21
  180. data/features/step_definitions/html_blocks_steps.rb +0 -9
  181. data/features/step_definitions/install_content_steps.rb +0 -4
  182. data/features/step_definitions/manage_content_blocks_steps.rb +0 -26
  183. data/features/step_definitions/manage_image_blocks_steps.rb +0 -31
  184. data/features/step_definitions/manage_sections_steps.rb +0 -18
  185. data/features/step_definitions/manage_user_steps.rb +0 -22
  186. data/features/step_definitions/more_custom_block_steps.rb +0 -34
  187. data/features/step_definitions/page_route_steps.rb +0 -65
  188. data/features/step_definitions/page_template_steps.rb +0 -5
  189. data/features/step_definitions/permissions_steps.rb +0 -13
  190. data/features/step_definitions/portlets_steps.rb +0 -64
  191. data/features/step_definitions/redirect_steps.rb +0 -12
  192. data/features/step_definitions/sitemap_steps.rb +0 -18
  193. data/features/step_definitions/tag_cloud_steps.rb +0 -11
  194. data/features/step_definitions/task_steps.rb +0 -4
  195. data/features/step_definitions/taxonomy_steps.rb +0 -16
  196. data/features/step_definitions/upgrade_module_steps.rb +0 -76
  197. data/features/step_definitions/web_steps.rb +0 -211
  198. data/features/support/async_support.rb +0 -17
  199. data/features/support/command_line_helpers.rb +0 -63
  200. data/features/support/debug_formatter.rb +0 -7
  201. data/features/support/debugging.rb +0 -28
  202. data/features/support/env.rb +0 -73
  203. data/features/support/git_api.rb +0 -9
  204. data/features/support/open_on_first_failure.rb +0 -25
  205. data/features/support/paths.rb +0 -32
  206. data/features/support/rails_api.rb +0 -8
  207. data/features/support/selectors.rb +0 -39
  208. data/features/support/transforms.rb +0 -7
  209. data/features/taxonomy/add_content_with_category.feature +0 -30
  210. data/features/taxonomy/manage_categories.feature +0 -20
  211. data/features/taxonomy/manage_category_types.feature +0 -16
  212. data/lib/cms/init.rb +0 -105
  213. data/lib/tasks/data.rake +0 -43
  214. data/lib/tasks/db.rake +0 -82
  215. data/public/styled_file_field/index.html +0 -72
  216. data/script/cucumber +0 -10
  217. data/script/rails +0 -6
  218. data/test/console_helper.rb +0 -5
  219. data/test/custom_assertions.rb +0 -84
  220. data/test/dummy/Rakefile +0 -7
  221. data/test/dummy/app/assets/javascripts/application.js +0 -9
  222. data/test/dummy/app/assets/javascripts/content_page.js +0 -2
  223. data/test/dummy/app/assets/stylesheets/application.css +0 -7
  224. data/test/dummy/app/assets/stylesheets/content_page.css +0 -4
  225. data/test/dummy/app/controllers/application_controller.rb +0 -3
  226. data/test/dummy/app/controllers/cms/products_controller.rb +0 -2
  227. data/test/dummy/app/controllers/cms/sample_blocks_controller.rb +0 -3
  228. data/test/dummy/app/controllers/content_page_controller.rb +0 -13
  229. data/test/dummy/app/helpers/application_helper.rb +0 -2
  230. data/test/dummy/app/helpers/content_page_helper.rb +0 -2
  231. data/test/dummy/app/mailers/.gitkeep +0 -0
  232. data/test/dummy/app/models/.gitkeep +0 -0
  233. data/test/dummy/app/models/cms/sample_block.rb +0 -22
  234. data/test/dummy/app/models/product.rb +0 -5
  235. data/test/dummy/app/views/cms/products/_form.html.erb +0 -7
  236. data/test/dummy/app/views/cms/products/render.html.erb +0 -3
  237. data/test/dummy/app/views/content_page/custom_page.html.erb +0 -3
  238. data/test/dummy/app/views/content_page/index.html.erb +0 -2
  239. data/test/dummy/app/views/layouts/application.html.erb +0 -14
  240. data/test/dummy/app/views/layouts/templates/default.html.erb +0 -17
  241. data/test/dummy/app/views/layouts/templates/subpage.html.erb +0 -16
  242. data/test/dummy/app/views/test_route/index.html.erb +0 -14
  243. data/test/dummy/config.ru +0 -4
  244. data/test/dummy/config/application.rb +0 -45
  245. data/test/dummy/config/boot.rb +0 -10
  246. data/test/dummy/config/database.yml +0 -27
  247. data/test/dummy/config/environment.rb +0 -5
  248. data/test/dummy/config/environments/development.rb +0 -32
  249. data/test/dummy/config/environments/production.rb +0 -60
  250. data/test/dummy/config/environments/test.rb +0 -46
  251. data/test/dummy/config/initializers/backtrace_silencers.rb +0 -7
  252. data/test/dummy/config/initializers/browsercms.rb +0 -7
  253. data/test/dummy/config/initializers/inflections.rb +0 -10
  254. data/test/dummy/config/initializers/mime_types.rb +0 -5
  255. data/test/dummy/config/initializers/quiet_sprocket_assets.rb +0 -13
  256. data/test/dummy/config/initializers/secret_token.rb +0 -7
  257. data/test/dummy/config/initializers/session_store.rb +0 -8
  258. data/test/dummy/config/initializers/wrap_parameters.rb +0 -14
  259. data/test/dummy/config/locales/en.yml +0 -5
  260. data/test/dummy/config/routes.rb +0 -23
  261. data/test/dummy/db/migrate/20111228141250_create_products.rb +0 -16
  262. data/test/dummy/db/seeds.rb +0 -1
  263. data/test/dummy/lib/assets/.gitkeep +0 -0
  264. data/test/dummy/public/404.html +0 -26
  265. data/test/dummy/public/422.html +0 -26
  266. data/test/dummy/public/500.html +0 -26
  267. data/test/dummy/public/favicon.ico +0 -0
  268. data/test/dummy/script/rails +0 -6
  269. data/test/dummy/test/functional/content_page_controller_test.rb +0 -9
  270. data/test/dummy/test/unit/helpers/content_page_helper_test.rb +0 -4
  271. data/test/factories.rb +0 -235
  272. data/test/fixtures/multipart/foo.jpg +0 -0
  273. data/test/fixtures/multipart/sample_upload.txt +0 -1
  274. data/test/fixtures/multipart/second_upload.txt +0 -1
  275. data/test/fixtures/multipart/test.jpg +0 -0
  276. data/test/fixtures/multipart/version1.txt +0 -1
  277. data/test/fixtures/multipart/version2.txt +0 -1
  278. data/test/functional/cms/cache_controller_test.rb +0 -16
  279. data/test/functional/cms/categories_controller_test.rb +0 -28
  280. data/test/functional/cms/connectors_controller_test.rb +0 -64
  281. data/test/functional/cms/content_block_controller_test.rb +0 -127
  282. data/test/functional/cms/content_controller_test.rb +0 -351
  283. data/test/functional/cms/dashboard_controller_test.rb +0 -20
  284. data/test/functional/cms/file_blocks_controller_test.rb +0 -55
  285. data/test/functional/cms/home_controller_test.rb +0 -160
  286. data/test/functional/cms/html_blocks_controller_test.rb +0 -159
  287. data/test/functional/cms/image_blocks_controller_test.rb +0 -78
  288. data/test/functional/cms/links_controller_test.rb +0 -92
  289. data/test/functional/cms/log/test.log +0 -0
  290. data/test/functional/cms/pages_controller_test.rb +0 -233
  291. data/test/functional/cms/portlets_controller_test.rb +0 -57
  292. data/test/functional/cms/sections_controller_test.rb +0 -234
  293. data/test/functional/cms/sessions_controller_test.rb +0 -80
  294. data/test/functional/cms/tasks_controller_test.rb +0 -64
  295. data/test/functional/cms/toolbar_controller_test.rb +0 -76
  296. data/test/functional/cms/users_controller_test.rb +0 -218
  297. data/test/integration/cms/password_management_test.rb +0 -66
  298. data/test/integration/sitemap_performance_test.rb +0 -26
  299. data/test/mock_file.rb +0 -33
  300. data/test/performance/browsing_test.rb +0 -9
  301. data/test/support/engine_controller_hacks.rb +0 -34
  302. data/test/support/factory_helpers.rb +0 -57
  303. data/test/support/rails_3_1_routes_hack.rb +0 -70
  304. data/test/test_helper.rb +0 -199
  305. data/test/test_logging.rb +0 -67
  306. data/test/unit/active_record_callbacks.rb +0 -50
  307. data/test/unit/behaviors/attaching_test.rb +0 -370
  308. data/test/unit/behaviors/cms_user_test.rb +0 -67
  309. data/test/unit/behaviors/connectable_test.rb +0 -32
  310. data/test/unit/behaviors/connecting_test.rb +0 -56
  311. data/test/unit/behaviors/dynamic_attributes_test.rb +0 -74
  312. data/test/unit/behaviors/namespacing_test.rb +0 -76
  313. data/test/unit/behaviors/publishable_test.rb +0 -83
  314. data/test/unit/behaviors/rendering_test.rb +0 -68
  315. data/test/unit/behaviors/searching_test.rb +0 -102
  316. data/test/unit/behaviors/taggable_test.rb +0 -110
  317. data/test/unit/behaviors/userstamping_test.rb +0 -27
  318. data/test/unit/behaviors/versioning_test.rb +0 -102
  319. data/test/unit/extensions/active_record/base_test.rb +0 -25
  320. data/test/unit/extensions/hash_test.rb +0 -26
  321. data/test/unit/extensions/integer_test.rb +0 -10
  322. data/test/unit/extensions/string_test.rb +0 -14
  323. data/test/unit/factories_test.rb +0 -50
  324. data/test/unit/generators/install_generator_test.rb +0 -15
  325. data/test/unit/helpers/application_helper_test.rb +0 -104
  326. data/test/unit/helpers/date_picker_test.rb +0 -17
  327. data/test/unit/helpers/menu_helper_test.rb +0 -240
  328. data/test/unit/helpers/page_helper_test.rb +0 -69
  329. data/test/unit/helpers/path_helper_test.rb +0 -38
  330. data/test/unit/helpers/rendering_helper_test.rb +0 -8
  331. data/test/unit/lib/acts_as_content_page_test.rb +0 -72
  332. data/test/unit/lib/cms/authentication/controller_test.rb +0 -20
  333. data/test/unit/lib/cms/engine_helper_test.rb +0 -119
  334. data/test/unit/lib/cms/sitemap_test.rb +0 -210
  335. data/test/unit/lib/cms_domain_support_test.rb +0 -44
  336. data/test/unit/lib/command_line_test.rb +0 -70
  337. data/test/unit/lib/content_block_test.rb +0 -304
  338. data/test/unit/lib/content_rendering_support_test.rb +0 -40
  339. data/test/unit/lib/generators_test.rb +0 -40
  340. data/test/unit/lib/routes_test.rb +0 -98
  341. data/test/unit/mock_file_test.rb +0 -19
  342. data/test/unit/models/attachment_test.rb +0 -160
  343. data/test/unit/models/category_test.rb +0 -40
  344. data/test/unit/models/category_type_test.rb +0 -8
  345. data/test/unit/models/connector_test.rb +0 -152
  346. data/test/unit/models/content_type_group_test.rb +0 -26
  347. data/test/unit/models/content_type_test.rb +0 -177
  348. data/test/unit/models/dynamic_views_test.rb +0 -36
  349. data/test/unit/models/email_page_portlet_test.rb +0 -20
  350. data/test/unit/models/file_block_test.rb +0 -246
  351. data/test/unit/models/group_test.rb +0 -29
  352. data/test/unit/models/html_block_test.rb +0 -121
  353. data/test/unit/models/image_block_test.rb +0 -35
  354. data/test/unit/models/link_test.rb +0 -52
  355. data/test/unit/models/namespaces_test.rb +0 -57
  356. data/test/unit/models/page_partial_test.rb +0 -37
  357. data/test/unit/models/page_route_test.rb +0 -113
  358. data/test/unit/models/page_template_test.rb +0 -50
  359. data/test/unit/models/page_test.rb +0 -879
  360. data/test/unit/models/permission_test.rb +0 -10
  361. data/test/unit/models/portlet_test.rb +0 -99
  362. data/test/unit/models/sections_test.rb +0 -278
  363. data/test/unit/models/site_test.rb +0 -50
  364. data/test/unit/models/task_test.rb +0 -150
  365. data/test/unit/models/user_test.rb +0 -358
  366. data/test/unit/schema_statements_test.rb +0 -137
  367. data/todo_list.markdown +0 -50
@@ -67,7 +67,9 @@ class Cms::SectionsController < Cms::BaseController
67
67
  @move_to = Section.root.first
68
68
  end
69
69
  end
70
-
70
+
71
+ # Generates XML for Browsing files/pages/etc.
72
+ # @todo This MIGHT best live in the FCKeditor module, since it's XML format is highly coupled to that module's needs.
71
73
  def file_browser
72
74
  @section = Cms::Section.find_by_name_path(params[:CurrentFolder])
73
75
  if request.post? && params[:NewFile]
@@ -112,7 +114,7 @@ class Cms::SectionsController < Cms::BaseController
112
114
  else
113
115
  @section.pages
114
116
  end
115
- render 'cms/sections/file_browser.xml.builder', :layout => false
117
+ render 'cms/sections/file_browser', :layout => false, :format=>:xml
116
118
  end
117
119
 
118
120
  def public_groups
@@ -1,14 +1,18 @@
1
1
  module Cms
2
- class ToolbarController < Cms::BaseController
2
+ class ToolbarController < Cms::BaseController
3
3
 
4
- def index
5
- if params[:page_toolbar] != "0"
6
- @mode = params[:mode]
7
- @page_toolbar_enabled = true
4
+ layout "cms/toolbar"
5
+
6
+ helper MobileHelper
7
+
8
+ def index
9
+ if params[:page_toolbar] != "0"
10
+ @mode = params[:mode]
11
+ @page_toolbar_enabled = true
12
+ end
13
+ @page_version = params[:page_version]
14
+ @page = Page.find(params[:page_id]).as_of_version(params[:page_version]) if params[:page_id]
8
15
  end
9
- @page_version = params[:page_version]
10
- @page = Page.find(params[:page_id]).as_of_version(params[:page_version]) if params[:page_id]
11
- end
12
-
13
- end
16
+
17
+ end
14
18
  end
@@ -9,9 +9,9 @@ module Cms
9
9
  end
10
10
 
11
11
  def searchable_sections(selected = nil)
12
- root = Section.root.first
12
+ root = Section.root.first
13
13
  options = [['All sections', 'all'], [root.name, root.id]]
14
- root.master_section_list.each { |s| options << [s.full_path, s.id] }
14
+ root.master_section_list.each { |s| options << [s.full_path, s.id] }
15
15
  options_for_select(options, selected.to_i)
16
16
  end
17
17
 
@@ -24,7 +24,7 @@ module Cms
24
24
  text = select_tag(:version,
25
25
  options_for_select(page.versions.all(:order => "version desc").map { |r|
26
26
  ["v#{r.version}: #{r.version_comment} by #{r.updated_by.login} at #{time_on_date(r.updated_at)}", r.version]
27
- }, page.version),
27
+ }, page.version),
28
28
  :onchange => 'this.form.submit(); return false')
29
29
  text << javascript_tag("$('version').selectedIndex = 0") if page.live?
30
30
  text
@@ -54,7 +54,7 @@ module Cms
54
54
  #
55
55
  # @param [String] selector The CSS selector for the checkboxes that should be mass checked/unchecked.
56
56
  def check_uncheck_tag(selector)
57
- check_id = to_id(selector, "check")
57
+ check_id = to_id(selector, "check")
58
58
  uncheck_id = to_id(selector, "uncheck")
59
59
  content_for :html_head do
60
60
  html = <<HTML
@@ -71,7 +71,7 @@ HTML
71
71
  javascript_tag html
72
72
  end
73
73
 
74
- "#{link_to "Check All", '#', :id=>check_id}, #{link_to "Uncheck All", '#', :id=>uncheck_id}".html_safe
74
+ "#{link_to "Check All", '#', :id => check_id}, #{link_to "Uncheck All", '#', :id => uncheck_id}".html_safe
75
75
  end
76
76
 
77
77
  # @deprecated Use check_uncheck_tag instead. Retained for backwards compatibility w/ CMS implementations.
@@ -87,7 +87,7 @@ jQuery(function($) {
87
87
  HTML
88
88
  javascript_tag html
89
89
  end
90
- link_to name, '#', :id=>id
90
+ link_to name, '#', :id => id
91
91
  end
92
92
 
93
93
  # @deprecated Use check_uncheck_tag instead. Retained for backwards compatibility w/ CMS implementations.
@@ -103,7 +103,7 @@ jQuery(function($) {
103
103
  HTML
104
104
  javascript_tag html
105
105
  end
106
- link_to name, '#', :id=>id
106
+ link_to name, '#', :id => id
107
107
  end
108
108
 
109
109
  def span_tag(content)
@@ -117,7 +117,7 @@ HTML
117
117
  <div class="lt_button_content">
118
118
  <span>#{ content }</span>
119
119
  </div>
120
- #{image_tag "cms/lt_button_r.gif", :style=>"margin-right: 10px;"}
120
+ #{image_tag "cms/lt_button_r.gif", :style => "margin-right: 10px;"}
121
121
  </div>
122
122
  LBW
123
123
  button.html_safe
@@ -138,22 +138,26 @@ LBW
138
138
  # Fetches a list of categories for a cms_drop_down. Will prompt users to create Categories/Categories types if the proper ones don't exist.
139
139
  def categories_for(category_type_name, order="name")
140
140
  cat_type = CategoryType.named(category_type_name).first
141
- categories = cat_type ? cat_type.category_list(order) : [Category.new(:name=>"-- You must first create a 'Category Type' named '#{category_type_name}'")]
142
- categories.empty? ? [Category.new(:name=>"-- You must first create a Category with a Category Type of '#{category_type_name}'.")]: categories
141
+ categories = cat_type ? cat_type.category_list(order) : [Category.new(:name => "-- You must first create a 'Category Type' named '#{category_type_name}'")]
142
+ categories.empty? ? [Category.new(:name => "-- You must first create a Category with a Category Type of '#{category_type_name}'.")] : categories
143
143
  end
144
144
 
145
- def render_pagination(collection, collection_name, options={})
145
+ # Generates the HTML to render a paging control, if there is more than one page to be shown.
146
+ #
147
+ # @param [Array] collection List of content to be shown
148
+ # @param [Cms::ContentType] content_type The content type of the collection (used to generate links to Previous/Next)
149
+ # @param [Hash] options
150
+ def render_pagination(collection, content_type, options={})
146
151
  if collection.blank?
147
152
  content_tag(:div, "No Content", :class => "pagination")
148
153
  else
149
- collection_path = "#{collection_name}_path"
150
154
  render :partial => "cms/shared/pagination", :locals => {
151
- :collection => collection,
152
- :first_page_path => send(collection_path, {:page => 1}.merge(options)),
153
- :previous_page_path => send(collection_path, {:page => collection.previous_page ? collection.previous_page : 1}.merge(options)),
154
- :current_page_path => send(collection_path, options),
155
- :next_page_path => send(collection_path, {:page => collection.next_page ? collection.next_page : collection.current_page}.merge(options)),
156
- :last_page_path => send(collection_path, {:page => collection.total_pages}.merge(options))
155
+ :collection => collection,
156
+ :first_page_path => cms_connectable_path(content_type, {:page => 1}.merge(options)),
157
+ :previous_page_path => cms_connectable_path(content_type, {:page => collection.previous_page ? collection.previous_page : 1}.merge(options)),
158
+ :current_page_path => cms_connectable_path(content_type, options),
159
+ :next_page_path => cms_connectable_path(content_type, {:page => collection.next_page ? collection.next_page : collection.current_page}.merge(options)),
160
+ :last_page_path => cms_connectable_path(content_type, {:page => collection.total_pages}.merge(options))
157
161
  }
158
162
  end
159
163
  end
@@ -222,7 +226,7 @@ LBW
222
226
  #
223
227
  # @param [Path] The path or URL to link_to. Takes same types at url_for or link_to.
224
228
  def add_button(path, options={})
225
- classes = "button"
229
+ classes = "button"
226
230
  span_options = {:class => classes}
227
231
  link_to span_tag("&nbsp;Add&nbsp;".html_safe), path, span_options
228
232
  end
@@ -12,21 +12,21 @@ class Cms::FormBuilder < ActionView::Helpers::FormBuilder
12
12
  # * :width - The width for the select (defaults to 455px).
13
13
  #
14
14
  def drop_down(method, choices, options = {}, html_options = {})
15
- select_class = "#{@object_name}_#{method}"
16
- h_opts = add_tabindex!(@default_options.merge(html_options))
15
+ select_class = "#{@object_name}_#{method}"
16
+ h_opts = add_tabindex!(@default_options.merge(html_options))
17
17
  h_opts[:class] = select_class
18
18
 
19
- opts = objectify_options(options)
19
+ opts = objectify_options(options)
20
20
  set_default_value!(method, options)
21
21
  cms_options = options.extract_only!(:default_value, :width)
22
22
  render_cms_form_partial :fancy_drop_down,
23
23
  :object_name => @object_name, :method => method,
24
- :choices => choices, :options => opts,
24
+ :choices => choices, :options => opts,
25
25
  :cms_options => cms_options, :html_options => h_opts
26
26
  end
27
27
 
28
28
  def date_picker(method, options={})
29
- text_field(method, {:size => 10, :class => "date_picker", :value=>Cms::DatePicker.format_for_ui(@object.send(method))}.merge(options))
29
+ text_field(method, {:size => 10, :class => "date_picker", :value => Cms::DatePicker.format_for_ui(@object.send(method))}.merge(options))
30
30
  end
31
31
 
32
32
  def tag_list(options={})
@@ -51,13 +51,49 @@ class Cms::FormBuilder < ActionView::Helpers::FormBuilder
51
51
  class_eval src, __FILE__, __LINE__
52
52
  end
53
53
 
54
+ # Returns a label for a given field
55
+ # @param [Symbol] field Name of the field
56
+ # @param [String] label_value If nil, will use default logic for Rails::FormBuilder#label
57
+ def cms_label(field, label_value)
58
+ if label_value
59
+ label field, label_value
60
+ else
61
+ label field
62
+ end
63
+ end
64
+
65
+ # Returns a file upload input tag for a given block, along with label and instructions.
66
+ #
67
+ # @param [Symbol] method The name of the model this form upload is associated with
68
+ # @param [Hash] options
69
+ # @option options [String] :label (Data)
70
+ # @option options [String] :instructions (blank) Helpful tips for the person entering the field, appears blank if nothing is specified.
71
+ # @option options [Boolean] :edit_path (false) If true, render a text field to allow users to edit path for this file.
72
+ # @option options [Boolean] :edit_section (false) If true, render a select box which allows users to choose which section this attachment should be placed in.
73
+ def cms_file_field(method, options={})
74
+ @object.ensure_attachment_exists if @object.respond_to?(:ensure_attachment_exists)
75
+ render_form_field("file_field", method, options)
76
+ end
77
+
78
+ # Renders a multiple file uploader for attachments. Allows users to add as many attachments to this model as needed.
79
+ def cms_attachment_manager
80
+ defs = Cms::Attachment.definitions_for(object.class.name, :multiple)
81
+ names = defs.keys.sort
82
+ return if names.empty?
83
+
84
+ names.unshift "Select a type to upload a file" if names.size > 1
85
+ render_cms_form_partial :attachment_manager, :asset_definitions => defs, :asset_types => names
86
+ end
87
+
88
+ # @params html_options
89
+ # @options html_option [:class] - This will be overridden, so don't bother to set it
54
90
  def cms_drop_down(method, choices, options={}, html_options={})
55
91
  add_tabindex!(html_options)
56
92
  set_default_value!(method, options)
57
93
  cms_options = options.extract_only!(:label, :instructions, :default_value)
58
94
  render_cms_form_partial :drop_down,
59
95
  :object_name => @object_name, :method => method,
60
- :choices => choices, :options => options,
96
+ :choices => choices, :options => options,
61
97
  :cms_options => cms_options, :html_options => html_options
62
98
  end
63
99
 
@@ -86,20 +122,20 @@ class Cms::FormBuilder < ActionView::Helpers::FormBuilder
86
122
  set_default_value!(method, options)
87
123
  cms_options = options.extract_only!(:label, :instructions, :default_value)
88
124
  render_cms_form_partial :text_editor,
89
- :id => (options[:id] || "#{@object_name}_#{method}"),
125
+ :id => (options[:id] || "#{@object_name}_#{method}"),
90
126
  :editor_enabled => (cookies["editorEnabled"].blank? ? true : (cookies["editorEnabled"] == 'true' || cookies["editorEnabled"] == ['true'])),
91
- :object_name => @object_name, :method => method,
92
- :options => options, :cms_options => cms_options
127
+ :object_name => @object_name, :method => method,
128
+ :options => options, :cms_options => cms_options
93
129
  end
94
130
 
95
131
  # Renders instructions for a given field below the field itself. Instructions can be used to provide helpful
96
132
  # guidance to content editors including formatting help or just explaining what a field is for.
97
133
  #
98
- # Will not render if instructions are blank/nil.
134
+ # If instructions are blank/nil, then nothing will be shown.
99
135
  #
100
- # * instructions - The text of the instructions to show (Defaults to blank)
136
+ # @param [String] instructions (blank) The help text to show
101
137
  def cms_instructions(instructions)
102
- render_cms_form_partial :instructions, :instructions=>instructions
138
+ render_cms_form_partial :instructions, :instructions => instructions
103
139
  end
104
140
 
105
141
  # Renders a label and checkbox suitable for allow editors to update a boolean field.
@@ -115,7 +151,7 @@ class Cms::FormBuilder < ActionView::Helpers::FormBuilder
115
151
  add_tabindex!(options)
116
152
  set_default_value!(method, options)
117
153
  cms_options = options.extract_only!(:label, :instructions, :default_value)
118
- render_cms_form_partial "check_box", :method=>method, :options => options, :cms_options => cms_options
154
+ render_cms_form_partial "check_box", :method => method, :options => options, :cms_options => cms_options
119
155
  end
120
156
 
121
157
  #
@@ -139,10 +175,10 @@ class Cms::FormBuilder < ActionView::Helpers::FormBuilder
139
175
  set_default_value!(method, options)
140
176
  options[:default_handler] = "erb" unless options[:default_handler]
141
177
 
142
- cms_options = options.extract_only!(:label, :instructions)
178
+ cms_options = options.extract_only!(:label, :instructions)
143
179
  dropdown_options = options.extract_only!(:default_handler)
144
180
  add_tabindex!(options)
145
- render_cms_form_partial :template_editor, :method=>method, :dropdown_options=>dropdown_options, :options => options, :cms_options=>cms_options
181
+ render_cms_form_partial :template_editor, :method => method, :dropdown_options => dropdown_options, :options => options, :cms_options => cms_options
146
182
  end
147
183
  end
148
184
 
@@ -156,11 +192,23 @@ class Cms::FormBuilder < ActionView::Helpers::FormBuilder
156
192
  errors_list << @template.content_tag(:p, "There were problems with the following fields:")
157
193
  errors_list << @template.content_tag(:ul, object.errors.full_messages.map { |message| @template.content_tag(:li, message).html_safe }.join("\n").html_safe).html_safe
158
194
 
159
- @template.content_tag(:div, errors_list.html_safe, :class => "errorExplanation", :id=>"errorExplanation")
195
+ @template.content_tag(:div, errors_list.html_safe, :class => "errorExplanation", :id => "errorExplanation")
160
196
  end
161
197
 
162
198
  private
163
199
 
200
+ def render_form_field(form_control_name, method, options)
201
+ add_tabindex!(options)
202
+ set_default_value!(method, options)
203
+ cms_options = options.extract!(:label, :instructions, :default_value)
204
+ render_cms_form_partial form_control_name.to_sym,
205
+ :model_object => @object,
206
+ :object_name => @object_name,
207
+ :method => method,
208
+ :options => options,
209
+ :cms_options => cms_options
210
+ end
211
+
164
212
  def set_default_value!(method, options={})
165
213
  if options.has_key?(:default_value) && @object.send(method).blank?
166
214
  @object.send("#{method}=", options[:default_value])
@@ -182,8 +230,7 @@ class Cms::FormBuilder < ActionView::Helpers::FormBuilder
182
230
  end
183
231
 
184
232
  def render_cms_form_partial(field_type_name, locals)
185
- @template.render :partial => "cms/form_builder/cms_#{field_type_name}",
186
- :locals => {:f => self}.merge(locals)
233
+ @template.render :partial => "cms/form_builder/cms_#{field_type_name}", :locals => {:f => self}.merge(locals)
187
234
  end
188
235
 
189
236
  end
@@ -0,0 +1,19 @@
1
+ module Cms
2
+ module MobileHelper
3
+
4
+ def full_site_url
5
+ main_app.url_for(:host => Rails.configuration.cms.site_domain, :prefer_full_site => true)
6
+ end
7
+
8
+ def mobile_site_url
9
+ main_app.url_for(:host => Rails.configuration.cms.site_domain, :prefer_mobile_site => true)
10
+ end
11
+
12
+ # Determines if the mobile template exists for a given page.
13
+ # Used by view to show/hide the mobile toggle.
14
+ def mobile_template_exists?(page)
15
+ controller.template_exists?(page.layout_name, "layouts/mobile")
16
+ end
17
+ end
18
+
19
+ end
@@ -5,6 +5,30 @@ module Cms
5
5
  # From app, should be cms.xyz_path
6
6
  module PathHelper
7
7
 
8
+ # Returns the relative path to the given attachment.
9
+ # Content editors will see exact specific version path, while other users will see the 'public' url for the path.
10
+ def attachment_path_for(attachment)
11
+ return "" unless attachment
12
+ if current_user.able_to?(:edit_content)
13
+ attachment.attachment_version_path
14
+ else
15
+ attachment.url
16
+ end
17
+ end
18
+
19
+ # Returns a path to sort a table of Content Blocks by a given parameter. Retains other relevant parameters (like search criteria).
20
+ #
21
+ # @param [Cms::ContentType] content_type
22
+ # @param [String] column_to_sort The name of the column to sort on.
23
+ def cms_sortable_column_path(content_type, column_to_sort)
24
+ filtered_params = params.clone
25
+ filtered_params.delete(:action)
26
+ filtered_params.delete(:controller)
27
+ filtered_params.merge!(:order => determine_order(filtered_params[:order], column_to_sort))
28
+ cms_connectable_path(content_type.model_class, filtered_params)
29
+ end
30
+
31
+ # @deprecated Use cms_connectable_path instead.
8
32
  def cms_index_path_for(resource, options={})
9
33
  polymorphic_path(build_path_for(resource), options)
10
34
  end
@@ -23,6 +47,10 @@ module Cms
23
47
  send("new_#{resource_collection_name(resource).underscore.gsub('/', '_')}_url", options)
24
48
  end
25
49
 
50
+ # @param [Class, String] connectable The model class (i.e. HtmlBlock) or plural collection name (html_blocks) to link to
51
+ # @param [Hash] options Passed to polymorphic_path
52
+ #
53
+ # @return [String] path suitable to give to link_to
26
54
  def cms_connectable_path(connectable, options={})
27
55
  if Portlet === connectable
28
56
  cms.portlet_path(connectable)
@@ -74,11 +102,9 @@ module Cms
74
102
 
75
103
  private
76
104
 
105
+
77
106
  def build_path_for(model_or_class_or_content_type)
78
- path = []
79
- path << engine_for(model_or_class_or_content_type)
80
- path.concat path_elements_for(model_or_class_or_content_type)
81
- path
107
+ Cms::EngineAwarePathBuilder.new(model_or_class_or_content_type).build(self)
82
108
  end
83
109
 
84
110
  # Returns the name of the collection that this resource belongs to
@@ -4,8 +4,16 @@
4
4
  #
5
5
  module Cms
6
6
  module RenderingHelper
7
+
8
+ # Renders a table of attachments for a given content block.
9
+ # This is intended as a basic view of the content, and probably won't be suitable for blocks that need to be added directly to pages.
10
+ #
11
+ def attachment_viewer(content)
12
+ render :partial => 'cms/attachments/attachment_table', :locals => { :block => content, :can_delete => false }
13
+ end
14
+
7
15
  def render_connector_and_connectable(connector, connectable)
8
- logger.warn "Rendering #{connectable} "
16
+ logger.debug "Rendering #{connectable} "
9
17
  if logged_in? && @mode == "edit" && current_user.able_to_edit?(connector.page)
10
18
  render(:partial => 'cms/pages/edit_connector', :locals => { :connector => connector, :connectable => connectable})
11
19
  else
@@ -2,23 +2,23 @@ module Cms
2
2
  class AbstractFileBlock < ActiveRecord::Base
3
3
 
4
4
 
5
- set_table_name Namespacing.prefix("file_blocks")
5
+ self.table_name = Namespacing.prefix("file_blocks")
6
6
 
7
7
  validates_presence_of :name
8
8
 
9
9
  scope :by_section, lambda { |section| {
10
- :include => {:attachment => :section_node },
11
- :conditions => ["#{SectionNode.table_name}.ancestry = ?", section.node.ancestry_path] }
12
- }
10
+ :include => {:attachment => :section_node},
11
+ :conditions => ["#{SectionNode.table_name}.ancestry = ?", section.node.ancestry_path]}
12
+ }
13
13
 
14
14
  # Return the parent section for this block.
15
15
  # @return [Cms::Section]
16
16
  def parent
17
- attachment.parent
17
+ file.parent
18
18
  end
19
19
 
20
20
  def path
21
- attachment_file_path
21
+ file.url
22
22
  end
23
23
 
24
24
  def self.publishable?
@@ -1,150 +1,149 @@
1
- require 'digest/sha1'
2
- require 'fileutils'
3
-
4
1
  module Cms
5
2
  class Attachment < ActiveRecord::Base
6
3
 
7
- #----- Constants ----------------------------------------------------------------
8
- UNKNOWN_MIME_TYPE = 'application/octet-stream'
4
+ MULTIPLE = 'multiple'
9
5
 
10
- #----- Macros ----------------------------------------------------------------
11
- is_archivable
12
- is_publishable
13
- uses_soft_delete
14
- is_userstamped
15
- is_versioned
6
+ SANITIZATION_REGEXES = [[/\s/, '_'], [/[&+()]/, '-'], [/[=?!'"{}\[\]#<>%]/, '']]
7
+ #' this tic cleans up emacs ruby mode
16
8
 
17
- # Rails 3 uses a new ActionDispatch::Http::UploadedFile. This holds that during the file processing.
18
- attr_accessor :uploaded_file
9
+ self.table_name = :cms_attachments
19
10
 
20
- # This exists for backwards compatibility
21
- alias_method :temp_file, :uploaded_file
22
- alias_method :temp_file=, :uploaded_file=
11
+ cattr_accessor :definitions, :instance_writer => false
12
+ @@definitions = {}.with_indifferent_access
13
+ cattr_reader :configuration
14
+ attr_accessor :attachable_class
15
+ attr_accessible :attachable_class
23
16
 
24
- #----- Callbacks -------------------------------------------------------------
25
- before_validation :make_dirty_if_temp_file
26
- before_validation :prepend_file_path_with_slash
27
- before_validation :extract_file_extension_from_file_name
28
- before_validation :extract_file_type_from_temp_file
29
- before_validation :extract_file_size_from_temp_file
30
- before_validation :set_file_location
17
+ before_validation :set_cardinality
18
+ before_save :set_section, :sanitized_file_path_and_name
19
+ before_create :setup_attachment
31
20
 
32
- after_save :write_temp_file_to_storage_location
33
- after_save :clear_ivars
21
+ belongs_to :attachable, :polymorphic => true
34
22
 
23
+ include DefaultAccessible
24
+ attr_accessible :data, :attachable, :attachment_name
35
25
 
36
- #----- Associations ----------------------------------------------------------
26
+ validates :attachment_name, :attachable_type, :presence => true
37
27
 
38
28
  include Cms::Addressable
39
29
  include Cms::Addressable::DeprecatedPageAccessors
40
30
  has_one :section_node, :as => :node, :class_name => 'Cms::SectionNode'
41
31
  alias :node :section_node
42
32
 
43
- #----- Validations -----------------------------------------------------------
33
+ is_archivable; is_publishable; uses_soft_delete; is_userstamped; is_versioned
44
34
 
45
- validates_presence_of :temp_file, :message => "You must upload a file", :on => :create
46
- validates_presence_of :file_path
47
- validates_uniqueness_of :file_path
35
+ #TODO change this to a simple method
36
+ #def named(mid)
37
+ # find_all_by_name mid
38
+ # end
39
+ # ...or even get rid of it
40
+ scope :named, lambda { |name|
41
+ {:conditions => {:attachment_name => name.to_s}}
42
+ }
48
43
 
49
- #----- Virtual Attributes ----------------------------------------------------
44
+ scope :multiple, :conditions => {:cardinality => MULTIPLE}
50
45
 
51
- def section=(section)
52
- dirty! if self.section != section
53
- super(section)
54
- end
46
+ FILE_BLOCKS = "Cms::AbstractFileBlock"
47
+ validates_presence_of :data_file_path, :if => Proc.new { |a| a.attachable_type == FILE_BLOCKS }
48
+ validates_uniqueness_of :data_file_path, :message =>"must be unique. (Another file, image or page is already using '%{value}').", :if => Proc.new { |a| a.attachable_type == FILE_BLOCKS }
55
49
 
56
- #----- Callbacks Methods -----------------------------------------------------
50
+ class << self
57
51
 
58
- def make_dirty_if_temp_file
59
- dirty! if temp_file
60
- end
61
-
62
- def prepend_file_path_with_slash
63
- unless file_path.blank?
64
- self.file_path = "/#{file_path}" unless file_path =~ /^\//
52
+ # Makes file paths more URL friendly
53
+ def sanitize_file_path(file_path)
54
+ SANITIZATION_REGEXES.inject(file_path.to_s) do |s, (regex, replace)|
55
+ s.gsub(regex, replace)
56
+ end
65
57
  end
66
- end
67
58
 
68
- def extract_file_extension_from_file_name
69
- if file_name && file_name['.']
70
- self.file_extension = file_name.split('.').last.to_s.downcase
59
+ def definitions_for(klass, type)
60
+ definitions[klass].inject({}) { |d, (k, v)| d[k.capitalize] = v if v["type"] == type; d }
71
61
  end
72
- end
73
62
 
74
- def extract_file_type_from_temp_file
75
- unless temp_file.blank?
76
- self.file_type = temp_file.content_type
63
+ def configuration
64
+ @@configuration ||= Cms::Attachments.configuration
65
+ end
77
66
 
78
- # Some
79
- if self.file_type == nil
80
- self.file_type = UNKNOWN_MIME_TYPE
67
+ # Returns a Proc that can be used to dynamically determine styles based on the Cms::Attachment class
68
+ #
69
+ # Paperclip can handle a :styles parameter which responds to :call(Paperclip::Attachment)
70
+ def dynamically_return_styles
71
+ lambda do |paperclip_attachment|
72
+ cms_attachment = paperclip_attachment.instance
73
+
74
+ # Look up the style for the given block
75
+ if cms_attachment.has_assigned_content_type?
76
+ configuration_value(cms_attachment.content_block_class, cms_attachment.attachment_name, :styles)
77
+ else # New attachments that aren't associated with an Attaching type yet have no styles
78
+ {}
79
+ end
81
80
  end
82
81
  end
83
- end
84
82
 
85
- def extract_file_size_from_temp_file
86
- unless temp_file.blank?
87
- self.file_size = temp_file.size
83
+ def find_live_by_file_path(path)
84
+ Attachment.published.not_archived.find_by_data_file_path path
88
85
  end
89
- end
90
86
 
91
- # The file will be stored on disk at
92
- # Attachment.storage_location/year/month/day/sha1
93
- # The sha1 is a 40 character hash based on the original_filename
94
- # of the file uploaded and the current time
95
- def set_file_location
96
- unless temp_file.blank?
97
- sha1 = Digest::SHA1.hexdigest("#{temp_file.original_filename}#{Time.now.to_f}")
98
- self.file_location = "#{Time.now.strftime("%Y/%m/%d")}/#{sha1}"
99
- end
100
- end
101
87
 
102
- def write_temp_file_to_storage_location
103
- unless temp_file.blank?
104
- actual_temp_file = temp_file.tempfile
88
+ def configure_paperclip
89
+ # @todo This might be better done using subclasses of Attachment for each document instance.
90
+ # We could use single table inheritance to avoid needing to do meta configurations.
91
+ has_attached_file :data,
92
+ :url => configuration.url,
93
+ :path => configuration.path,
94
+ :styles => dynamically_return_styles,
105
95
 
106
- Rails.logger.warn "I want to write out #{temp_file.tempfile}"
107
- FileUtils.mkdir_p File.dirname(full_file_location)
108
- if actual_temp_file.path
109
- FileUtils.copy actual_temp_file.path, full_file_location
110
- else
111
- open(full_file_location, 'w') { |f| f << actual_temp_file.read }
112
- end
96
+ # Needed for versioning so that we keep all previous files.
97
+ :preserve_files => true,
98
+
99
+ #TODO: enable custom processors
100
+ :processors => configuration.processors,
101
+ :default_url => configuration.default_url,
102
+ :default_style => configuration.default_style,
103
+ :use_timestamp => configuration.use_timestamp,
104
+ :whiny => configuration.whiny,
105
+ :storage => rail_config(:storage),
106
+ :s3_credentials => rail_config(:s3_credentials),
107
+ :bucket => rail_config(:s3_bucket)
113
108
 
114
- if Cms.attachment_file_permission
115
- FileUtils.chmod Cms.attachment_file_permission, full_file_location
116
- end
117
109
  end
118
- end
119
110
 
120
- def clear_ivars
121
- @temp_file = nil
122
- @section = nil
123
- @section_id = nil
124
- end
111
+ # Looks up a value from Rails config
112
+ def rail_config(key)
113
+ Rails.configuration.cms.attachments[key]
114
+ end
125
115
 
126
- #----- Class Methods ---------------------------------------------------------
116
+ # Looks up the configuration value given:
117
+ # @param [Class] block_class The class of a block which has an attachment.
118
+ # @param [String] name_of_attachment The name of the attachment association (i.e. if was 'has_attachment :photos' then pass 'photo')
119
+ # @param [Symbol] key The key for the value to be fetched (i.e. :styles)
120
+ #
121
+ def configuration_value(block_class, name_of_attachment, key)
122
+ class_definitions = definitions[block_class]
123
+ if class_definitions == nil
124
+ raise "Couldn't find any definitions for '#{block_class}'."
125
+ end
126
+ attachment_definition = class_definitions[name_of_attachment]
127
+ if attachment_definition == nil
128
+ raise "Verify that '#{block_class}' defines an attachment named ':#{name_of_attachment}'."
129
+ end
130
+ attachment_definition[key] || configuration.send(key)
131
+ end
127
132
 
128
- def self.storage_location
129
- @storage_location ||= File.join(Rails.root, "/tmp/uploads")
130
133
  end
131
134
 
132
- def self.storage_location=(storage_location)
133
- @storage_location = storage_location
134
- end
135
135
 
136
- def self.find_live_by_file_path(file_path)
137
- Cms::Attachment.published.not_archived.first(:conditions => {:file_path => file_path})
136
+ def section=(section)
137
+ dirty! if self.section != section
138
+ super(section)
138
139
  end
139
140
 
140
- #----- Instance Methods ------------------------------------------------------
141
-
142
- def file_name
143
- file_path ? file_path.split('/').last : nil
141
+ def config_value_for(key)
142
+ self.class.configuration_value(content_block_class, attachment_name, key)
144
143
  end
145
144
 
146
- def name
147
- file_name
145
+ def content_block_class
146
+ attachable_class || attachable.try(:class).try(:name) || attachable_type
148
147
  end
149
148
 
150
149
  def icon
@@ -160,25 +159,115 @@ module Cms
160
159
  :xml => %w[xml],
161
160
  :zip => %w[zip rar tar gz tgz]
162
161
  }.each do |icon, extensions|
163
- return icon if extensions.include?(file_extension.to_s)
162
+ return icon if extensions.include?(data_file_extension)
164
163
  end
165
164
  :file
166
165
  end
167
166
 
167
+ # For authorized users, return the path to get the specific version of the file associated with this attachment.
168
+ # Guests should always get 'data_file_path' which is the public version of the asset.
169
+ def attachment_version_path
170
+ "/cms/attachments/#{id}?version=#{version}"
171
+ end
172
+
168
173
  def public?
169
174
  section ? section.public? : false
170
175
  end
171
176
 
172
- def full_file_location
173
- File.join(Cms::Attachment.storage_location, file_location)
177
+ def is_image?
178
+ %w[jpg gif png jpeg].include?(data_file_extension)
179
+ end
180
+
181
+ # Returns a Paperclip generated relative path to the file (with thumbnail sizing)
182
+ def url(style_name = configuration.default_style)
183
+ data.url(style_name)
184
+ end
185
+
186
+ # Returns the absolute file location of the underlying asset
187
+ def path(style_name = configuration.default_style)
188
+ data.path(style_name)
189
+ end
190
+
191
+ def original_filename
192
+ data_file_name
193
+ end
194
+
195
+ alias :file_name :original_filename
196
+
197
+ def size
198
+ data_file_size
199
+ end
200
+
201
+ def content_type
202
+ data_content_type
203
+ end
204
+
205
+ alias :file_type :content_type
206
+
207
+ # Returns the definitions for this particular attachment type.
208
+ # @return [Hash] Empty Hash if no definition have been defined for this attachment.
209
+ def config
210
+ content_defs = definitions[content_block_class] ? definitions[content_block_class] : {}
211
+ content_defs[attachment_name] ? content_defs[attachment_name] : {}
212
+ end
213
+
214
+ # Determines if this Attachment has access to configuration information yet. Until it is assigned to an Attaching object,
215
+ # it will lack style information.
216
+ def has_assigned_content_type?()
217
+ attachable_type && attachment_name
218
+ end
219
+
220
+ protected
221
+
222
+ private
223
+
224
+ def data_file_extension
225
+ data_file_name.split('.').last.downcase if data_file_name && data_file_name['.']
226
+ end
227
+
228
+ # Filter - Ensure that paths are going to URL friendly (and won't need encoding for special characters.')
229
+ def sanitized_file_path_and_name
230
+ if data_file_path
231
+ self.data_file_path = self.class.sanitize_file_path(data_file_path)
232
+ if !data_file_path.empty? && !data_file_path.starts_with?("/")
233
+ self.data_file_path = "/#{data_file_path}"
234
+ end
235
+ end
236
+
237
+ if data_file_name
238
+ self.data_file_name = self.class.sanitize_file_path(data_file_name)
239
+ end
240
+ end
241
+
242
+ def set_section
243
+ unless parent
244
+ self.parent = Section.root.first
245
+ end
246
+ end
247
+
248
+ def setup_attachment
249
+ data.instance_variable_set :@url, config_value_for(:url)
250
+ data.instance_variable_set :@path, config_value_for(:path)
251
+ data.instance_variable_set :@styles, config_value_for(:styles)
252
+ data.instance_variable_set :@normalized_styles, nil
253
+ data.send :post_process_styles
254
+ end
255
+
256
+ # Attachments should always be configured with a cardinality
257
+ def set_cardinality
258
+ unless cardinality
259
+ self.cardinality = config[:type].to_s
260
+ end
261
+
174
262
  end
175
263
 
176
264
  # Forces this record to be changed, even if nothing has changed
177
265
  # This is necessary if just the section.id has changed, for example
266
+ # test if this is necessary now that the attributes are in the
267
+ # model itself.
178
268
  def dirty!
179
269
  # Seems like a hack, is there a better way?
180
270
  self.updated_at = Time.now
181
271
  end
182
-
183
272
  end
184
- end
273
+ end