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
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CreatePageVersions < ActiveRecord::Migration[5.2]
4
+ def change
5
+ create_table :alchemy_page_versions do |t|
6
+ t.references :page,
7
+ null: false,
8
+ index: true,
9
+ foreign_key: {
10
+ to_table: :alchemy_pages,
11
+ on_delete: :cascade,
12
+ }
13
+ t.datetime :public_on
14
+ t.datetime :public_until
15
+ t.index [:public_on, :public_until]
16
+ t.timestamps
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AddPageVersionIdToAlchemyElements < ActiveRecord::Migration[5.2]
4
+ class LocalPage < ActiveRecord::Base
5
+ self.table_name = :alchemy_pages
6
+ has_many :elements, class_name: "LocalElement", inverse_of: :page
7
+ has_many :versions, class_name: "LocalVersion", inverse_of: :page, foreign_key: :page_id
8
+ end
9
+
10
+ class LocalVersion < ActiveRecord::Base
11
+ self.table_name = :alchemy_page_versions
12
+ belongs_to :page, class_name: "LocalPage", inverse_of: :versions
13
+ has_many :elements, class_name: "LocalElement", inverse_of: :versions
14
+ end
15
+
16
+ class LocalElement < ActiveRecord::Base
17
+ self.table_name = :alchemy_elements
18
+ belongs_to :page, class_name: "LocalPage", inverse_of: :elements
19
+ belongs_to :page_version, class_name: "LocalVersion", inverse_of: :elements
20
+ end
21
+
22
+ def change
23
+ add_reference :alchemy_elements, :page_version,
24
+ index: false,
25
+ foreign_key: {
26
+ to_table: :alchemy_page_versions,
27
+ on_delete: :cascade,
28
+ }
29
+ add_index :alchemy_elements, [:page_version_id, :parent_element_id],
30
+ name: "idx_alchemy_elements_on_page_version_id_and_parent_element_id"
31
+ add_index :alchemy_elements, [:page_version_id, :position],
32
+ name: "idx_alchemy_elements_on_page_version_id_and_position"
33
+
34
+ # Add a page version for each page so we can add a not null constraint
35
+ reversible do |dir|
36
+ dir.up do
37
+ say_with_time "Create draft version for each page." do
38
+ LocalPage.find_each do |page|
39
+ next if page.versions.any?
40
+
41
+ page.versions.create!.tap do |version|
42
+ Alchemy::Element.where(page_id: page.id).update_all(page_version_id: version.id)
43
+ end
44
+ end
45
+ LocalVersion.count
46
+ end
47
+ end
48
+ end
49
+
50
+ change_column_null :alchemy_elements, :page_version_id, false
51
+
52
+ # Remove the existing page relation
53
+ remove_reference :alchemy_elements, :page,
54
+ null: false,
55
+ index: false,
56
+ foreign_key: {
57
+ to_table: :alchemy_pages,
58
+ on_delete: :cascade,
59
+ on_update: :cascade,
60
+ }
61
+ if index_exists? :alchemy_elements,
62
+ :parent_element_id,
63
+ name: "index_alchemy_elements_on_page_id_and_parent_element_id"
64
+ remove_index :alchemy_elements,
65
+ column: [:parent_element_id],
66
+ name: "index_alchemy_elements_on_page_id_and_parent_element_id"
67
+ end
68
+ if index_exists? :alchemy_elements,
69
+ :position,
70
+ name: "index_elements_on_page_id_and_position"
71
+ remove_index :alchemy_elements,
72
+ column: [:position],
73
+ name: "index_elements_on_page_id_and_position"
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ class RenamePublicOnAndPublicUntilOnAlchemyPages < ActiveRecord::Migration[6.0]
4
+ def change
5
+ remove_index :alchemy_pages, column: [:public_on, :public_until],
6
+ name: "index_alchemy_pages_on_public_on_and_public_until"
7
+ rename_column :alchemy_pages, :public_on, :legacy_public_on
8
+ rename_column :alchemy_pages, :public_until, :legacy_public_until
9
+ end
10
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AddSanitizedBodyToAlchemyEssenceRichtexts < ActiveRecord::Migration[6.0]
4
+ def change
5
+ add_column :alchemy_essence_richtexts, :sanitized_body, :text
6
+ end
7
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AddAlchemyEssenceHeadlines < ActiveRecord::Migration[6.0]
4
+ def change
5
+ create_table :alchemy_essence_headlines do |t|
6
+ t.text :body
7
+ t.integer :level
8
+ t.integer :size
9
+ t.timestamps
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CreateEssenceAudios < ActiveRecord::Migration[6.0]
4
+ def up
5
+ return if table_exists? :alchemy_essence_audios
6
+
7
+ create_table :alchemy_essence_audios do |t|
8
+ t.references :attachment
9
+ t.boolean :controls, default: true, null: false
10
+ t.boolean :autoplay, default: false
11
+ t.boolean :loop, default: false, null: false
12
+ t.boolean :muted, default: false, null: false
13
+ end
14
+ end
15
+
16
+ def down
17
+ drop_table :alchemy_essence_audios
18
+ end
19
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CreateEssenceVideos < ActiveRecord::Migration[6.0]
4
+ def up
5
+ return if table_exists? :alchemy_essence_videos
6
+
7
+ create_table :alchemy_essence_videos do |t|
8
+ t.references :attachment
9
+ t.string :width
10
+ t.string :height
11
+ t.boolean :allow_fullscreen, default: true, null: false
12
+ t.boolean :autoplay, default: false, null: false
13
+ t.boolean :controls, default: true, null: false
14
+ t.boolean :loop, default: false, null: false
15
+ t.boolean :muted, default: false, null: false
16
+ t.string :preload
17
+ end
18
+ end
19
+
20
+ def down
21
+ drop_table :alchemy_essence_videos
22
+ end
23
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CreateAlchemyIngredients < ActiveRecord::Migration[6.0]
4
+ def change
5
+ create_table :alchemy_ingredients do |t|
6
+ t.references :element, null: false, foreign_key: { to_table: :alchemy_elements, on_delete: :cascade }
7
+ t.string :type, index: true, null: false
8
+ t.string :role, null: false
9
+ t.text :value
10
+ if ActiveRecord::Migration.connection.adapter_name.match?(/postgres/i)
11
+ t.jsonb :data, default: {}
12
+ else
13
+ t.json :data
14
+ end
15
+ t.belongs_to :related_object, null: true, polymorphic: true, index: false
16
+ t.index [:element_id, :role], unique: true
17
+ t.index [:related_object_id, :related_object_type], name: "idx_alchemy_ingredient_relation"
18
+
19
+ t.timestamps
20
+ end
21
+ end
22
+ end
@@ -45,8 +45,10 @@ module Alchemy
45
45
  if @preview_config && uri
