alchemy_cms 5.2.0 → 6.0.0.b3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (289) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +6 -14
  3. data/.gitignore +0 -1
  4. data/.hound.yml +1 -1
  5. data/.rubocop.yml +46 -4
  6. data/CHANGELOG.md +114 -5
  7. data/Gemfile +8 -1
  8. data/README.md +5 -2
  9. data/alchemy_cms.gemspec +78 -65
  10. data/app/assets/javascripts/alchemy/admin.js +0 -2
  11. data/app/assets/javascripts/alchemy/alchemy.base.js.coffee +0 -27
  12. data/app/assets/javascripts/alchemy/alchemy.confirm_dialog.js.coffee +2 -1
  13. data/app/assets/javascripts/alchemy/alchemy.dialog.js.coffee +1 -1
  14. data/app/assets/javascripts/alchemy/alchemy.dragndrop.js.coffee +0 -25
  15. data/app/assets/javascripts/alchemy/alchemy.element_editors.js.coffee +1 -1
  16. data/app/assets/javascripts/alchemy/alchemy.elements_window.js.coffee +2 -0
  17. data/app/assets/javascripts/alchemy/alchemy.fixed_elements.js +1 -1
  18. data/app/assets/javascripts/alchemy/alchemy.gui.js.coffee +3 -1
  19. data/app/assets/javascripts/alchemy/alchemy.image_overlay.coffee +1 -1
  20. data/app/assets/javascripts/alchemy/alchemy.link_dialog.js.coffee +40 -27
  21. data/app/assets/javascripts/alchemy/templates/node_folder.hbs +1 -1
  22. data/app/assets/stylesheets/alchemy/_extends.scss +15 -2
  23. data/app/assets/stylesheets/alchemy/admin.scss +1 -1
  24. data/app/assets/stylesheets/alchemy/archive.scss +20 -5
  25. data/app/assets/stylesheets/alchemy/buttons.scss +0 -4
  26. data/app/assets/stylesheets/alchemy/elements.scss +73 -61
  27. data/app/assets/stylesheets/alchemy/images.scss +8 -0
  28. data/app/assets/stylesheets/alchemy/node-select.scss +4 -3
  29. data/app/assets/stylesheets/alchemy/page-select.scss +1 -0
  30. data/app/controllers/alchemy/admin/attachments_controller.rb +8 -4
  31. data/app/controllers/alchemy/admin/base_controller.rb +5 -7
  32. data/app/controllers/alchemy/admin/elements_controller.rb +59 -34
  33. data/app/controllers/alchemy/admin/essence_audios_controller.rb +30 -0
  34. data/app/controllers/alchemy/admin/essence_files_controller.rb +0 -14
  35. data/app/controllers/alchemy/admin/essence_pictures_controller.rb +8 -79
  36. data/app/controllers/alchemy/admin/essence_videos_controller.rb +33 -0
  37. data/app/controllers/alchemy/admin/ingredients_controller.rb +30 -0
  38. data/app/controllers/alchemy/admin/layoutpages_controller.rb +0 -1
  39. data/app/controllers/alchemy/admin/pages_controller.rb +7 -22
  40. data/app/controllers/alchemy/admin/pictures_controller.rb +56 -17
  41. data/app/controllers/alchemy/admin/resources_controller.rb +84 -10
  42. data/app/controllers/alchemy/api/elements_controller.rb +13 -4
  43. data/app/controllers/alchemy/api/pages_controller.rb +4 -3
  44. data/app/controllers/concerns/alchemy/admin/archive_overlay.rb +13 -3
  45. data/app/controllers/concerns/alchemy/admin/crop_action.rb +26 -0
  46. data/app/decorators/alchemy/element_editor.rb +26 -1
  47. data/app/decorators/alchemy/ingredient_editor.rb +158 -0
  48. data/app/helpers/alchemy/admin/elements_helper.rb +1 -0
  49. data/app/helpers/alchemy/admin/essences_helper.rb +1 -1
  50. data/app/helpers/alchemy/admin/ingredients_helper.rb +42 -0
  51. data/app/helpers/alchemy/elements_block_helper.rb +23 -6
  52. data/app/helpers/alchemy/elements_helper.rb +12 -5
  53. data/app/helpers/alchemy/pages_helper.rb +3 -11
  54. data/app/jobs/alchemy/base_job.rb +11 -0
  55. data/app/jobs/alchemy/publish_page_job.rb +11 -0
  56. data/app/models/alchemy/attachment.rb +24 -7
  57. data/app/models/alchemy/content.rb +1 -6
  58. data/app/models/alchemy/content/factory.rb +23 -27
  59. data/app/models/alchemy/element.rb +39 -72
  60. data/app/models/alchemy/element/definitions.rb +29 -27
  61. data/app/models/alchemy/element/element_contents.rb +131 -122
  62. data/app/models/alchemy/element/element_essences.rb +111 -98
  63. data/app/models/alchemy/element/element_ingredients.rb +184 -0
  64. data/app/models/alchemy/element/presenters.rb +104 -85
  65. data/app/models/alchemy/elements_repository.rb +126 -0
  66. data/app/models/alchemy/essence_audio.rb +12 -0
  67. data/app/models/alchemy/essence_headline.rb +40 -0
  68. data/app/models/alchemy/essence_picture.rb +4 -116
  69. data/app/models/alchemy/essence_richtext.rb +12 -0
  70. data/app/models/alchemy/essence_video.rb +12 -0
  71. data/app/models/alchemy/image_cropper_settings.rb +87 -0
  72. data/app/models/alchemy/ingredient.rb +183 -0
  73. data/app/models/alchemy/ingredient_validator.rb +97 -0
  74. data/app/models/alchemy/ingredients/audio.rb +29 -0
  75. data/app/models/alchemy/ingredients/boolean.rb +21 -0
  76. data/app/models/alchemy/ingredients/datetime.rb +20 -0
  77. data/app/models/alchemy/ingredients/file.rb +30 -0
  78. data/app/models/alchemy/ingredients/headline.rb +42 -0
  79. data/app/models/alchemy/ingredients/html.rb +19 -0
  80. data/app/models/alchemy/ingredients/link.rb +16 -0
  81. data/app/models/alchemy/ingredients/node.rb +23 -0
  82. data/app/models/alchemy/ingredients/page.rb +23 -0
  83. data/app/models/alchemy/ingredients/picture.rb +41 -0
  84. data/app/models/alchemy/ingredients/richtext.rb +57 -0
  85. data/app/models/alchemy/ingredients/select.rb +10 -0
  86. data/app/models/alchemy/ingredients/text.rb +17 -0
  87. data/app/models/alchemy/ingredients/video.rb +33 -0
  88. data/app/models/alchemy/language.rb +0 -11
  89. data/app/models/alchemy/page.rb +76 -33
  90. data/app/models/alchemy/page/fixed_attributes.rb +53 -51
  91. data/app/models/alchemy/page/page_elements.rb +186 -205
  92. data/app/models/alchemy/page/page_naming.rb +66 -64
  93. data/app/models/alchemy/page/page_natures.rb +139 -142
  94. data/app/models/alchemy/page/page_scopes.rb +117 -102
  95. data/app/models/alchemy/page/publisher.rb +50 -0
  96. data/app/models/alchemy/page/url_path.rb +1 -1
  97. data/app/models/alchemy/page_version.rb +58 -0
  98. data/app/models/alchemy/picture.rb +18 -40
  99. data/app/models/alchemy/picture/calculations.rb +2 -8
  100. data/app/models/alchemy/picture/preprocessor.rb +2 -0
  101. data/app/models/alchemy/picture/transformations.rb +24 -96
  102. data/app/models/concerns/alchemy/picture_thumbnails.rb +181 -0
  103. data/app/models/concerns/alchemy/touch_elements.rb +2 -2
  104. data/app/presenters/alchemy/picture_view.rb +88 -0
  105. data/app/serializers/alchemy/element_serializer.rb +5 -0
  106. data/app/serializers/alchemy/page_tree_serializer.rb +3 -2
  107. data/app/services/alchemy/delete_elements.rb +44 -0
  108. data/app/services/alchemy/duplicate_element.rb +56 -0
  109. data/app/views/alchemy/admin/attachments/_archive_overlay.html.erb +2 -3
  110. data/app/views/alchemy/admin/attachments/_file_to_assign.html.erb +3 -3
  111. data/app/views/alchemy/admin/attachments/assign.js.erb +11 -0
  112. data/app/views/alchemy/admin/attachments/index.html.erb +2 -3
  113. data/app/views/alchemy/admin/crop.html.erb +36 -0
  114. data/app/views/alchemy/admin/elements/_element.html.erb +14 -10
  115. data/app/views/alchemy/admin/elements/{_element_footer.html.erb → _footer.html.erb} +0 -0
  116. data/app/views/alchemy/admin/elements/{_new_element_form.html.erb → _form.html.erb} +1 -1
  117. data/app/views/alchemy/admin/elements/{_element_header.html.erb → _header.html.erb} +1 -1
  118. data/app/views/alchemy/admin/elements/{_element_toolbar.html.erb → _toolbar.html.erb} +5 -6
  119. data/app/views/alchemy/admin/elements/create.js.erb +1 -1
  120. data/app/views/alchemy/admin/elements/{trash.js.erb → destroy.js.erb} +2 -6
  121. data/app/views/alchemy/admin/elements/fold.js.erb +2 -2
  122. data/app/views/alchemy/admin/elements/new.html.erb +3 -3
  123. data/app/views/alchemy/admin/elements/order.js.erb +0 -17
  124. data/app/views/alchemy/admin/elements/update.js.erb +3 -2
  125. data/app/views/alchemy/admin/essence_audios/edit.html.erb +7 -0
  126. data/app/views/alchemy/admin/essence_pictures/update.js.erb +0 -1
  127. data/app/views/alchemy/admin/essence_videos/edit.html.erb +11 -0
  128. data/app/views/alchemy/admin/ingredients/_audio_fields.html.erb +4 -0
  129. data/app/views/alchemy/admin/ingredients/_file_fields.html.erb +18 -0
  130. data/app/views/alchemy/admin/ingredients/_picture_fields.html.erb +25 -0
  131. data/app/views/alchemy/admin/ingredients/_video_fields.html.erb +8 -0
  132. data/app/views/alchemy/admin/ingredients/edit.html.erb +4 -0
  133. data/app/views/alchemy/admin/layoutpages/edit.html.erb +0 -5
  134. data/app/views/alchemy/admin/nodes/_node.html.erb +2 -2
  135. data/app/views/alchemy/admin/pages/_anchor_link.html.erb +1 -1
  136. data/app/views/alchemy/admin/pages/_external_link.html.erb +1 -1
  137. data/app/views/alchemy/admin/pages/_file_link.html.erb +1 -1
  138. data/app/views/alchemy/admin/pages/_form.html.erb +0 -6
  139. data/app/views/alchemy/admin/pages/_internal_link.html.erb +1 -1
  140. data/app/views/alchemy/admin/pages/_tinymce_custom_config.html.erb +5 -2
  141. data/app/views/alchemy/admin/pages/_toolbar.html.erb +1 -1
  142. data/app/views/alchemy/admin/pages/edit.html.erb +36 -24
  143. data/app/views/alchemy/admin/pages/index.html.erb +2 -9
  144. data/app/views/alchemy/admin/partials/_remote_search_form.html.erb +2 -4
  145. data/app/views/alchemy/admin/partials/_routes.html.erb +7 -11
  146. data/app/views/alchemy/admin/partials/_search_form.html.erb +9 -0
  147. data/app/views/alchemy/admin/pictures/_archive.html.erb +1 -1
  148. data/app/views/alchemy/admin/pictures/_archive_overlay.html.erb +1 -1
  149. data/app/views/alchemy/admin/pictures/_filter_and_size_bar.html.erb +5 -7
  150. data/app/views/alchemy/admin/pictures/_infos.html.erb +0 -1
  151. data/app/views/alchemy/admin/pictures/_picture_to_assign.html.erb +4 -4
  152. data/app/views/alchemy/admin/pictures/assign.js.erb +10 -0
  153. data/app/views/alchemy/admin/pictures/index.html.erb +8 -3
  154. data/app/views/alchemy/admin/resources/_filter.html.erb +12 -0
  155. data/app/views/alchemy/admin/resources/_filter_bar.html.erb +14 -17
  156. data/app/views/alchemy/admin/resources/_form.html.erb +3 -0
  157. data/app/views/alchemy/admin/resources/_table_header.html.erb +15 -0
  158. data/app/views/alchemy/admin/resources/index.html.erb +3 -11
  159. data/app/views/alchemy/essences/_essence_audio_editor.html.erb +4 -0
  160. data/app/views/alchemy/essences/_essence_audio_view.html.erb +15 -0
  161. data/app/views/alchemy/essences/_essence_file_editor.html.erb +15 -6
  162. data/app/views/alchemy/essences/_essence_headline_editor.html.erb +36 -0
  163. data/app/views/alchemy/essences/_essence_headline_view.html.erb +10 -0
  164. data/app/views/alchemy/essences/_essence_link_editor.html.erb +8 -4
  165. data/app/views/alchemy/essences/_essence_picture_editor.html.erb +27 -12
  166. data/app/views/alchemy/essences/_essence_picture_view.html.erb +3 -3
  167. data/app/views/alchemy/essences/_essence_text_editor.html.erb +12 -4
  168. data/app/views/alchemy/essences/_essence_video_editor.html.erb +4 -0
  169. data/app/views/alchemy/essences/_essence_video_view.html.erb +18 -0
  170. data/app/views/alchemy/essences/shared/_essence_picture_tools.html.erb +21 -16
  171. data/app/views/alchemy/essences/shared/_linkable_essence_tools.html.erb +2 -2
  172. data/app/views/alchemy/ingredients/_audio_editor.html.erb +5 -0
  173. data/app/views/alchemy/ingredients/_audio_view.html.erb +14 -0
  174. data/app/views/alchemy/ingredients/_boolean_editor.html.erb +11 -0
  175. data/app/views/alchemy/ingredients/_boolean_view.html.erb +1 -0
  176. data/app/views/alchemy/ingredients/_datetime_editor.html.erb +17 -0
  177. data/app/views/alchemy/ingredients/_datetime_view.html.erb +9 -0
  178. data/app/views/alchemy/ingredients/_file_editor.html.erb +52 -0
  179. data/app/views/alchemy/ingredients/_file_view.html.erb +17 -0
  180. data/app/views/alchemy/ingredients/_headline_editor.html.erb +30 -0
  181. data/app/views/alchemy/ingredients/_headline_view.html.erb +9 -0
  182. data/app/views/alchemy/ingredients/_html_editor.html.erb +8 -0
  183. data/app/views/alchemy/ingredients/_html_view.html.erb +1 -0
  184. data/app/views/alchemy/ingredients/_link_editor.html.erb +24 -0
  185. data/app/views/alchemy/ingredients/_link_view.html.erb +9 -0
  186. data/app/views/alchemy/ingredients/_node_editor.html.erb +26 -0
  187. data/app/views/alchemy/ingredients/_node_view.html.erb +1 -0
  188. data/app/views/alchemy/ingredients/_page_editor.html.erb +25 -0
  189. data/app/views/alchemy/ingredients/_page_view.html.erb +4 -0
  190. data/app/views/alchemy/ingredients/_picture_editor.html.erb +60 -0
  191. data/app/views/alchemy/ingredients/_picture_view.html.erb +5 -0
  192. data/app/views/alchemy/ingredients/_richtext_editor.html.erb +12 -0
  193. data/app/views/alchemy/ingredients/_richtext_view.html.erb +3 -0
  194. data/app/views/alchemy/ingredients/_select_editor.html.erb +30 -0
  195. data/app/views/alchemy/ingredients/_select_view.html.erb +1 -0
  196. data/app/views/alchemy/ingredients/_text_editor.html.erb +20 -0
  197. data/app/views/alchemy/ingredients/_text_view.html.erb +16 -0
  198. data/app/views/alchemy/ingredients/_video_editor.html.erb +5 -0
  199. data/app/views/alchemy/ingredients/_video_view.html.erb +17 -0
  200. data/app/views/alchemy/ingredients/shared/_link_tools.html.erb +20 -0
  201. data/app/views/alchemy/ingredients/shared/_picture_tools.html.erb +57 -0
  202. data/config/brakeman.ignore +66 -159
  203. data/config/initializers/dragonfly.rb +10 -0
  204. data/config/locales/alchemy.en.yml +108 -64
  205. data/config/routes.rb +17 -22
  206. data/db/migrate/20201207131309_create_page_versions.rb +19 -0
  207. data/db/migrate/20201207135820_add_page_version_id_to_alchemy_elements.rb +76 -0
  208. data/db/migrate/20210205143548_rename_public_on_and_public_until_on_alchemy_pages.rb +10 -0
  209. data/db/migrate/20210326105046_add_sanitized_body_to_alchemy_essence_richtexts.rb +7 -0
  210. data/db/migrate/20210406093436_add_alchemy_essence_headlines.rb +12 -0
  211. data/db/migrate/20210506135919_create_essence_audios.rb +19 -0
  212. data/db/migrate/20210506140258_create_essence_videos.rb +23 -0
  213. data/db/migrate/20210508091432_create_alchemy_ingredients.rb +22 -0
  214. data/lib/alchemy/admin/preview_url.rb +2 -0
  215. data/lib/alchemy/deprecation.rb +1 -1
  216. data/lib/alchemy/dragonfly/processors/auto_orient.rb +18 -0
  217. data/lib/alchemy/dragonfly/processors/crop_resize.rb +35 -0
  218. data/lib/alchemy/elements_finder.rb +14 -60
  219. data/lib/alchemy/essence.rb +1 -2
  220. data/lib/alchemy/forms/builder.rb +21 -1
  221. data/lib/alchemy/hints.rb +8 -4
  222. data/lib/alchemy/page_layout.rb +0 -13
  223. data/lib/alchemy/permissions.rb +30 -29
  224. data/lib/alchemy/resource.rb +13 -3
  225. data/lib/alchemy/resource_filter.rb +40 -0
  226. data/lib/alchemy/resources_helper.rb +1 -16
  227. data/lib/alchemy/tasks/tidy.rb +29 -0
  228. data/lib/alchemy/test_support.rb +2 -11
  229. data/lib/alchemy/test_support/essence_shared_examples.rb +0 -1
  230. data/lib/alchemy/test_support/factories/element_factory.rb +8 -8
  231. data/lib/alchemy/test_support/factories/essence_audio_factory.rb +7 -0
  232. data/lib/alchemy/test_support/factories/essence_video_factory.rb +7 -0
  233. data/lib/alchemy/test_support/factories/ingredient_factory.rb +25 -0
  234. data/lib/alchemy/test_support/factories/page_factory.rb +20 -1
  235. data/lib/alchemy/test_support/factories/page_version_factory.rb +23 -0
  236. data/lib/alchemy/test_support/having_crop_action_examples.rb +170 -0
  237. data/lib/alchemy/test_support/having_picture_thumbnails_examples.rb +646 -0
  238. data/lib/alchemy/test_support/shared_ingredient_editor_examples.rb +21 -0
  239. data/lib/alchemy/test_support/shared_ingredient_examples.rb +75 -0
  240. data/lib/alchemy/tinymce.rb +17 -0
  241. data/lib/alchemy/upgrader/six_point_zero.rb +21 -0
  242. data/lib/alchemy/upgrader/tasks/add_page_versions.rb +33 -0
  243. data/lib/alchemy/upgrader/tasks/ingredients_migrator.rb +62 -0
  244. data/lib/alchemy/version.rb +1 -1
  245. data/lib/alchemy_cms.rb +1 -0
  246. data/lib/generators/alchemy/elements/elements_generator.rb +1 -0
  247. data/lib/generators/alchemy/elements/templates/view.html.erb +9 -0
  248. data/lib/generators/alchemy/elements/templates/view.html.haml +9 -0
  249. data/lib/generators/alchemy/elements/templates/view.html.slim +9 -0
  250. data/lib/generators/alchemy/ingredient/ingredient_generator.rb +38 -0
  251. data/lib/generators/alchemy/ingredient/templates/editor.html.erb +14 -0
  252. data/lib/generators/alchemy/ingredient/templates/model.rb.tt +13 -0
  253. data/lib/generators/alchemy/ingredient/templates/view.html.erb +1 -0
  254. data/lib/generators/alchemy/install/templates/dragonfly.rb.tt +1 -1
  255. data/lib/generators/alchemy/menus/templates/node.html.erb +1 -1
  256. data/lib/generators/alchemy/menus/templates/node.html.haml +1 -1
  257. data/lib/generators/alchemy/menus/templates/node.html.slim +1 -1
  258. data/lib/generators/alchemy/menus/templates/wrapper.html.erb +1 -1
  259. data/lib/generators/alchemy/menus/templates/wrapper.html.haml +1 -1
  260. data/lib/generators/alchemy/menus/templates/wrapper.html.slim +1 -1
  261. data/lib/tasks/alchemy/thumbnails.rake +4 -2
  262. data/lib/tasks/alchemy/tidy.rake +12 -0
  263. data/lib/tasks/alchemy/upgrade.rake +26 -0
  264. data/package.json +3 -2
  265. data/package/admin.js +11 -1
  266. data/package/src/__tests__/i18n.spec.js +23 -0
  267. data/package/src/file_editors.js +28 -0
  268. data/package/src/i18n.js +1 -3
  269. data/package/src/image_cropper.js +103 -0
  270. data/package/src/image_loader.js +58 -0
  271. data/package/src/node_tree.js +5 -5
  272. data/package/src/picture_editors.js +169 -0
  273. data/package/src/utils/__tests__/ajax.spec.js +20 -12
  274. data/package/src/utils/ajax.js +8 -3
  275. data/vendor/assets/javascripts/jquery_plugins/jquery.Jcrop.min.js +3 -18
  276. data/vendor/assets/stylesheets/jquery.Jcrop.min.scss +2 -28
  277. metadata +292 -55
  278. data/app/assets/javascripts/alchemy/alchemy.image_cropper.js.coffee +0 -44
  279. data/app/assets/javascripts/alchemy/alchemy.trash_window.js.coffee +0 -30
  280. data/app/assets/stylesheets/alchemy/trash.scss +0 -8
  281. data/app/controllers/alchemy/admin/trash_controller.rb +0 -44
  282. data/app/views/alchemy/admin/attachments/_filter_bar.html.erb +0 -29
  283. data/app/views/alchemy/admin/essence_files/assign.js.erb +0 -3
  284. data/app/views/alchemy/admin/essence_pictures/assign.js.erb +0 -4
  285. data/app/views/alchemy/admin/essence_pictures/crop.html.erb +0 -48
  286. data/app/views/alchemy/admin/pictures/_filter_bar.html.erb +0 -30
  287. data/app/views/alchemy/admin/trash/clear.js.erb +0 -4
  288. data/app/views/alchemy/admin/trash/index.html.erb +0 -31
  289. data/lib/alchemy/test_support/factories.rb +0 -16
