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
data/doc/release_notes.md CHANGED
@@ -1,3 +1,62 @@
1
+ v3.5.0
2
+ ======
3
+
4
+ This release includes a number of new features, including:
5
+
6
+ * Improved Attachments
7
+ * Mobile Friendly templates
8
+ * Rail 3.2 compatibility
9
+ * Improved Heroku support
10
+
11
+ See the upgrade instructions here for existing projects: https://github.com/browsermedia/browsercms/wiki/Upgrading-to-3.5.x-from-3.4.x
12
+
13
+ Improved Attachments
14
+ --------------------
15
+
16
+ Attachments have been completely reworked to use Paperclip (https://github.com/thoughtbot/paperclip).
17
+
18
+ * Each block can now have multiple attachments using different styles.
19
+ * Attachments can be defined as one to one (has_attachment :image) or be stored as a collection (has_many_attachments :photos).
20
+ * Upgrade migrations are provide to migrate file and data for older projects to the new attachment structure.
21
+ * New generators have been provided to create content blocks with the new attachment styles.
22
+
23
+ See this Attachments API guide for more details: https://github.com/browsermedia/browsercms/wiki/Attachments-API
24
+
25
+ Mobile
26
+ ------
27
+
28
+ The CMS can now be configured to serve mobile optimized content, using a mobile subdomain and smart redirecting based on User Agents.
29
+
30
+ * Mobile Templates: Each template can have a 'mobile' version, which will be used when users request a mobile version of that page.
31
+ * Fallback Templates: Any page which lacks a mobile ready template will use the 'full' desktop template when displayed as mobile.
32
+ * Mobile Subdomain: Any requests to the mobile subdomain automatically serve mobile pages. m. is the assumed subdomain.
33
+ * Agent Redirection: Users on mobile devices can be automatically redirected to the mobile subdomain. (Handled via Apache User Agent detection.)
34
+ * Mobile Site Opt Out: Users on mobile device can opt to be redirected to the desktop site if they want. (Handled via a cookie)
35
+ * Mobile caching: The mobile and full sites have their own separate page cache, mean both can be served quickly by Apache.
36
+ * View as Mobile: Editors can preview the mobile templates while editing pages in the admin, if a page has a mobile template. Once in 'mobile' mode, all pages should be viewed as mobile until they disable it.
37
+
38
+ These features are originally from the bcms_mobile module, which has been inlined into the CMS core. See the [Mobile Setup Guide](https://github.com/browsermedia/browsercms/wiki/Setting-up-mobile-sites) for more information.
39
+
40
+ Improved Heroku Support
41
+ -----------------------
42
+
43
+ To better support deploying BrowserCMS to Heroku, we have put together a new guide: https://github.com/browsermedia/browsercms/wiki/Deploying-to-Heroku which covers what steps are required, as well as some considerations. For example, using Heroku requires storing files on an external service, so we refactored the core CMS and worked on a new Amazon S3 module (bcms_aws_s3) that will integrate with it.
44
+
45
+ As a side note, the CMS should work with Postgresql as well, based on our testing with Heroku (which uses Postgres by default).
46
+
47
+ Notable Fixes
48
+ -------------
49
+
50
+ * [#493] Add Mobile capability
51
+ * [#494] Speed up Cucumber Scenarios
52
+ * [#492] Upgrade to be Rails 3.2.x compatible
53
+ * [#509] Pagination works for custom blocks now
54
+ * [#508] Remove fancy file upload (probably unused and wasn't working anyway)
55
+ * [#519] Better support for Amazon/AWS S3
56
+ * [#521] Remove SITE_DOMAIN constant in favor of more conventional rails configuration methods
57
+
58
+ See the [detailed changelog](https://github.com/browsermedia/browsercms/compare/v3.4.0...v3.5.0.rc1) for more info.
59
+
1
60
  v3.4.2
2
61
  ======
3
62
 
@@ -8,6 +67,13 @@ Maintenance Release
8
67
  * [#470] Fix issue where loading throws errors on some OS's (Ubuntu)
9
68
 
10
69
 
70
+ v3.4.1
71
+ ======
72
+
73
+ Maintenance Release
74
+
75
+ * [#490] Fix issue where Javascript errors occured when in Page edit mode
76
+
11
77
  v3.4.0
12
78
  ======
13
79
 
@@ -63,6 +129,14 @@ Other Notable Fixes
63
129
 
64
130
  See the [detailed changelog](https://github.com/browsermedia/browsercms/compare/v3.3.3...v3.4.0) for a complete list of changes, as well as the [Closed Tickets for 3.4.0](https://github.com/browsermedia/browsercms/issues?milestone=1&state=closed) for a complete list of closed items.
65
131
 
132
+ v3.3.4
133
+ ======
134
+
135
+ Maintenance release
136
+
137
+ * [#503] - Searching and sort content blocks works
138
+ * [#472] - Sorting content blocks works
139
+
66
140
  v3.3.3
67
141
  ======
68
142
 
data/lib/browsercms.rb CHANGED
@@ -15,6 +15,9 @@ require 'cms/domain_support'
15
15
  require 'cms/authoring'
16
16
  require 'cms/date_picker'
17
17
  require 'cms/content_rendering_support'
18
+ require 'cms/mobile_aware'
19
+ require 'cms/attachments/configuration'
20
+ require 'cms/default_accessible'
18
21
 
19
22
  # This shouldn't be necessary, except for the need to get into the loadpath for testing.
20
23
  require 'command_line'
@@ -11,13 +11,20 @@ module Cms
11
11
  # Adds Content Block behavior to this class
12
12
  #
13
13
  # @param [Hash] options
14
+ # @option options [Boolean] :allow_attachments (true) Determines whether this content block can be marked as having attachments.
14
15
  def acts_as_content_block(options={})
15
16
  defaults = {
16
- # Set default values here.
17
+ # Set default values here.
18
+ :allow_attachments => true
17
19
  }
18
20
  options = defaults.merge(options)
19
21
 
20
- belongs_to_attachment(options[:belongs_to_attachment].is_a?(Hash) ? options[:belongs_to_attachment] : {}) if options[:belongs_to_attachment]
22
+ if options[:belongs_to_attachment].present?
23
+ raise ArgumentError.new ":belongs_to_attachment option is no longer supported. Please use :has_attachments option"
24
+ end
25
+
26
+ include Cms::DefaultAccessible
27
+ allow_attachments if options[:allow_attachments]
21
28
  is_archivable(options[:archiveable].is_a?(Hash) ? options[:archiveable] : {}) unless options[:archiveable] == false
22
29
  is_connectable(options[:connectable].is_a?(Hash) ? options[:connectable] : {}) unless options[:connectable] == false
23
30
  flush_cache_on_change(options[:flush_cache_on_change].is_a?(Hash) ? options[:flush_cache_on_change] : {}) unless options[:flush_cache_on_change] == false
@@ -30,6 +37,7 @@ module Cms
30
37
  is_versioned(options[:versioned].is_a?(Hash) ? options[:versioned] : {}) unless options[:versioned] == false
31
38
 
32
39
  include InstanceMethods
40
+ extend Cms::Behaviors::Naming
33
41
  end
34
42
 
35
43
  module InstanceMethods
@@ -4,6 +4,10 @@
4
4
  module Cms
5
5
  module Addressable
6
6
 
7
+ def self.included(model_class)
8
+ model_class.attr_accessible :parent
9
+ end
10
+
7
11
  # Returns a list of all Addressable objects that are ancestors to this record.
8
12
  # @param [Hash] options
9
13
  # @option [Symbol] :include_self If this object should be included in the Array
@@ -57,6 +61,10 @@ module Cms
57
61
  # These exist for backwards compatibility to avoid having to change tests.
58
62
  # I want to get rid of these in favor of parent and parent_id
59
63
  module DeprecatedPageAccessors
64
+
65
+ def self.included(model_class)
66
+ model_class.attr_accessible :section_id, :section
67
+ end
60
68
  include LeafNode
61
69
  include NodeAccessors
62
70
 
@@ -0,0 +1,59 @@
1
+ module Cms
2
+ module Attachments
3
+
4
+ # Can be added to Controllers to handle serving files.
5
+ module Serving
6
+
7
+ # Send the file if:
8
+ # 1. It exists:
9
+ # 2. The user has permissions to see it.
10
+ #
11
+ # The strategy used to send the file can be configured based on the config.cms.attachments.storage parameter.
12
+ # Default is:
13
+ # storage = :filesystem -> Cms::Attachments::FilesystemStrategy
14
+ def send_attachment(attachment)
15
+ if attachment
16
+ raise Cms::Errors::AccessDenied unless current_user.able_to_view?(attachment)
17
+ strategy_class = send_attachments_with
18
+ strategy_class.send_attachment(attachment, self)
19
+ end
20
+ end
21
+
22
+ def send_attachments_with
23
+ Cms::Attachments::Serving.send_attachments_with
24
+ end
25
+
26
+ # @return [#send_attachments] The strategy that will be used to serve files.
27
+ def self.send_attachments_with
28
+ storage = Rails.configuration.cms.attachments.storage
29
+ "Cms::Attachments::#{storage.to_s.classify}Strategy".constantize
30
+ end
31
+
32
+ end
33
+
34
+ class FilesystemStrategy
35
+
36
+ def self.attachments_storage_location
37
+ Rails.configuration.cms.attachments.storage_directory
38
+ end
39
+
40
+ def self.send_attachment(attachment, controller)
41
+ style = controller.params[:style]
42
+ style = "original" unless style
43
+ path_to_file = attachment.path(style)
44
+ if File.exists?(path_to_file)
45
+ Rails.logger.debug "Sending file #{path_to_file}"
46
+ controller.send_file(path_to_file,
47
+ :filename => attachment.file_name,
48
+ :type => attachment.file_type,
49
+ :disposition => "inline"
50
+ )
51
+ else
52
+ msg = "Couldn't find file #{path_to_file}'"
53
+ Rails.logger.error msg
54
+ raise ActiveRecord::RecordNotFound.new(msg)
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,88 @@
1
+ require 'paperclip'
2
+ require 'cms/attachments/attachment_serving'
3
+
4
+ module Cms
5
+ # Cms::Attachments::Configuration exposes an interface to setup paperclip for BrowserCMS.
6
+ #
7
+ # Provides defaults that are suitable for the CMS and that probably make sense for most installations.
8
+ #
9
+ #This module also declares Paperclip interpolations that are used
10
+ #to construct the default url and path.
11
+ #
12
+ # For example, the default path:
13
+ #
14
+ # ":attachments_root/:id_partition/:style/:fingerprint"
15
+ #
16
+ # expands to something like:
17
+ #
18
+ # #{Rails.root}/tmp/uploads/2011/1/30/thumb/thcu098rc87dgd
19
+ #
20
+ # but can be changed by using paperclip interpolations, either
21
+ # the ones already defined by Paperlip itself, the Cms::Assets
22
+ # module, or user defined.
23
+ module Attachments
24
+ mattr_accessor :configuration
25
+
26
+ def self.configure
27
+ configuration = self.configuration
28
+ yield configuration if block_given?
29
+ @@configuration = configuration
30
+ Attachment.configure_paperclip
31
+ end
32
+
33
+ def self.configuration
34
+ @@configuration || Configuration.new
35
+ end
36
+
37
+ # Allows each Attachment to have a customized configuration, even though there is a single Attachment class.
38
+ # Designed to allow 'typical' Paperclip configuration to be specified per attachment.
39
+ class Configuration
40
+ attr_accessor :url, :path, :styles, :processors, :default_url, :default_style, :storage, :whiny
41
+ attr_accessor :s3_credentials, :bucket
42
+ attr_accessor :file_permissions
43
+
44
+ attr_reader :use_timestamp
45
+
46
+ # Set default configurations for Attachments.
47
+ def initialize
48
+ self.url = ":attachment_file_path"
49
+ self.path = ":attachments_root/:id_partition/:style/:fingerprint"
50
+ self.styles = {}
51
+ self.processors = [:thumbnail]
52
+ self.default_url = "/:attachment/:style/missing.png"
53
+ self.default_style = :original
54
+ self.storage = :filesystem
55
+ self.whiny = false
56
+
57
+ @use_timestamp = false
58
+ end
59
+ end
60
+
61
+
62
+ # This is the typical url for attachments
63
+ # Some attachments will have a custom path (data_file_path) specified by users
64
+ # while others will just be 'defaults'. This dynamically returns path at runtime based
65
+ # on the instance of the specific instance.
66
+ Paperclip.interpolates :attachment_file_path do |asset, style|
67
+ path = asset.instance.data_file_path
68
+ unless path
69
+ path = "/attachments/#{asset.instance.id}/#{asset.instance.data_file_name}"
70
+ end
71
+ if style && style != :original
72
+ "#{path}?style=#{style}"
73
+ else
74
+ path
75
+ end
76
+ end
77
+
78
+ Paperclip.interpolates :attachments_root do |_, _|
79
+ strategy = Cms::Attachments::Serving.send_attachments_with
80
+ strategy.attachments_storage_location
81
+ end
82
+
83
+ Paperclip.interpolates :full_filename do |asset, _|
84
+ "#{asset.instance.content_block_class}_#{asset.instance.data_file_name}"
85
+ end
86
+
87
+ end
88
+ end
@@ -1,207 +1,376 @@
1
1
  module Cms
2
+ # @todo Comments need to be cleaned up to get rid of 'uses_paperclip'
2
3
  module Behaviors
4
+ # Allows one or more files to be attached to content blocks.
5
+ #
6
+ # class Book
7
+ # acts_as_content_block
8
+ # uses_paperclip
9
+ # end
10
+ #
11
+ # It would probably be nice to do something like:
12
+ #
13
+ # class Book
14
+ # acts_as_content_block :uses_paperclip => true
15
+ # end
16
+ #
17
+ # has_attached_asset and has_many_attached_assets are very similar.
18
+ # They both define a couple of methods in the content block:
19
+ #
20
+ # class Book
21
+ # uses_paperclip
22
+ #
23
+ # has_attached_asset :cover
24
+ # has_many_attached_assets :drafts
25
+ # end
26
+ #
27
+ # book = Book.new
28
+ # book.cover = nil #is basically calling: book.assets.named(:cover).first
29
+ # book.drafts = [] #is calling book.assets.named(:drafts)
30
+ #
31
+ # Book#cover and Book#drafts both return Asset objects as opposed to what
32
+ # happens with stand alone Paperclip:
33
+ #
34
+ # class Book
35
+ # has_attached_file :invoice #straight Paperclip
36
+ # end
37
+ #
38
+ # Book.new.invoice # returns an instance of Paperclip::Attachment
39
+ #
40
+ # However, Asset instances respond to most of the same methods
41
+ # Paperclip::Attachments do (at least the most usefull ones and the ones
42
+ # that make sense for this implementation). Please see asset.rb for more on
43
+ # this.
44
+ #
45
+ # At the moment, calling has_attached_asset does not enforce that only
46
+ # one asset is created, it only defines a method that returns the first one
47
+ # ActiveRecord finds. It would be possible to do if that makes sense.
48
+ #
49
+ # In terms of validations, I'm aiming to expose the same 3 class methods
50
+ # Paperclip exposes, apart from those needed by BCMS itself (like enforcing
51
+ # unique attachment paths) but this is not ready yet:
52
+ #
53
+ # validates_asset_size
54
+ # validates_asset_presence
55
+ # validates_asset_content_type
56
+ #
3
57
  module Attaching
4
- SANITIZATION_REGEXES = [[/\s/, '_'], [/[&+()]/, '-'], [/[=?!'"{}\[\]#<>%]/, '']]
5
- #' this tic cleans up emacs ruby mode
6
58
 
7
- def self.included(model_class)
8
- model_class.extend(MacroMethods)
59
+ def self.included(base)
60
+ base.extend MacroMethods
9
61
  end
10
62
 
11
63
  module MacroMethods
12
- def belongs_to_attachment?
13
- !!@belongs_to_attachment
14
- end
15
64
 
16
- def belongs_to_attachment(options={})
17
- @belongs_to_attachment = true
65
+ # Adds additional behavior to a model which allows it to have attachments.
66
+ # Typically, clients will not need to call this directly. Enabling attachments is normally done via:
67
+ #
68
+ # acts_as_content_block :allow_attachments => false
69
+ #
70
+ ## By default, blocks can have attachments.
71
+ def allow_attachments
72
+ extend ClassMethods
73
+ extend Validations
18
74
  include InstanceMethods
19
- before_validation :process_attachment
20
- before_save :update_attachment_if_changed
21
- after_save :clear_attachment_ivars
22
- belongs_to :attachment, :dependent => :destroy, :class_name => 'Cms::Attachment'
23
-
24
- validates_each :attachment_file do |record, attr, value|
25
- if record.attachment && !record.attachment.valid?
26
- record.attachment.errors.each do |err_field, err_value|
27
- if err_field.to_sym == :file_path
28
- record.errors.add(:attachment_file_path, err_value)
29
- else
30
- record.errors.add(:attachment_file, err_value)
31
- end
32
- end
33
- end
34
- end
75
+
76
+ # Allows a block to be associated with a list of uploaded attachments (done via AJAX)
77
+ attr_accessor :attachment_id_list
78
+
79
+ Cms::Attachment.definitions[self.name] = {}
80
+ has_many :attachments, :as => :attachable, :dependent => :destroy, :class_name => 'Cms::Attachment', :autosave => false
81
+
82
+ accepts_nested_attributes_for :attachments,
83
+ :allow_destroy => true,
84
+ # New attachments must have an uploaded file
85
+ :reject_if => lambda { |a| a[:data].blank? && a[:id].blank? }
86
+ attr_accessible :attachments_attributes, :attachment_id_list
87
+
88
+ validates_associated :attachments
89
+ before_validation :initialize_attachments, :check_for_updated_attachments
90
+ after_validation :filter_generic_attachment_errors
91
+
92
+ before_create :associate_new_attachments
93
+ before_save :ensure_status_matches_attachable
94
+
95
+ after_save :save_associated_attachments
96
+
35
97
  end
36
98
  end
37
- module InstanceMethods
38
99
 
39
- def attachment_file
40
- @attachment_file ||= attachment ? attachment.temp_file : nil
100
+ #NOTE: Assets should be validated when created individually.
101
+ module Validations
102
+ def validates_attachment_size(name, options = {})
103
+
104
+ min = options[:greater_than] || (options[:in] && options[:in].first) || 0
105
+ max = options[:less_than] || (options[:in] && options[:in].last) || (1.0/0)
106
+ range = (min..max)
107
+ message = options[:message] || "#{name.to_s.capitalize} file size must be between :min and :max bytes."
108
+ message = message.gsub(/:min/, min.to_s).gsub(/:max/, max.to_s)
109
+
110
+ #options[:unless] = Proc.new {|r| r.a.asset_name != name.to_s}
111
+
112
+ validate(options) do |record|
113
+ record.attachments.each do |attachment|
114
+ next unless attachment.attachment_name == name.to_s
115
+ record.errors.add_to_base(message) unless range.include?(attachment.data_file_size)
116
+ end
117
+ end
41
118
  end
42
119
 
43
- def attachment_file=(file)
44
- if @attachment_file != file
45
- dirty!
46
- @attachment_file = file
120
+ def validates_attachment_presence(name, options = {})
121
+ message = options[:message] || "Must provide at least one #{name}"
122
+ validate(options) do |record|
123
+ record.errors.add(:attachment, message) unless record.attachments.any? { |a| a.attachment_name == name.to_s }
47
124
  end
48
125
  end
49
126
 
50
- def attachment_file_name
51
- @attachment_file_name ||= attachment ? attachment.file_name : nil
127
+ def validates_attachment_content_type(name, options = {})
128
+ validation_options = options.dup
129
+ allowed_types = [validation_options[:content_type]].flatten
130
+ validate(validation_options) do |record|
131
+ attachments.each do |a|
132
+ if !allowed_types.any? { |t| t === a.data_content_type } && !(a.data_content_type.nil? || a.data_content_type.blank?)
133
+ record.add_to_base(options[:message] || "is not one of #{allowed_types.join(', ')}")
134
+ end
135
+ end
136
+
137
+ end
52
138
  end
53
139
 
54
- def attachment_file_path
55
- @attachment_file_path ||= attachment ? attachment.file_path : nil
140
+ # Define at :set_attachment_path if you would like to override the way file_path is set
141
+ def handle_setting_attachment_path
142
+ if self.respond_to? :set_attachment_path
143
+ set_attachment_path
144
+ else
145
+ use_default_attachment_path
146
+ end
56
147
  end
148
+ end
149
+
150
+ module ClassMethods
151
+
152
+
153
+ # Defines an single attachement with a given name.
154
+ #
155
+ # @param [Symbol] name The name of the attachment
156
+ # @param [Hash] options Accepts most Paperclip options for Paperclip::ClassMethods.has_attached_file
157
+ # @see http://rubydoc.info/gems/paperclip/Paperclip/ClassMethods:has_attached_file
158
+ def has_attachment(name, options = {})
159
+ options[:type] = :single
160
+ options[:index] = Cms::Attachment.definitions[self.name].size
161
+ Cms::Attachment.definitions[self.name][name] = options
57
162
 
58
- def attachment_file_path=(file_path)
59
- fp = sanitize_file_path(file_path)
60
- if @attachment_file_path != fp
61
- dirty!
62
- @attachment_file_path = fp
163
+ define_method name do
164
+ attachment_named(name)
165
+ end
166
+ define_method "#{name}?" do
167
+ (attachment_named(name) != nil)
63
168
  end
64
169
  end
65
170
 
66
- def attachment_section_id
67
- @attachment_section_id ||= attachment ? attachment.section_id : nil
171
+ # Allows multiple attachments under a specific name.
172
+ #
173
+ # @param [Symbol] name The name of the attachments.
174
+ # @param [Hash] options Accepts most Paperclip options for Paperclip::ClassMethods.has_attached_file
175
+ # @see http://rubydoc.info/gems/paperclip/Paperclip/ClassMethods:has_attached_file
176
+ def has_many_attachments(name, options = {})
177
+ options[:type] = :multiple
178
+ Cms::Attachment.definitions[self.name][name] = options
179
+
180
+ define_method name do
181
+ attachments.named name
182
+ end
183
+
184
+ define_method "#{name}?" do
185
+ !attachments.named(name).empty?
186
+ end
68
187
  end
69
188
 
70
- def attachment_section_id=(section_id)
71
- if @attachment_section_id != section_id
72
- dirty!
73
- @attachment_section_id = section_id
189
+ # Find all attachments as of the given version for the specified block. Excludes attachments that were
190
+ # deleted as of a version.
191
+ #
192
+ # @param [Integer] version_number
193
+ # @param [Attaching] attachable The object with attachments
194
+ # @return [Array<Cms::Attachment>]
195
+ def attachments_as_of_version(version_number, attachable)
196
+ found_versions = Cms::Attachment::Version.where(:attachable_id => attachable.id).
197
+ where(:attachable_type => attachable.attachable_type).
198
+ where(:attachable_version => version_number).
199
+ order(:version).all
200
+ found_attachments = []
201
+
202
+ found_versions.each do |av|
203
+ record = av.build_object_from_version
204
+ found_attachments << record
74
205
  end
206
+ found_attachments.delete_if { |value| value.deleted? }
207
+ found_attachments
75
208
  end
76
209
 
77
- def attachment_section
78
- @attachment_section ||= attachment ? attachment.section : nil
210
+ # Return all definitions for a given class.
211
+ def definitions_for(name)
212
+ Cms::Attachment.definitions[self.name][name]
79
213
  end
214
+ end
80
215
 
81
- def attachment_section=(section)
82
- if @attachment_section != section
83
- dirty!
84
- @attachment_section_id = section ? section.id : nil
85
- @attachment_section = section
216
+ module InstanceMethods
217
+
218
+ # This ensures that if a change is made to an attachment, that this model is also marked as changed.
219
+ # Otherwise, if the change isn't detected, this record won't save a new version (since updates are rejected if no changes were made)
220
+ def check_for_updated_attachments
221
+ attachments.each do |a|
222
+ if a.changed?
223
+ changed_attributes['attachments'] = "Uploaded new files"
224
+ end
86
225
  end
87
226
  end
88
227
 
89
- def process_attachment
90
- Rails.logger.warn "Processing attachment (#{attachment_file})"
91
- if attachment.nil? && attachment_file.blank?
92
- unless attachment_file_path.blank?
93
- errors.add(:attachment_file, "You must upload a file")
94
- return false
95
- end
96
- unless attachment_section_id.blank?
97
- errors.add(:attachment_file, "You must upload a file")
98
- return false
99
- end
100
- else
101
- build_attachment if attachment.nil?
102
- attachment.temp_file = attachment_file
103
- handle_setting_attachment_path
104
- if attachment.file_path.blank?
105
- errors.add(:attachment_file_path, "File Name is required for attachment")
106
- return false
107
- end
108
- handle_setting_attachment_section
109
- unless attachment.section
110
- errors.add(:attachment_file, "Section is required for attachment")
111
- return false
112
- end
228
+ # Returns a list of all attachments this content type has defined.
229
+ # @return [Array<String>] Names
230
+ def attachment_names
231
+ Cms::Attachment.definitions[self.class.name].keys
232
+ end
113
233
 
114
- end
234
+ def after_publish
235
+ attachments.each &:publish
115
236
  end
116
237
 
117
- # Define at :set_attachment_path if you would like to override the way file_path is set
118
- def handle_setting_attachment_path
119
- if self.respond_to? :set_attachment_path
120
- set_attachment_path
121
- else
122
- use_default_attachment_path
123
- end
238
+ # Locates the attachment with a given name
239
+ # @param [Symbol] name The name of the attachment
240
+ def attachment_named(name)
241
+ attachments.select { |item| item.attachment_name.to_sym == name }.first
124
242
  end
125
243
 
126
- def clear_attachment_ivars
127
- @attachment_file = nil
128
- @attachment_file_path = nil
129
- @attachment_section_id = nil
130
- @attachment_section = nil
244
+ def unassigned_attachments
245
+ return [] if attachment_id_list.blank?
246
+ Cms::Attachment.find attachment_id_list.split(',').map(&:to_i)
131
247
  end
132
248
 
133
- # Implement a :set_attachment_section method if you would like to override the way the section is set
134
- def handle_setting_attachment_section
135
- if self.respond_to? :set_attachment_section
136
- set_attachment_section
137
- else
138
- use_default_attachment_section
139
- end
249
+ def all_attachments
250
+ attachments << unassigned_attachments
251
+ end
252
+
253
+ def attachable_type
254
+ self.class.name
140
255
  end
141
256
 
142
- # Default behavior for assigning a section, if a block does not define its own.
143
- def use_default_attachment_section
144
- if !attachment_file.blank?
145
- attachment.section = Cms::Section.root.first
257
+ # Versioning Callback - This will result in a new version of attachments being created every time the attachable is updated.
258
+ # Allows a complete version history to be reconstructed.
259
+ # @param [Versionable] new_version
260
+ def after_build_new_version(new_version)
261
+ attachments.each do |a|
262
+ a.attachable_version = new_version.version
146
263
  end
147
264
  end
148
265
 
149
- def use_default_attachment_path
150
- if !attachment_file.blank?
151
- attachment.file_path = "/attachments/#{File.basename(attachment_file.original_filename).to_s.downcase}"
266
+ # Version Callback - Reconstruct this object exactly as it was as of a particularly version
267
+ # Called after the object is 'reset' to the specific version in question.
268
+ def after_as_of_version()
269
+ @attachments_as_of = self.class.attachments_as_of_version(version, self)
270
+
271
+
272
+ # Override #attachments to return the original attachments for the current version.
273
+ metaclass = class << self;
274
+ self;
275
+ end
276
+ metaclass.send :define_method, :attachments do
277
+ @attachments_as_of
152
278
  end
153
279
  end
154
280
 
155
- def sanitize_file_path(file_path)
156
- SANITIZATION_REGEXES.inject(file_path.to_s) do |s, (regex, replace)|
157
- s.gsub(regex, replace)
281
+ # Callback - Ensure attachments get reverted whenver a block does.
282
+ def after_revert(version)
283
+ version_number = version.version
284
+ attachments.each do |a|
285
+ a.revert_to(version_number, {:attachable_version => self.version+1})
158
286
  end
159
287
  end
160
288
 
161
- def update_attachment_if_changed
162
- logger.debug { "UPDATE ATTACHMENT if changed" }
163
- if attachment
164
- attachment.archived = archived if self.class.archivable?
165
- if respond_to?(:revert_to_version) && revert_to_version
166
- attachment.revert_to(revert_to_version.attachment_version)
167
- elsif new_record? || attachment.changed? || attachment.temp_file
168
- saved_attach = attachment.save
169
- logger.warn "Attachment was saved = #{saved_attach}"
289
+ # Ensures that attachments exist for form, since it uses attachments.each to iterate over them.
290
+ # Design Qualm: I don't like that this method has to exist, since its basically obscuring the fact that
291
+ # individual attachments don't exist when an object is created.
292
+ def ensure_attachment_exists
293
+ attachment_names.each do |n|
294
+ unless attachment_named(n.to_sym)
295
+ # Can't use attachments.build because sometimes its an array
296
+ attachments << Attachment.new(:attachment_name => n, :attachable => self)
170
297
  end
171
- self.attachment_version = attachment.draft.version
172
298
  end
173
299
  end
174
300
 
175
- def after_publish
176
- attachment.publish if attachment
301
+ # @return [Array<Cms::Attachment>]
302
+ def multiple_attachments
303
+ attachments.select { |a| a.cardinality == Attachment::MULTIPLE }
177
304
  end
178
305
 
179
- #Size in kilobytes
180
- def file_size
181
- attachment ? "%0.2f" % (attachment.file_size / 1024.0) : "?"
306
+ private
307
+
308
+ # Saves associated attachments if they were updated. (Used in place of :autosave=>true, since the CMS Versioning API seems to break that)
309
+ #
310
+ # ActiveRecord Callback
311
+ def save_associated_attachments
312
+ attachments.each do |a|
313
+ a.save if a.changed?
314
+ end
182
315
  end
183
316
 
184
- def after_as_of_version
185
- if attachment_id && attachment_version
186
- self.attachment = Cms::Attachment.find(attachment_id).as_of_version(attachment_version)
317
+
318
+ # Filter - Ensures that the status of all attachments matches the this block
319
+ def ensure_status_matches_attachable
320
+ if self.class.archivable?
321
+ attachments.each do |a|
322
+ a.archived = self.archived
323
+ end
324
+ end
325
+
326
+ if self.class.publishable?
327
+ attachments.each do |a|
328
+ a.publish_on_save = self.publish_on_save
329
+ end
187
330
  end
188
331
  end
189
332
 
190
- def attachment_link
191
- if attachment
192
- (published? && live_version?) ? attachment_file_path : "/cms/attachments/#{attachment_id}?version=#{attachment_version}"
193
- else
194
- nil
333
+ # Handles assigning attachments that were created via use of
334
+ # the cms_asset manager.
335
+ #
336
+ # Since Attachments are created via AJAX, we need to go back and associate those with this Attaching object.
337
+ def associate_new_attachments
338
+ unless attachment_id_list.blank?
339
+ ids = attachment_id_list.split(',').map(&:to_i)
340
+ ids.each do |i|
341
+ begin
342
+ attachment = Cms::Attachment.find(i)
343
+ rescue ActiveRecord::RecordNotFound
344
+ end
345
+
346
+ # Previously saved attachments shouldn't have an attachable_version or attachable_id yet.
347
+ if attachment
348
+ attachment.attachable_version = self.version
349
+ attachments << attachment
350
+ end
351
+
352
+ end
195
353
  end
196
354
  end
197
355
 
198
- # Forces this record to be changed, even if nothing has changed
199
- # This is necessary if just the section.id has changed, for example
200
- def dirty!
201
- # Seems like a hack, is there a better way?
202
- self.updated_at = Time.now
356
+ # We don't want errors like: Attachments is invalid showing up, since they are duplicates
357
+ def filter_generic_attachment_errors
358
+ filter_errors_named([:attachments])
203
359
  end
204
360
 
361
+ def initialize_attachments
362
+ attachments.each { |a| a.attachable_class = self.class.name }
363
+ end
364
+
365
+
366
+ private
367
+ def filter_errors_named(filter_list)
368
+ filtered_errors = self.errors.reject { |err| filter_list.include?(err.first) }
369
+
370
+ # reset the errors collection and repopulate it with the filtered errors.
371
+ self.errors.clear
372
+ filtered_errors.each { |err| self.errors.add(*err) }
373
+ end
205
374
  end
206
375
  end
207
376
  end