46
46
  uri_class.build(
47
47
  host: uri.host,
48
+ port: uri.port,
48
49
  path: page.url_path,
49
50
  userinfo: userinfo,
51
+ query: { alchemy_preview_mode: true }.to_param,
50
52
  ).to_s
51
53
  else
52
54
  routes.admin_page_path(page)
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module Alchemy
3
- Deprecation = ActiveSupport::Deprecation.new("6.0", "Alchemy")
3
+ Deprecation = ActiveSupport::Deprecation.new("6.1", "Alchemy")
4
4
  end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dragonfly/image_magick/commands"
4
+
5
+ module Alchemy
6
+ module Dragonfly
7
+ module Processors
8
+ class AutoOrient
9
+ def call(content)
10
+ ::Dragonfly::ImageMagick::Commands.convert(
11
+ content,
12
+ "-auto-orient"
13
+ )
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dragonfly/image_magick/commands"
4
+
5
+ module Alchemy
6
+ module Dragonfly
7
+ module Processors
8
+ class CropResize
9
+ include ::Dragonfly::ParamValidators
10
+
11
+ IS_CROP_ARGUMENT = ->(args_string) {
12
+ args_string.match?(::Dragonfly::ImageMagick::Processors::Thumb::CROP_GEOMETRY)
13
+ }
14
+
15
+ IS_RESIZE_ARGUMENT = ->(args_string) {
16
+ args_string.match?(::Dragonfly::ImageMagick::Processors::Thumb::RESIZE_GEOMETRY)
17
+ }
18
+
19
+ def call(content, crop_argument, resize_argument)
20
+ validate!(crop_argument, &IS_CROP_ARGUMENT)
21
+ validate!(resize_argument, &IS_RESIZE_ARGUMENT)
22
+ ::Dragonfly::ImageMagick::Commands.convert(
23
+ content,
24
+ "-crop #{crop_argument} -resize #{resize_argument}"
25
+ )
26
+ end
27
+
28
+ def update_url(attrs, _args = "", opts = {})
29
+ format = opts["format"]
30
+ attrs.ext = format if format
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -3,7 +3,7 @@
3
3
  require "alchemy/logger"