@@ -63,19 +63,6 @@ module Alchemy
63
63
  mapped_layouts_for_select(selectable_layouts(language_id, only_layoutpages))
64
64
  end
65
65
 
66
- # Returns page layouts including given layout ready for Rails' select form helper.
67
- #
68
- def layouts_with_own_for_select(page_layout_name, language_id, only_layoutpages = false)
69
- layouts = selectable_layouts(language_id, only_layoutpages)
70
- if layouts.detect { |l| l["name"] == page_layout_name }.nil?
71
- @map_array = [[human_layout_name(page_layout_name), page_layout_name]]
72
- else
73
- @map_array = []
74
- end
75
- mapped_layouts_for_select(layouts)
76
- end
77
- deprecate :layouts_with_own_for_select, deprecator: Alchemy::Deprecation
78
-
79
66
  # Returns all layouts that can be used for creating a new page.
80
67
  #
81
68
  # It removes all layouts from available layouts that are unique and already taken and that are marked as hide.
@@ -36,14 +36,13 @@ module Alchemy
36
36
  module GuestUser
37
37
  def alchemy_guest_user_rules
38
38
  can([:show, :download], Alchemy::Attachment) { |a| !a.restricted? }
39
- can :see, Alchemy::Page, restricted: false
40
39
 
41
40
  can :read, Alchemy::Content, Alchemy::Content.available.not_restricted do |c|