4
4
 
5
5
  module Alchemy
6
- # Loads elements from given page
6
+ # Loads elements from given page version.
7
7
  #
8
8
  # Used by {Alchemy::Page#find_elements} and {Alchemy::ElementsHelper#render_elements} helper.
9
9
  #
@@ -30,22 +30,18 @@ module Alchemy
30
30
  @options = options
31
31
  end
32
32
 
33
- # @param page [Alchemy::Page|String]
34
- # The page the elements are loaded from. You can pass a page_layout String or a {Alchemy::Page} object.
35
- # @return [ActiveRecord::Relation]
36
- def elements(page:)
37
- elements = find_elements(page)
38
-
39
- if fallback_required?(elements)
40
- elements = elements.merge(fallback_elements)
41
- end
33
+ # @param page [Alchemy::PageVersion]
34
+ # The page version the elements are loaded from.
35
+ # @return [Alchemy::ElementsRepository]
36
+ def elements(page_version:)
37
+ elements = find_elements(page_version)
42
38
 
43
39
  if options[:reverse]
44
- elements = elements.reverse_order
40
+ elements = elements.reverse
45
41
  end
46
42
 
47
43
  if options[:random]
48
- elements = elements.reorder(Arel.sql(random_function))
44
+ elements = elements.random
49
45
  end
50
46
 
51
47
  elements.offset(options[:offset]).limit(options[:count])
@@ -53,17 +49,14 @@ module Alchemy
53
49
 
54
50
  private
55
51
 
56
- attr_reader :page, :options
52
+ attr_reader :options
57
53
 
58
- def find_elements(page_or_layout)
59
- @page = get_page(page_or_layout)
60
- return Alchemy::Element.none unless page
54
+ def find_elements(page_version)
55
+ return Alchemy::ElementsRepository.none unless page_version
61
56
 
62
- if options[:fixed]
63
- elements = page.fixed_elements
64
- else
65
- elements = page.elements
66
- end
57
+ elements = Alchemy::ElementsRepository.new(page_version.elements.available)
58
+ elements = elements.not_nested
59
+ elements = options[:fixed] ? elements.fixed : elements.unfixed
67
60
 
68
61
  if options[:only]
69
62
  elements = elements.named(options[:only])
@@ -75,44 +68,5 @@ module Alchemy
75
68
 
76
69
  elements
77
70
  end
78
-
79
- def get_page(page_or_layout)
80
- case page_or_layout
81
- when Alchemy::Page
82
- page_or_layout
83
- when String
84
- Alchemy::Deprecation.warn "Passing a String as `from_page` option to " \
85
- "`render_elements` is deprecated and will be removed with Alchemy 6.0. " \
86
- "Please load the page beforehand and pass it as an object instead."
87
- Alchemy::Page.find_by(
88
- language: Alchemy::Language.current,
89
- page_layout: page_or_layout,
90
- restricted: false,
91
- )
92
- end
93
- end
94
-
95
- def fallback_required?(elements)
96
- if options[:fallback]
97
- Alchemy::Deprecation.warn "Passing `fallback` options to `render_elements` is deprecated an will be removed with Alchemy 6.0."
98
- end
99
- options[:fallback] && elements
100
- .where(Alchemy::Element.table_name => {name: options[:fallback][:for]})
101
- .none?
102
- end
103
-
104
- def fallback_elements
105
- find_elements(options[:fallback][:from])
106
- .named(options[:fallback][:with] || options[:fallback][:for])
107
- end
108
-
109
- def random_function
110
- case ActiveRecord::Base.connection_config[:adapter]
111
- when "postgresql", "sqlite3"
112
- "RANDOM()"
113
- else
114
- "RAND()"
115
- end
116
- end
117
71
  end
118
72
  end
@@ -59,10 +59,9 @@ module Alchemy #:nodoc:
59
59
  scope :from_element, ->(name) { joins(:element).where(Element.table_name => { name: name }) }
60
60
 
61
61
  delegate :restricted?, to: :page, allow_nil: true
62
- delegate :trashed?, to: :element, allow_nil: true
63
62
  delegate :public?, to: :element, allow_nil: true
64
63
 
65
- after_save :touch_element
64
+ after_update :touch_element
66
65
 
67
66
  def acts_as_essence_class
68
67
  #{name}
@@ -18,11 +18,31 @@ module Alchemy
18
18
  super
19
19
  end
20
20
 
21
+ # Renders a simple_form input that displays a datepicker
22
+ #
23
+ def datepicker(attribute_name, options = {})
24
+ options[:wrapper] = :alchemy
25
+
26
+ type = options[:as] || :date
27
+ value = options.fetch(:input_html, {}).delete(:value)
28
+ date = value || object.send(attribute_name.to_sym).presence
29
+ date = Time.zone.parse(date) if date.is_a?(String)
30
+
31
+ input_options = {
32
+ type: :text,
33
+ class: type,
34
+ data: { datepicker_type: type },
35
+ value: date&.iso8601,
36
+ }.merge(options[:input_html] || {})
37
+
38
+ input attribute_name, as: :string, input_html: input_options
39
+ end
40
+
21
41
  # Renders a button tag wrapped in a div with 'submit' class.
22
42
  #
23
43
  def submit(label, options = {})
24
44
  options = {
25
- wrapper_html: {class: "submit"},
45
+ wrapper_html: { class: "submit" },
26
46
  }.update(options)
27
47
  template.content_tag("div", options.delete(:wrapper_html)) do
28
48
  template.content_tag("button", label, options.delete(:input_html))
data/lib/alchemy/hints.rb CHANGED
@@ -35,21 +35,25 @@ module Alchemy
35
35
  # @return String
36
36
  #
37
37
  def hint
38
- hint = definition["hint"]
38
+ hint = definition[:hint]
39
39
  if hint == true
40
- Alchemy.t(name, scope: hint_translation_scope)
40
+ Alchemy.t(hint_translation_attribute, scope: hint_translation_scope)
41
41
  else
42
42
  hint
43
43
  end
44
44
  end
45
45
 
46
- # Returns true if the element has a hint
46
+ # Returns true if the element has a hint defined
47
47
  def has_hint?
48
- hint.present?
48
+ !!definition[:hint]
49
49
  end
50
50
 
51
51
  private
52
52
 
53
+ def hint_translation_attribute
54
+ name
55
+ end
56
+
53
57
  def hint_translation_scope
54
58
  "#{self.class.model_name.to_s.demodulize.downcase}_hints"
55
59
  end