42
- c.public? && !c.restricted? && !c.trashed?
41
+ c.public? && !c.restricted?
43
42
  end
44
43
 
45
44
  can :read, Alchemy::Element, Alchemy::Element.available.not_restricted do |e|
46
- e.public? && !e.restricted? && !e.trashed?
45
+ e.public? && !e.restricted?
47
46
  end
48
47
 
49
48
  can :read, Alchemy::Page, Alchemy::Page.published.not_restricted do |p|
@@ -64,14 +63,13 @@ module Alchemy
64
63
 
65
64
  # Resources
66
65
  can [:show, :download], Alchemy::Attachment
67
- can :see, Alchemy::Page, restricted: true
68
66
 
69
67
  can :read, Alchemy::Content, Alchemy::Content.available do |c|
70
- c.public? && !c.trashed?
68
+ c.public?
71
69
  end
72
70
 
73
71
  can :read, Alchemy::Element, Alchemy::Element.available do |e|
74
- e.public? && !e.trashed?
72
+ e.public?
75
73
  end
76
74
 
77
75
  can :read, Alchemy::Page, Alchemy::Page.published do |p|
@@ -103,24 +101,27 @@ module Alchemy
103
101
  ]
104
102
 
105
103
  # Controller actions
106
- can :leave, :alchemy_admin
107
- can [:info, :help], :alchemy_admin_dashboard
108
- can :manage, :alchemy_admin_clipboard
109
- can :index, :trash
110
- can :edit, :alchemy_admin_layoutpages
111
- can :tree, :alchemy_admin_pages
104
+ can :leave, :alchemy_admin
105
+ can [:info, :help], :alchemy_admin_dashboard
106
+ can :manage, :alchemy_admin_clipboard
107
+ can :edit, :alchemy_admin_layoutpages
108
+ can :tree, :alchemy_admin_pages
112
109
 
113
110
  # Resources
114
- can [:read, :download], Alchemy::Attachment
115
- can :manage, Alchemy::Content
116
- can :manage, Alchemy::Element
117
- can :manage, Alchemy::EssenceFile
118
- can :manage, Alchemy::EssencePicture
119
- can :manage, Alchemy::LegacyPageUrl
120
- can :manage, Alchemy::Node
121
- can :read, Alchemy::Picture
111
+ can [:read, :download], Alchemy::Attachment
112
+ can :manage, Alchemy::Content
113
+ can :manage, Alchemy::Element
114
+ can :manage, Alchemy::EssenceAudio
115
+ can :manage, Alchemy::EssenceFile
116
+ can :manage, Alchemy::EssencePicture
117
+ can :manage, Alchemy::EssenceVideo
118
+ can :manage, Alchemy::Ingredient
119
+ can [:crop], Alchemy::Ingredients::Picture
120
+ can :manage, Alchemy::LegacyPageUrl
121
+ can :manage, Alchemy::Node
122
+ can [:read, :url], Alchemy::Picture
122
123
  can [:read, :autocomplete], Alchemy::Tag
123
- can(:edit_content, Alchemy::Page) { |p| p.editable_by?(@user) }
124
+ can(:edit_content, Alchemy::Page) { |p| p.editable_by?(@user) }
124
125
  end
125
126
  end
126
127
 
@@ -140,9 +141,6 @@ module Alchemy
140
141
  :alchemy_admin_users,
141
142
  ]
142
143
 
143
- # Controller actions
144
- can :clear, :trash
145
-
146
144
  # Resources
147
145
  can [
148
146
  :copy,
@@ -164,13 +162,16 @@ module Alchemy
164
162
  can([
165
163
  :create,
166
164
  :destroy,
167
- :publish,
168
165
  ], Alchemy::Page) { |p| p.editable_by?(@user) }
169
166
 
167
+ can(:publish, Alchemy::Page) do |page|
168
+ page.language.public? && page.editable_by?(@user)
169
+ end
170
+
170
171
  can :manage, Alchemy::Picture
171
172
  can :manage, Alchemy::Attachment
172
173
  can :manage, Alchemy::Tag
173
- can :index, Alchemy::Language
174
+ can :index, Alchemy::Language
174
175
  end
175
176
  end
176
177
 
@@ -185,14 +186,14 @@ module Alchemy
185
186
  alchemy_editor_rules
186
187
 
187
188
  # Navigation
188
- can :index, [:alchemy_admin_sites, :alchemy_admin_styleguide]
189
+ can :index, [:alchemy_admin_sites, :alchemy_admin_styleguide]
189
190
 
190
191
  # Controller actions
191
192
  can [:info, :update_check], :alchemy_admin_dashboard
192
193
 
193
194
  # Resources
194
- can :manage, Alchemy::Language
195
- can :manage, Alchemy::Site
195
+ can :manage, Alchemy::Language
196
+ can :manage, Alchemy::Site
196
197
  end
197
198
  end
198
199
 
@@ -286,6 +286,11 @@ module Alchemy
286
286
  resource_relation(column_name).try(:[], :attr_type)
287
287
  end
288
288
 
289
+ def resource_relation_class(association)
290
+ class_name = association.options[:class_name] || association.name.to_s.classify
291
+ class_name.constantize
292
+ end
293
+
289
294
  def resource_column_type(col)
290
295
  resource_relation_type(col.name) || (col.try(:array) ? :array : col.type)
291
296
  end
@@ -298,10 +303,15 @@ module Alchemy
298
303
  def map_relations
299
304
  self.resource_relations = {}
300
305
  model.alchemy_resource_relations.each do |name, options|
301
- name = name.to_s.gsub(/_id$/, "") # ensure that we don't have an id
302
- association = association_from_relation_name(name)
306
+ relation_name = name.to_s.gsub(/_id$/, "") # ensure that we don't have an id
307
+ association = association_from_relation_name(relation_name)
303
308
  foreign_key = association.options[:foreign_key] || "#{association.name}_id".to_sym
304
- resource_relations[foreign_key] = options.merge(model_association: association, name: name)
309
+ collection = options[:collection] || resource_relation_class(association).all
310
+ resource_relations[foreign_key] = options.merge(
311
+ model_association: association,
312
+ name: relation_name,
313
+ collection: collection,
314
+ )
305
315
  end
306
316
  end
307
317
 
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Alchemy
4
+ class ResourceFilter
5
+ attr_reader :name
6
+
7
+ def initialize(filter, resource_name)
8
+ @filter = filter
9
+ @name = filter[:name]
10
+ @resource_name = resource_name
11
+ @values = filter[:values].presence || []
12
+ end
13
+
14
+ def options_for_select
15
+ translated_values.zip(values)
16
+ end
17
+
18
+ def values
19
+ if translated?
20
+ @values.map { |v| v[1] }
21
+ else
22
+ @values
23
+ end
24
+ end
25
+
26
+ private
27
+
28
+ def translated?
29
+ @values.first.is_a?(Array)
30
+ end
31
+
32
+ def translated_values
33
+ if translated?
34
+ @values.map { |a| a[0] }
35
+ else
36
+ @values.map { |v| Alchemy.t(v.to_sym, scope: ["filters", @resource_name, @name, "values"]) }
37
+ end
38
+ end
39
+ end
40
+ end
@@ -107,12 +107,10 @@ module Alchemy
107
107
  when "boolean"
108
108
  options
109
109
  when "date", "time", "datetime"
110
- date = resource_instance_variable.send(attribute[:name]) || Time.current
111
110
  options.merge(
112
111
  as: "string",
113
112
  input_html: {
114
- "data-datepicker-type" => input_type,
115
- value: date ? date.iso8601 : nil,
113
+ data: { datepicker_type: input_type },
116
114
  },
117
115
  )
118
116
  when "text"
@@ -171,18 +169,5 @@ module Alchemy
171
169
  def resource_has_tags
172
170
  resource_model.respond_to?(:tag_counts) && resource_model.tag_counts.any?
173
171
  end
174
-
175
- def resource_has_filters
176
- resource_model.respond_to?(:alchemy_resource_filters)
177
- end
178
-
179
- def resource_filter_select
180
- resource_model.alchemy_resource_filters.map do |filter_scope|
181
- [
182
- Alchemy.t(filter_scope.to_sym, scope: ["resources", resource_name, "filters"]),
183
- filter_scope,
184
- ]
185
- end
186
- end
187
172
  end
188
173
  end
@@ -62,6 +62,23 @@ module Alchemy
62
62
  end
63
63
  end
64
64
 
65
+ def remove_trashed_elements
66
+ puts "\n## Removing trashed elements"
67
+ elements = Alchemy::Element.unscoped.where(position: nil)
68
+ if elements.any?
69
+ log "Destroying #{elements.size} trashed elements"
70
+ nested_elements, parent_elements = elements.partition(&:parent_element_id)
71
+ (nested_elements + parent_elements).each do |element|
72
+ element.destroy
73
+ print "."
74
+ end
75
+ puts "\n"
76
+ log "Done", :message
77
+ else
78
+ log "No trashed elements found", :skip
79
+ end
80
+ end
81
+
65
82
  def remove_orphaned_contents
66
83
  puts "\n## Removing orphaned contents"
67
84
  contents = Alchemy::Content.unscoped.all
@@ -81,6 +98,18 @@ module Alchemy
81
98
  end
82
99
  end
83
100
 
101
+ def remove_duplicate_legacy_urls
102
+ puts "\n## Removing duplicate legacy URLs"
103
+ sql = <<~SQL
104
+ DELETE FROM alchemy_legacy_page_urls A USING alchemy_legacy_page_urls B
105
+ WHERE A.page_id = B.page_id
106
+ AND A.urlname = B.urlname
107
+ AND A.id < B.id
108
+ SQL
109
+ count = ActiveRecord::Base.connection.exec_delete(sql)
110
+ log "Deleted #{count} duplicate legacy URLs"
111
+ end
112
+
84
113
  private
85
114
 
86
115
  def destroy_orphaned_records(records, class_name)
@@ -2,17 +2,8 @@
2
2
 
3
3
  module Alchemy
4
4
  module TestSupport
5
- class << self
6
- def factory_paths
7
- Dir[
8
- ::Alchemy::Engine.root.join("lib", "alchemy", "test_support", "factories", "*_factory.rb")
9
- ].map { |path| path.sub(/.rb\z/, "") }
10
- end
11
- deprecate factory_paths: :factories_path, deprecator: Alchemy::Deprecation
12
-
13
- def factories_path
14
- ::Alchemy::Engine.root.join("lib", "alchemy", "test_support", "factories")
15
- end
5
+ def self.factories_path
6
+ ::Alchemy::Engine.root.join("lib", "alchemy", "test_support", "factories")
16
7
  end
17
8
  end
18
9
  end
@@ -265,7 +265,6 @@ RSpec.shared_examples_for "an essence" do
265
265
 
266
266
  context "delegations" do
267
267
  it { should delegate_method(:restricted?).to(:page) }
268
- it { should delegate_method(:trashed?).to(:element) }
269
268
  it { should delegate_method(:public?).to(:element) }
270
269
  end
271
270
 
@@ -4,7 +4,8 @@ FactoryBot.define do
4
4
  factory :alchemy_element, class: "Alchemy::Element" do
5
5
  name { "article" }
6
6
  autogenerate_contents { false }
7
- association :page, factory: :alchemy_page
7
+ autogenerate_ingredients { false }
8
+ association :page_version, factory: :alchemy_page_version
8
9
 
9
10
  trait :fixed do
10
11
  fixed { true }
@@ -16,23 +17,22 @@ FactoryBot.define do
16
17
  name { "header" }
17
18
  end
18
19
 
19
- trait :trashed do
20
- after(:create) do |element|
21
- element.update_column(:position, :null)
22
- end
23
- end
24
-
25
20
  trait :with_nestable_elements do
26
21
  name { "slider" }
27
22
  end
28
23
 
29
24
  trait :nested do
30
- association :parent_element, factory: :alchemy_element, name: "slider"
25
+ parent_element { build(:alchemy_element, name: "slider", page_version: page_version) }
31
26
  name { "slide" }
32
27
  end
33
28
 
34
29
  trait :with_contents do
35
30
  autogenerate_contents { true }
36
31
  end
32
+
33
+ trait :with_ingredients do
34
+ name { "element_with_ingredients" }
35
+ autogenerate_ingredients { true }
36
+ end
37
37
  end
38
38
  end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ FactoryBot.define do
4
+ factory :alchemy_essence_audio, class: "Alchemy::EssenceAudio" do
5
+ attachment factory: :alchemy_attachment
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ FactoryBot.define do
4
+ factory :alchemy_essence_video, class: "Alchemy::EssenceVideo" do
5
+ attachment factory: :alchemy_attachment
6
+ end
7
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ FactoryBot.define do
4
+ %w[
5
+ audio
6
+ boolean
7
+ datetime
8
+ file
9
+ headline
10
+ html
11
+ link
12
+ node
13
+ page
14
+ picture
15
+ richtext
16
+ select
17
+ text
18
+ ].each do |ingredient|
19
+ factory :"alchemy_ingredient_#{ingredient}", class: "Alchemy::Ingredients::#{ingredient.classify}" do
20
+ role { ingredient }
21
+ type { "Alchemy::Ingredients::#{ingredient.classify}" }
22
+ association :element, name: "all_you_can_eat_ingredients", factory: :alchemy_element
23
+ end
24
+ end
25
+ end
@@ -29,7 +29,26 @@ FactoryBot.define do
29
29
 
30
30
  trait :public do
31
31
  sequence(:name) { |n| "A Public Page #{n}" }
32
- public_on { Time.current }
32
+ transient do
33
+ public_on { Time.current }
34
+ public_until { nil }
35
+ end
36
+ after(:build) do |page, evaluator|
37
+ page.build_public_version(
38
+ public_on: evaluator.public_on,
39
+ public_until: evaluator.public_until,
40
+ )
41
+ end
42
+ after(:create) do |page|
43
+ if page.autogenerate_elements
44
+ page.definition["autogenerate"].each do |name|
45
+ create(:alchemy_element,
46
+ name: name,
47
+ page_version: page.public_version,
48
+ autogenerate_contents: true)
49
+ end
50
+ end
51
+ end
33
52
  end
34
53
 
35
54
  trait :layoutpage do
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ FactoryBot.define do
4
+ factory :alchemy_page_version, class: "Alchemy::PageVersion" do
5
+ association :page, factory: :alchemy_page
6
+
7
+ trait :published do
8
+ public_on { Time.current }
9
+ end
10
+
11
+ transient do
12
+ element_count { 1 }
13
+ end
14
+
15
+ trait :with_elements do
16
+ after(:build) do |page_version, evaluator|
17
+ evaluator.element_count.times do
18
+ page_version.elements.build(name: "article")
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end