alchemy_cms 6.1.10 → 7.0.0.pre.a

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 (249) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +0 -3
  3. data/.gitignore +1 -6
  4. data/CHANGELOG.md +19 -44
  5. data/Gemfile +1 -1
  6. data/Rakefile +14 -9
  7. data/alchemy_cms.gemspec +2 -3
  8. data/app/assets/javascripts/alchemy/alchemy.dirty.js.coffee +1 -1
  9. data/app/assets/javascripts/alchemy/alchemy.element_editors.js.coffee +18 -32
  10. data/app/assets/javascripts/alchemy/alchemy.elements_window.js.coffee +2 -2
  11. data/app/assets/javascripts/alchemy/alchemy.gui.js.coffee +2 -2
  12. data/app/assets/javascripts/alchemy/alchemy.link_dialog.js.coffee +27 -29
  13. data/app/assets/stylesheets/alchemy/elements.scss +16 -35
  14. data/app/assets/stylesheets/alchemy/forms.scss +0 -4
  15. data/app/assets/stylesheets/alchemy/node-select.scss +2 -2
  16. data/app/controllers/alchemy/admin/attachments_controller.rb +0 -1
  17. data/app/controllers/alchemy/admin/elements_controller.rb +7 -32
  18. data/app/controllers/alchemy/admin/pages_controller.rb +1 -1
  19. data/app/controllers/alchemy/admin/pictures_controller.rb +1 -1
  20. data/app/controllers/alchemy/admin/resources_controller.rb +1 -18
  21. data/app/controllers/alchemy/api/elements_controller.rb +0 -2
  22. data/app/controllers/alchemy/api/pages_controller.rb +8 -4
  23. data/app/controllers/alchemy/messages_controller.rb +9 -9
  24. data/app/controllers/alchemy/pages_controller.rb +23 -18
  25. data/app/decorators/alchemy/element_editor.rb +10 -30
  26. data/app/helpers/alchemy/admin/elements_helper.rb +0 -2
  27. data/app/helpers/alchemy/elements_block_helper.rb +5 -42
  28. data/app/helpers/alchemy/elements_helper.rb +3 -11
  29. data/app/helpers/alchemy/pages_helper.rb +0 -4
  30. data/app/models/alchemy/attachment.rb +6 -3
  31. data/app/models/alchemy/base_record.rb +2 -0
  32. data/app/models/alchemy/eager_loading.rb +0 -1
  33. data/app/models/alchemy/element/element_ingredients.rb +1 -8
  34. data/app/models/alchemy/element/presenters.rb +9 -25
  35. data/app/models/alchemy/element.rb +2 -18
  36. data/app/models/alchemy/ingredient.rb +17 -6
  37. data/app/models/alchemy/ingredients/audio.rb +2 -0
  38. data/app/models/alchemy/ingredients/datetime.rb +3 -1
  39. data/app/models/alchemy/ingredients/file.rb +7 -0
  40. data/app/models/alchemy/ingredients/headline.rb +6 -0
  41. data/app/models/alchemy/ingredients/link.rb +2 -0
  42. data/app/models/alchemy/ingredients/node.rb +2 -0
  43. data/app/models/alchemy/ingredients/page.rb +2 -0
  44. data/app/models/alchemy/ingredients/picture.rb +11 -0
  45. data/app/models/alchemy/ingredients/richtext.rb +6 -0
  46. data/app/models/alchemy/ingredients/select.rb +1 -0
  47. data/app/models/alchemy/ingredients/text.rb +8 -0
  48. data/app/models/alchemy/ingredients/video.rb +2 -0
  49. data/app/models/alchemy/node.rb +9 -6
  50. data/app/models/alchemy/page/page_elements.rb +5 -26
  51. data/app/models/alchemy/page/page_layouts.rb +0 -14
  52. data/app/models/alchemy/page/page_natures.rb +0 -10
  53. data/app/models/alchemy/page.rb +0 -10
  54. data/app/models/alchemy/picture/transformations.rb +0 -30
  55. data/app/models/alchemy/picture/url.rb +1 -1
  56. data/app/models/alchemy/picture.rb +14 -13
  57. data/app/models/alchemy/picture_thumb/create.rb +7 -18
  58. data/app/models/alchemy/picture_thumb/file_store.rb +33 -0
  59. data/app/models/alchemy/picture_thumb.rb +10 -10
  60. data/app/models/concerns/alchemy/picture_thumbnails.rb +2 -6
  61. data/app/serializers/alchemy/element_serializer.rb +1 -6
  62. data/app/services/alchemy/delete_elements.rb +1 -7
  63. data/app/services/alchemy/duplicate_element.rb +1 -6
  64. data/app/views/alchemy/admin/elements/_element.html.erb +5 -22
  65. data/app/views/alchemy/admin/elements/create.js.erb +1 -1
  66. data/app/views/alchemy/admin/elements/fold.js.erb +2 -2
  67. data/app/views/alchemy/admin/elements/order.js.erb +1 -1
  68. data/app/views/alchemy/admin/elements/update.js.erb +1 -2
  69. data/app/views/alchemy/admin/pages/_external_link.html.erb +2 -2
  70. data/app/views/alchemy/admin/pages/_file_link.html.erb +2 -2
  71. data/app/views/alchemy/admin/pages/_internal_link.html.erb +2 -2
  72. data/app/views/alchemy/admin/pages/_table.html.erb +0 -6
  73. data/app/views/alchemy/admin/pages/_tinymce_custom_config.html.erb +3 -6
  74. data/app/views/alchemy/admin/pages/edit.html.erb +1 -1
  75. data/app/views/alchemy/admin/pictures/_filter_and_size_bar.html.erb +1 -3
  76. data/app/views/alchemy/admin/pictures/_infos.html.erb +4 -6
  77. data/app/views/alchemy/admin/resources/_per_page_select.html.erb +1 -1
  78. data/app/views/alchemy/ingredients/_boolean_editor.html.erb +1 -1
  79. data/app/views/alchemy/ingredients/_headline_editor.html.erb +1 -1
  80. data/app/views/alchemy/ingredients/_html_editor.html.erb +1 -1
  81. data/app/views/alchemy/ingredients/_node_editor.html.erb +1 -1
  82. data/app/views/alchemy/ingredients/_picture_editor.html.erb +4 -4
  83. data/app/views/alchemy/ingredients/_select_editor.html.erb +2 -2
  84. data/app/views/alchemy/ingredients/_text_editor.html.erb +1 -1
  85. data/app/views/alchemy/ingredients/shared/_link_tools.html.erb +3 -3
  86. data/app/views/alchemy/pages/_meta_data.html.erb +0 -1
  87. data/app/views/layouts/alchemy/admin.html.erb +5 -1
  88. data/config/alchemy/config.yml +6 -6
  89. data/config/brakeman.ignore +56 -57
  90. data/config/locales/alchemy.en.yml +99 -113
  91. data/config/routes.rb +1 -16
  92. data/db/migrate/20230121212637_alchemy_six_point_one.rb +248 -0
  93. data/lib/alchemy/cache_digests/template_tracker.rb +6 -7
  94. data/lib/alchemy/config.rb +2 -2
  95. data/lib/alchemy/deprecation.rb +1 -1
  96. data/lib/alchemy/errors.rb +0 -11
  97. data/lib/alchemy/hints.rb +10 -10
  98. data/lib/alchemy/permissions.rb +4 -17
  99. data/lib/alchemy/routing_constraints.rb +3 -3
  100. data/lib/alchemy/searchable_resource.rb +38 -0
  101. data/lib/alchemy/seeder.rb +2 -8
  102. data/lib/alchemy/tasks/tidy.rb +0 -38
  103. data/lib/alchemy/test_support/capybara_helpers.rb +69 -0
  104. data/lib/alchemy/test_support/factories/element_factory.rb +0 -6
  105. data/lib/alchemy/test_support/factories/ingredient_factory.rb +1 -1
  106. data/lib/alchemy/test_support/factories/page_factory.rb +4 -2
  107. data/lib/alchemy/test_support/having_picture_thumbnails_examples.rb +0 -20
  108. data/lib/alchemy/test_support/shared_dom_ids_examples.rb +1 -1
  109. data/lib/alchemy/test_support/shared_ingredient_examples.rb +1 -1
  110. data/lib/alchemy/tinymce.rb +1 -18
  111. data/lib/alchemy/upgrader/seven_point_zero.rb +45 -0
  112. data/lib/alchemy/upgrader/tasks/.keep +0 -0
  113. data/lib/alchemy/upgrader.rb +8 -3
  114. data/lib/alchemy/version.rb +1 -1
  115. data/lib/alchemy.rb +0 -19
  116. data/lib/alchemy_cms.rb +1 -2
  117. data/lib/generators/alchemy/elements/elements_generator.rb +0 -1
  118. data/lib/generators/alchemy/elements/templates/view.html.erb +1 -10
  119. data/lib/generators/alchemy/elements/templates/view.html.haml +1 -9
  120. data/lib/generators/alchemy/elements/templates/view.html.slim +1 -9
  121. data/lib/generators/alchemy/install/files/alchemy.en.yml +7 -8
  122. data/lib/generators/alchemy/install/files/application.html.erb +1 -1
  123. data/lib/generators/alchemy/install/install_generator.rb +18 -34
  124. data/lib/generators/alchemy/install/templates/elements.yml.tt +12 -12
  125. data/lib/non_stupid_digest_assets.rb +1 -1
  126. data/lib/tasks/alchemy/thumbnails.rake +2 -21
  127. data/lib/tasks/alchemy/tidy.rake +1 -12
  128. data/lib/tasks/alchemy/upgrade.rake +10 -47
  129. data/package/dist/admin.js +16 -0
  130. data/package/dist/admin.js.map +7 -0
  131. data/package.json +5 -3
  132. metadata +18 -147
  133. data/app/controllers/alchemy/admin/contents_controller.rb +0 -21
  134. data/app/controllers/alchemy/admin/essence_audios_controller.rb +0 -30
  135. data/app/controllers/alchemy/admin/essence_files_controller.rb +0 -31
  136. data/app/controllers/alchemy/admin/essence_pictures_controller.rb +0 -43
  137. data/app/controllers/alchemy/admin/essence_videos_controller.rb +0 -34
  138. data/app/controllers/alchemy/api/contents_controller.rb +0 -52
  139. data/app/decorators/alchemy/content_editor.rb +0 -119
  140. data/app/helpers/alchemy/admin/contents_helper.rb +0 -42
  141. data/app/helpers/alchemy/admin/essences_helper.rb +0 -31
  142. data/app/models/alchemy/content/factory.rb +0 -143
  143. data/app/models/alchemy/content.rb +0 -247
  144. data/app/models/alchemy/element/element_contents.rb +0 -200
  145. data/app/models/alchemy/element/element_essences.rb +0 -133
  146. data/app/models/alchemy/essence_audio.rb +0 -13
  147. data/app/models/alchemy/essence_boolean.rb +0 -20
  148. data/app/models/alchemy/essence_date.rb +0 -25
  149. data/app/models/alchemy/essence_file.rb +0 -49
  150. data/app/models/alchemy/essence_headline.rb +0 -41
  151. data/app/models/alchemy/essence_html.rb +0 -23
  152. data/app/models/alchemy/essence_link.rb +0 -21
  153. data/app/models/alchemy/essence_node.rb +0 -19
  154. data/app/models/alchemy/essence_page.rb +0 -17
  155. data/app/models/alchemy/essence_picture.rb +0 -67
  156. data/app/models/alchemy/essence_picture_view.rb +0 -90
  157. data/app/models/alchemy/essence_richtext.rb +0 -44
  158. data/app/models/alchemy/essence_select.rb +0 -19
  159. data/app/models/alchemy/essence_text.rb +0 -23
  160. data/app/models/alchemy/essence_video.rb +0 -13
  161. data/app/serializers/alchemy/content_serializer.rb +0 -17
  162. data/app/serializers/alchemy/essence_boolean_serializer.rb +0 -10
  163. data/app/serializers/alchemy/essence_date_serializer.rb +0 -10
  164. data/app/serializers/alchemy/essence_file_serializer.rb +0 -13
  165. data/app/serializers/alchemy/essence_html_serializer.rb +0 -10
  166. data/app/serializers/alchemy/essence_link_serializer.rb +0 -13
  167. data/app/serializers/alchemy/essence_picture_serializer.rb +0 -28
  168. data/app/serializers/alchemy/essence_richtext_serializer.rb +0 -11
  169. data/app/serializers/alchemy/essence_select_serializer.rb +0 -10
  170. data/app/serializers/alchemy/essence_text_serializer.rb +0 -22
  171. data/app/views/alchemy/admin/contents/create.js.erb +0 -21
  172. data/app/views/alchemy/admin/essence_audios/edit.html.erb +0 -7
  173. data/app/views/alchemy/admin/essence_files/edit.html.erb +0 -21
  174. data/app/views/alchemy/admin/essence_pictures/destroy.js.erb +0 -5
  175. data/app/views/alchemy/admin/essence_pictures/edit.html.erb +0 -30
  176. data/app/views/alchemy/admin/essence_pictures/save_link.js.erb +0 -3
  177. data/app/views/alchemy/admin/essence_pictures/update.js.erb +0 -8
  178. data/app/views/alchemy/admin/essence_videos/edit.html.erb +0 -12
  179. data/app/views/alchemy/essences/_essence_audio_editor.html.erb +0 -4
  180. data/app/views/alchemy/essences/_essence_audio_view.html.erb +0 -15
  181. data/app/views/alchemy/essences/_essence_boolean_editor.html.erb +0 -11
  182. data/app/views/alchemy/essences/_essence_boolean_view.html.erb +0 -2
  183. data/app/views/alchemy/essences/_essence_date_editor.html.erb +0 -16
  184. data/app/views/alchemy/essences/_essence_date_view.html.erb +0 -10
  185. data/app/views/alchemy/essences/_essence_file_editor.html.erb +0 -54
  186. data/app/views/alchemy/essences/_essence_file_view.html.erb +0 -18
  187. data/app/views/alchemy/essences/_essence_headline_editor.html.erb +0 -36
  188. data/app/views/alchemy/essences/_essence_headline_view.html.erb +0 -10
  189. data/app/views/alchemy/essences/_essence_html_editor.html.erb +0 -10
  190. data/app/views/alchemy/essences/_essence_html_view.html.erb +0 -2
  191. data/app/views/alchemy/essences/_essence_link_editor.html.erb +0 -30
  192. data/app/views/alchemy/essences/_essence_link_view.html.erb +0 -10
  193. data/app/views/alchemy/essences/_essence_node_editor.html.erb +0 -27
  194. data/app/views/alchemy/essences/_essence_node_view.html.erb +0 -1
  195. data/app/views/alchemy/essences/_essence_page_editor.html.erb +0 -26
  196. data/app/views/alchemy/essences/_essence_page_view.html.erb +0 -5
  197. data/app/views/alchemy/essences/_essence_picture_editor.html.erb +0 -59
  198. data/app/views/alchemy/essences/_essence_picture_view.html.erb +0 -6
  199. data/app/views/alchemy/essences/_essence_richtext_editor.html.erb +0 -14
  200. data/app/views/alchemy/essences/_essence_richtext_view.html.erb +0 -4
  201. data/app/views/alchemy/essences/_essence_select_editor.html.erb +0 -28
  202. data/app/views/alchemy/essences/_essence_select_view.html.erb +0 -2
  203. data/app/views/alchemy/essences/_essence_text_editor.html.erb +0 -29
  204. data/app/views/alchemy/essences/_essence_text_view.html.erb +0 -17
  205. data/app/views/alchemy/essences/_essence_video_editor.html.erb +0 -4
  206. data/app/views/alchemy/essences/_essence_video_view.html.erb +0 -19
  207. data/app/views/alchemy/essences/shared/_essence_picture_tools.html.erb +0 -59
  208. data/app/views/alchemy/essences/shared/_linkable_essence_tools.html.erb +0 -20
  209. data/app/views/alchemy/pages/show.rss.builder +0 -21
  210. data/db/migrate/20200226213334_alchemy_four_point_four.rb +0 -313
  211. data/db/migrate/20200423073425_create_alchemy_essence_nodes.rb +0 -11
  212. data/db/migrate/20200504210159_remove_site_id_from_nodes.rb +0 -28
  213. data/db/migrate/20200505215518_add_language_id_foreign_key_to_alchemy_pages.rb +0 -8
  214. data/db/migrate/20200511113603_add_menu_type_to_alchemy_nodes.rb +0 -27
  215. data/db/migrate/20200514091507_make_page_layoutpage_null_false.rb +0 -6
  216. data/db/migrate/20200519073500_remove_visible_from_alchemy_pages.rb +0 -24
  217. data/db/migrate/20200617110713_create_alchemy_picture_thumbs.rb +0 -22
  218. data/db/migrate/20200907111332_remove_tri_state_booleans.rb +0 -33
  219. data/db/migrate/20201207131309_create_page_versions.rb +0 -19
  220. data/db/migrate/20201207135820_add_page_version_id_to_alchemy_elements.rb +0 -76
  221. data/db/migrate/20210205143548_rename_public_on_and_public_until_on_alchemy_pages.rb +0 -10
  222. data/db/migrate/20210326105046_add_sanitized_body_to_alchemy_essence_richtexts.rb +0 -7
  223. data/db/migrate/20210406093436_add_alchemy_essence_headlines.rb +0 -12
  224. data/db/migrate/20210506135919_create_essence_audios.rb +0 -19
  225. data/db/migrate/20210506140258_create_essence_videos.rb +0 -23
  226. data/db/migrate/20210508091432_create_alchemy_ingredients.rb +0 -22
  227. data/db/migrate/20220514072456_restrict_on_delete_page_id_foreign_key_from_alchemy_nodes.rb +0 -13
  228. data/db/migrate/20220622130905_add_playsinline_to_alchemy_essence_videos.rb +0 -9
  229. data/lib/alchemy/essence.rb +0 -250
  230. data/lib/alchemy/tasks/usage.rb +0 -34
  231. data/lib/alchemy/test_support/essence_shared_examples.rb +0 -271
  232. data/lib/alchemy/test_support/factories/content_factory.rb +0 -20
  233. data/lib/alchemy/test_support/factories/essence_audio_factory.rb +0 -7
  234. data/lib/alchemy/test_support/factories/essence_file_factory.rb +0 -7
  235. data/lib/alchemy/test_support/factories/essence_page_factory.rb +0 -7
  236. data/lib/alchemy/test_support/factories/essence_picture_factory.rb +0 -11
  237. data/lib/alchemy/test_support/factories/essence_text_factory.rb +0 -7
  238. data/lib/alchemy/test_support/factories/essence_video_factory.rb +0 -7
  239. data/lib/alchemy/upgrader/five_point_zero.rb +0 -41
  240. data/lib/alchemy/upgrader/six_point_zero.rb +0 -21
  241. data/lib/alchemy/upgrader/tasks/add_page_versions.rb +0 -33
  242. data/lib/alchemy/upgrader/tasks/element_views_updater.rb +0 -34
  243. data/lib/alchemy/upgrader/tasks/harden_gutentag_migrations.rb +0 -29
  244. data/lib/alchemy/upgrader/tasks/ingredients_migrator.rb +0 -73
  245. data/lib/generators/alchemy/essence/essence_generator.rb +0 -49
  246. data/lib/generators/alchemy/essence/templates/editor.html.erb +0 -17
  247. data/lib/generators/alchemy/essence/templates/view.html.erb +0 -2
  248. data/lib/generators/alchemy/install/files/babel.config.js +0 -64
  249. data/lib/tasks/alchemy/usage.rake +0 -44
@@ -17,12 +17,6 @@ module Alchemy
17
17
  public_until ? public_until - Time.current : nil
18
18
  end
19
19
 
20
- def taggable?
21
- definition["taggable"] == true
22
- end
23
-
24
- deprecate :taggable?, deprecator: Alchemy::Deprecation
25
-
26
20
  def rootpage?
27
21
  !new_record? && parent_id.blank?
28
22
  end
@@ -33,10 +27,6 @@ module Alchemy
33
27
  folded_pages.where(user_id: user_id, folded: true).any?
34
28
  end
35
29
 
36
- def contains_feed?
37
- definition["feed"]
38
- end
39
-
40
30
  # Returns an Array of Alchemy roles which are able to edit this template
41
31
  #
42
32
  # # config/alchemy/page_layouts.yml
@@ -121,8 +121,6 @@ module Alchemy
121
121
  has_one :draft_version, -> { drafts }, class_name: "Alchemy::PageVersion"
122
122
  has_one :public_version, -> { published }, class_name: "Alchemy::PageVersion", autosave: -> { persisted? }
123
123
 
124
- has_many :page_essences, class_name: "Alchemy::EssencePage", foreign_key: :page_id, inverse_of: :ingredient_association, dependent: :nullify
125
-
126
124
  before_validation :set_language,
127
125
  if: -> { language.nil? }
128
126
 
@@ -165,14 +163,6 @@ module Alchemy
165
163
  # site_name accessor
166
164
  delegate :name, to: :site, prefix: true, allow_nil: true
167
165
 
168
- # Old public_on and public_until attributes for historical reasons
169
- #
170
- # These attributes now exist on the page versions
171
- #
172
- attr_readonly :legacy_public_on, :legacy_public_until
173
- deprecate :legacy_public_on, deprecator: Alchemy::Deprecation
174
- deprecate :legacy_public_until, deprecator: Alchemy::Deprecation
175
-
176
166
  # Class methods
177
167
  #
178
168
  class << self
@@ -33,36 +33,6 @@ module Alchemy
33
33
  image_file.thumbnail(upsample ? size : "#{size}>")
34
34
  end
35
35
 
36
- # Returns true if picture's width is greater than it's height
37
- #
38
- def landscape_format?
39
- image_file.landscape?
40
- end
41
-
42
- alias_method :landscape?, :landscape_format?
43
- deprecate landscape_format?: "Use image_file.landscape? instead", deprecator: Alchemy::Deprecation
44
- deprecate landscape?: "Use image_file.landscape? instead", deprecator: Alchemy::Deprecation
45
-
46
- # Returns true if picture's width is smaller than it's height
47
- #
48
- def portrait_format?
49
- image_file.portrait?
50
- end
51
-
52
- alias_method :portrait?, :portrait_format?
53
- deprecate portrait_format?: "Use image_file.portrait? instead", deprecator: Alchemy::Deprecation
54
- deprecate portrait?: "Use image_file.portrait? instead", deprecator: Alchemy::Deprecation
55
-
56
- # Returns true if picture's width and height is equal
57
- #
58
- def square_format?
59
- image_file.aspect_ratio == 1.0
60
- end
61
-
62
- alias_method :square?, :square_format?
63
- deprecate square_format?: "Use image_file.aspect_ratio instead", deprecator: Alchemy::Deprecation
64
- deprecate square?: "Use image_file.aspect_ratio instead", deprecator: Alchemy::Deprecation
65
-
66
36
  # Returns true if the class we're included in has a meaningful render_size attribute
67
37
  #
68
38
  def render_size?
@@ -36,7 +36,7 @@ module Alchemy
36
36
  else
37
37
  uid = PictureThumb::Uid.call(signature, variant)
38
38
  ActiveRecord::Base.connected_to(role: ActiveRecord::Base.writing_role) do
39
- PictureThumb.generator_class.call(variant, signature, uid)
39
+ PictureThumb::Create.call(variant, signature, uid)
40
40
  end
41
41
  uid
42
42
  end
@@ -47,17 +47,16 @@ module Alchemy
47
47
  include Alchemy::TouchElements
48
48
  include Calculations
49
49
 
50
- has_many :essence_pictures,
51
- class_name: "Alchemy::EssencePicture",
52
- foreign_key: "picture_id",
53
- inverse_of: :ingredient_association
50
+ has_many :picture_ingredients,
51
+ class_name: "Alchemy::Ingredients::Picture",
52
+ foreign_key: "related_object_id",
53
+ inverse_of: :related_object
54
54
 
55
- has_many :contents, through: :essence_pictures
56
- has_many :elements, through: :contents
55
+ has_many :elements, through: :picture_ingredients
57
56
  has_many :pages, through: :elements
58
57
  has_many :thumbs, class_name: "Alchemy::PictureThumb", dependent: :destroy
59
58
 
60
- # Raise error, if picture is in use (aka. assigned to an EssencePicture)
59
+ # Raise error, if picture is in use (aka. assigned to an Picture ingredient)
61
60
  #
62
61
  # === CAUTION
63
62
  #
@@ -114,7 +113,10 @@ module Alchemy
114
113
 
115
114
  scope :named, ->(name) { where("#{table_name}.name LIKE ?", "%#{name}%") }
116
115
  scope :recent, -> { where("#{table_name}.created_at > ?", Time.current - 24.hours).order(:created_at) }
117
- scope :deletable, -> { where("#{table_name}.id NOT IN (SELECT picture_id FROM #{EssencePicture.table_name})") }
116
+ scope :deletable,
117
+ -> {
118
+ where("#{table_name}.id NOT IN (SELECT related_object_id FROM alchemy_ingredients WHERE related_object_type = 'Alchemy::Picture')")
119
+ }
118
120
  scope :without_tag, -> { left_outer_joins(:taggings).where(gutentag_taggings: { id: nil }) }
119
121
  scope :by_file_format, ->(format) { where(image_file_format: format) }
120
122
 
@@ -136,15 +138,14 @@ module Alchemy
136
138
  end
137
139
 
138
140
  def alchemy_resource_filters
139
- @_file_formats ||= distinct.pluck(:image_file_format).compact.presence || []
140
141
  [
141
142
  {
142
143
  name: :by_file_format,
143
- values: @_file_formats,
144
+ values: distinct.pluck(:image_file_format),
144
145
  },
145
146
  {
146
147
  name: :misc,
147
- values: %w(recent last_upload without_tag deletable),
148
+ values: %w(recent last_upload without_tag),
148
149
  },
149
150
  ]
150
151
  end
@@ -280,10 +281,10 @@ module Alchemy
280
281
  pages.any? && pages.not_restricted.blank?
281
282
  end
282
283
 
283
- # Returns true if picture is not assigned to any EssencePicture.
284
+ # Returns true if picture is not assigned to any Picture ingredient.
284
285
  #
285
286
  def deletable?
286
- essence_pictures.empty?
287
+ picture_ingredients.empty?
287
288
  end
288
289
 
289
290
  # A size String from original image file values.
@@ -2,9 +2,11 @@
2
2
 
3
3
  module Alchemy
4
4
  class PictureThumb < BaseRecord
5
- # Stores the render result of a Alchemy::PictureVariant
6
- # in the configured Dragonfly datastore
7
- # (Default: Dragonfly::FileDataStore)
5
+ # Creates a Alchemy::PictureThumb
6
+ #
7
+ # Stores the processes result of a Alchemy::PictureVariant
8
+ # in the configured +Alchemy::PictureThumb.storage_class+
9
+ # (Default: {Alchemy::PictureThumb::FileStore})
8
10
  #
9
11
  class Create
10
12
  class << self
@@ -24,26 +26,13 @@ module Alchemy
24
26
  thumb.uid = uid
25
27
  end
26
28
  begin
27
- # process the image
28
- image = variant.image
29
- # store the processed image
30
- image.to_file(server_path(uid)).close
31
- rescue RuntimeError => e
29
+ Alchemy::PictureThumb.storage_class.call(variant, uid)
30
+ rescue StandardError => e
32
31
  ErrorTracking.notification_handler.call(e)
33
32
  # destroy the thumb if processing or storing fails
34
33
  @thumb&.destroy
35
34
  end
36
35
  end
37
-
38
- private
39
-
40
- # Alchemys dragonfly datastore config seperates the storage path from the public server
41
- # path for security reasons. The Dragonfly FileDataStorage does not support that,
42
- # so we need to build the path on our own.
43
- def server_path(uid)
44
- dragonfly_app = ::Dragonfly.app(:alchemy_pictures)
45
- "#{dragonfly_app.datastore.server_root}/#{uid}"
46
- end
47
36
  end
48
37
  end
49
38
  end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Alchemy
4
+ class PictureThumb < BaseRecord
5
+ # Stores the render result of a Alchemy::PictureVariant
6
+ # in the configured Dragonfly datastore
7
+ # (Default: Dragonfly::FileDataStore)
8
+ #
9
+ class FileStore
10
+ class << self
11
+ # @param [Alchemy::PictureVariant] variant the to be rendered image
12
+ # @param [String] uid The Unique Image Identifier the image is stored at
13
+ #
14
+ def call(variant, uid)
15
+ # process the image
16
+ image = variant.image
17
+ # store the processed image
18
+ image.to_file(server_path(uid)).close
19
+ end
20
+
21
+ private
22
+
23
+ # Alchemys dragonfly datastore config seperates the storage path from the public server
24
+ # path for security reasons. The Dragonfly FileDataStorage does not support that,
25
+ # so we need to build the path on our own.
26
+ def server_path(uid)
27
+ dragonfly_app = ::Dragonfly.app(:alchemy_pictures)
28
+ "#{dragonfly_app.datastore.server_root}/#{uid}"
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -7,7 +7,7 @@ module Alchemy
7
7
  # different thumbnail store (ie. a remote file storage).
8
8
  #
9
9
  # config/initializers/alchemy.rb
10
- # Alchemy::PictureThumb.generator_class = My::ThumbnailGenerator
10
+ # Alchemy::PictureThumb.storage_class = My::ThumbnailStore
11
11
  #
12
12
  class PictureThumb < BaseRecord
13
13
  belongs_to :picture, class_name: "Alchemy::Picture"
@@ -16,18 +16,18 @@ module Alchemy
16
16
  validates :uid, presence: true
17
17
 
18
18
  class << self
19
- # Thumbnail generator class
19
+ # Thumbnail storage class
20
20
  #
21
- # @see Alchemy::PictureThumb::Create
22
- def generator_class
23
- @_generator_class ||= Alchemy::PictureThumb::Create
21
+ # @see Alchemy::PictureThumb::FileStore
22
+ def storage_class
23
+ @_storage_class ||= Alchemy::PictureThumb::FileStore
24
24
  end
25
25
 
26
- # Set a thumbnail generator class
26
+ # Set a thumbnail storage class
27
27
  #
28
- # @see Alchemy::PictureThumb::Create
29
- def generator_class=(klass)
30
- @_generator_class = klass
28
+ # @see Alchemy::PictureThumb::FileStore
29
+ def storage_class=(klass)
30
+ @_storage_class = klass
31
31
  end
32
32
 
33
33
  # Upfront generation of picture thumbnails
@@ -49,7 +49,7 @@ module Alchemy
49
49
  next if thumb
50
50
 
51
51
  uid = Alchemy::PictureThumb::Uid.call(signature, variant)
52
- generator_class.call(variant, signature, uid)
52
+ Alchemy::PictureThumb::Create.call(variant, signature, uid)
53
53
  end
54
54
  end
55
55
  end
@@ -20,7 +20,7 @@ module Alchemy
20
20
  #
21
21
  # === Example:
22
22
  #
23
- # essence_picture.picture_url(size: '200x300', crop: true, format: 'gif')
23
+ # picture_view.picture_url(size: '200x300', crop: true, format: 'gif')
24
24
  # # '/pictures/1/show/200x300/crop/cats.gif?sh=765rfghj'
25
25
  #
26
26
  # @option options size [String]
@@ -100,7 +100,7 @@ module Alchemy
100
100
  ).to_h
101
101
  end
102
102
 
103
- # Show image cropping link for content
103
+ # Show image cropping link for ingredient
104
104
  def allow_image_cropping?
105
105
  settings[:crop] && picture &&
106
106
  picture.can_be_cropped_to?(
@@ -115,8 +115,6 @@ module Alchemy
115
115
  return nil unless settings[:crop] && settings[:size]
116
116
 
117
117
  mask = inferred_dimensions_from_string(settings[:size])
118
- return if mask.nil?
119
-
120
118
  zoom = thumbnail_zoom_factor(mask)
121
119
  return nil if zoom.zero?
122
120
 
@@ -152,8 +150,6 @@ module Alchemy
152
150
  width, height = dimensions_from_string(string)
153
151
  ratio = image_file_width.to_f / image_file_height.to_i
154
152
 
155
- return if ratio.nan?
156
-
157
153
  if width.zero? && ratio.is_a?(Float)
158
154
  width = height * ratio
159
155
  end
@@ -10,16 +10,11 @@ module Alchemy
10
10
  :tag_list,
11
11
  :created_at,
12
12
  :updated_at,
13
- :ingredients,
14
- :content_ids,
15
13
  :dom_id,
16
14
  :display_name
17
15
 
18
16
  has_many :nested_elements
19
-
20
- def ingredients
21
- object.contents.collect(&:serialize)
22
- end
17
+ has_many :ingredients
23
18
 
24
19
  def display_name
25
20
  object.display_name_with_preview_text
@@ -3,6 +3,7 @@
3
3
  module Alchemy
4
4
  class DeleteElements
5
5
  class WouldLeaveOrphansError < StandardError; end
6
+
6
7
  attr_reader :elements
7
8
 
8
9
  def initialize(elements)
@@ -14,13 +15,6 @@ module Alchemy
14
15
  raise WouldLeaveOrphansError
15
16
  end
16
17
 
17
- contents = Alchemy::Content.where(element_id: elements.map(&:id))
18
- contents.group_by(&:essence_type)
19
- .transform_values! { |value| value.map(&:essence_id) }
20
- .each do |class_name, ids|
21
- class_name.constantize.where(id: ids).delete_all
22
- end
23
- contents.delete_all
24
18
  Gutentag::Tagging.where(taggable: elements).delete_all
25
19
  delete_elements
26
20
  end
@@ -25,7 +25,6 @@ module Alchemy
25
25
  .except(*SKIPPED_ATTRIBUTES_ON_COPY)
26
26
  .merge(differences)
27
27
  .merge(
28
- autogenerate_contents: false,
29
28
  autogenerate_ingredients: false,
30
29
  autogenerate_nested_elements: false,
31
30
  tags: source_element.tags,
@@ -35,17 +34,13 @@ module Alchemy
35
34
  new_element.ingredients = source_element.ingredients.map(&:dup)
36
35
  new_element.save!
37
36
 
38
- source_element.contents.map do |content|
39
- Content.copy(content, element: new_element)
40
- end
41
-
42
37
  nested_elements = repository.children_of(source_element)
43
38
  Element.acts_as_list_no_update do
44
39
  nested_elements.each.with_index(1) do |nested_element, position|
45
40
  self.class.new(nested_element, repository: repository).call(
46
41
  parent_element: new_element,
47
42
  page_version: new_element.page_version,
48
- position: position
43
+ position: position,
49
44
  )
50
45
  end
51
46
  end
@@ -20,7 +20,7 @@
20
20
 
21
21
  <% if element.editable? %>
22
22
  <%= form_for [alchemy, :admin, element], remote: true,
23
- html: {id: "element_#{element.id}_form".html_safe, class: 'element-content'} do |f| %>
23
+ html: {id: "element_#{element.id}_form".html_safe, class: 'element-body'} do |f| %>
24
24
 
25
25
  <div id="element_<%= element.id %>_errors" class="element_errors"></div>
26
26
 
@@ -31,35 +31,18 @@
31
31
 
32
32
  <!-- Each ingredient group -->
33
33
  <% element.ingredients.select { |i| i.definition[:group] }.group_by { |i| i.definition[:group] }.each do |group, ingredients| %>
34
- <div class="content-group">
35
- <%= link_to '#', id: "element_#{element.id}_content_group_#{group.parameterize.underscore}_header", class: 'content-group-header', data: { toggle_content_group: true } do %>
34
+ <div class="ingredient-group">
35
+ <%= link_to '#', id: "element_#{element.id}_ingredient_group_#{group.parameterize.underscore}_header", class: 'ingredient-group-header', data: { toggle_ingredient_group: true } do %>
36
36
  <%= element.translated_group group %>
37
- <i class="content-group-expand icon fa-fw fa-angle-down fas"></i>
37
+ <i class="ingredient-group-expand icon fa-fw fa-angle-down fas"></i>
38
38
  <% end %>
39
- <%= content_tag :div, id: "element_#{element.id}_content_group_#{group.parameterize.underscore}", class: 'content-group-contents' do %>
39
+ <%= content_tag :div, id: "element_#{element.id}_ingredient_group_#{group.parameterize.underscore}", class: 'ingredient-group-ingredients' do %>
40
40
  <%= render ingredients, element_form: f %>
41
41
  <% end %>
42
42
  </div>
43
43
  <% end %>
44
44
  </div>
45
45
  <% end %>
46
- <!-- Contents -->
47
- <div id="element_<%= element.id %>_content" class="element-content-editors">
48
- <%= render element.contents.select { |c| !c.definition[:group] } %>
49
-
50
- <!-- Each content group -->
51
- <% element.contents.select { |c| c.definition[:group] }.group_by { |c| c.definition[:group] }.each do |group, contents| %>
52
- <div class="content-group">
53
- <%= link_to '#', id: "element_#{element.id}_content_group_#{group.parameterize.underscore}_header", class: 'content-group-header', data: { toggle_content_group: true } do %>
54
- <%= element.translated_group group %>
55
- <i class="content-group-expand icon fa-fw fa-angle-down fas"></i>
56
- <% end %>
57
- <%= content_tag :div, id: "element_#{element.id}_content_group_#{group.parameterize.underscore}", class: 'content-group-contents' do %>
58
- <%= render contents, element_form: f %>
59
- <% end %>
60
- </div>
61
- <% end %>
62
- </div>
63
46
 
64
47
  <% if element.taggable? %>
65
48
  <div class="autocomplete_tag_list">
@@ -34,7 +34,7 @@
34
34
 
35
35
  Alchemy.growl('<%= Alchemy.t(:successfully_added_element) %>');
36
36
  Alchemy.closeCurrentDialog();
37
- Alchemy.Tinymce.init(<%= (@element.richtext_contents_ids + @element.richtext_ingredients_ids).to_json %>);
37
+ Alchemy.Tinymce.init(<%= @element.richtext_ingredients_ids.to_json %>);
38
38
  Alchemy.PreviewWindow.refresh(function() {
39
39
  Alchemy.ElementEditors.focusElementPreview(<%= @element.id %>);
40
40
  });
@@ -14,12 +14,12 @@
14
14
 
15
15
  <% if @element.folded? -%>
16
16
 
17
- Alchemy.Tinymce.remove(<%= (@element.richtext_contents_ids + @element.richtext_ingredients_ids).to_json %>);
17
+ Alchemy.Tinymce.remove(<%= @element.richtext_ingredients_ids.to_json %>);
18
18
 
19
19
  <% else -%>
20
20
 
21
21
  $el.trigger('FocusElementEditor.Alchemy');
22
- Alchemy.Tinymce.init(<%= (@element.richtext_contents_ids + @element.richtext_ingredients_ids).to_json %>);
22
+ Alchemy.Tinymce.init(<%= @element.richtext_ingredients_ids.to_json %>);
23
23
  Alchemy.GUI.initElement($el);
24
24
  Alchemy.SortableElements(
25
25
  <%= @page.id %>,
@@ -1,5 +1,5 @@
1
1
  (function() {
2
- <% if @parent_element && @parent_element.contents.empty? %>
2
+ <% if @parent_element && @parent_element.ingredients.empty? %>
3
3
  var $parent = $('#element_<%= @parent_element.id %>');
4
4
  Alchemy.ElementEditors.setTitle(
5
5
  $parent,
@@ -1,7 +1,7 @@
1
1
  (function() {
2
2
  var $el = $('#element_<%= @element.id %>');
3
3
  var $errors = $('#element_<%= @element.id %>_errors');
4
- $('> .element-content .content_editor, > .element-content .ingredient-editor', $el).removeClass('validation_failed');
4
+ $('> .element-body .ingredient-editor', $el).removeClass('validation_failed');
5
5
 
6
6
  <%- if @element_validated -%>
7
7
 
@@ -20,7 +20,6 @@
20
20
  Alchemy.growl('<%= j @notice %>', 'warn');
21
21
  $errors.html('<%= j @error_message %><ul><li><%== j @error_messages.join("</li><li>") %></li></ul>');
22
22
  $errors.show();
23
- $('<%= @element.contents_with_errors.map { |content| "#" + content.dom_id }.join(", ") %>').addClass('validation_failed');
24
23
  $('<%== @element.ingredients_with_errors.map { |ingredient| "[data-ingredient-id=\"#{ingredient.id}\"]" }.join(", ") %>').addClass('validation_failed');
25
24
  Alchemy.Buttons.enable($el);
26
25
 
@@ -8,8 +8,8 @@
8
8
  <ul></ul>
9
9
  </div>
10
10
  <div class="input text">
11
- <label for="external_url" class="control-label">URL</label>
12
- <%= text_field_tag "external_url" %>
11
+ <label for="external_link" class="control-label">URL</label>
12
+ <%= text_field_tag "external_link" %>
13
13
  </div>
14
14
  <div class="input text">
15
15
  <label for="external_link_title" class="control-label">
@@ -3,10 +3,10 @@
3
3
  <h3><%= Alchemy.t(:choose_file_to_link) %></h3>
4
4
  <% end %>
5
5
  <div class="input select">
6
- <label for="public_filename" class="control-label">
6
+ <label for="file_link" class="control-label">
7
7
  <%= Alchemy.t(:file) %>
8
8
  </label>
9
- <%= select_tag "public_filename",
9
+ <%= select_tag "file_link",
10
10
  options_for_select(@attachments),
11
11
  prompt: Alchemy.t('Please choose'),
12
12
  class: "alchemy_selectbox" %>
@@ -4,10 +4,10 @@
4
4
  <p><%= Alchemy.t(:internal_link_page_elements_explanation) %></p>
5
5
  <% end %>
6
6
  <div class="input select">
7
- <label for="page_urlname" class="control-label">
7
+ <label for="internal_link" class="control-label">
8
8
  <%= Alchemy.t(:page) %>
9
9
  </label>
10
- <input type="text" id="page_urlname" class="alchemy_selectbox full_width">
10
+ <input type="text" id="internal_link" class="alchemy_selectbox full_width">
11
11
  </div>
12
12
  <div class="input select">
13
13
  <label for="element_anchor" class="control-label">
@@ -25,9 +25,3 @@
25
25
  <%= render partial: "table_row", collection: @pages, as: "page" %>
26
26
  </tbody>
27
27
  </table>
28
-
29
- <script type="text/javascript">
30
- $(function() {
31
- Alchemy.PagePublicationFields();
32
- });
33
- </script>
@@ -1,11 +1,8 @@
1
1
  <script>
2
2
  // Populate custom tinymce configurations
3
- <% (
4
- Alchemy::Tinymce.custom_config_contents(@page) +
5
- Alchemy::Tinymce.custom_config_ingredients(@page)
6
- ).each do |content| %>
7
- Alchemy.Tinymce.customConfigs["<%= content['element'] %>_<%= content['name'] || content['role'] %>"] = {
8
- <% content.fetch('settings', {}).fetch('tinymce', {}).each do |k, v| %>
3
+ <% Alchemy::Tinymce.custom_config_ingredients(@page).each do |ingredient| %>
4
+ Alchemy.Tinymce.customConfigs["<%= ingredient['element'] %>_<%= ingredient['role'] %>"] = {
5
+ <% ingredient.fetch('settings', {}).fetch('tinymce', {}).each do |k, v| %>
9
6
  <%= k %>: <%== v.to_json %>,
10
7
  <% end %>
11
8
  };
@@ -197,7 +197,7 @@
197
197
  Alchemy.SortableElements(<%= @page.id %>, '<%= form_authenticity_token %>');
198
198
  Alchemy.ElementEditors.init();
199
199
  Alchemy.SelectBox('.element-editor');
200
- Alchemy.Tinymce.init(<%= (@page.richtext_contents_ids + @page.richtext_ingredients_ids).to_json %>);
200
+ Alchemy.Tinymce.init(<%= @page.richtext_ingredients_ids.to_json %>);
201
201
  $('#fixed-elements').tabs().tabs('paging', {
202
202
  follow: true,
203
203
  followOnSelect: true,
@@ -8,9 +8,7 @@
8
8
  redirect_url: alchemy.admin_pictures_path(
9
9
  size: search_filter_params[:size],
10
10
  filter: { misc: 'last_upload' },
11
- form_field_id: @form_field_id,
12
- content_id: @content.try(:id),
13
- element_id: @element.try(:id)
11
+ form_field_id: @form_field_id
14
12
  ) %>
15
13
  <div class="toolbar_spacer"></div>
16
14
  <% end %>
@@ -22,7 +22,7 @@
22
22
  <div id="pictures_page_list">
23
23
  <% if @assignments.any? %>
24
24
  <ul>
25
- <% @assignments.group_by(&:page).each do |page, essence_pictures| %>
25
+ <% @assignments.group_by(&:page).each do |page, picture_ingredients| %>
26
26
  <% if page %>
27
27
  <li>
28
28
  <h3>
@@ -30,20 +30,18 @@
30
30
  <p><%= link_to page.name, edit_admin_page_path(page) %></p>
31
31
  </h3>
32
32
  <ul class="list">
33
- <% essence_pictures.group_by(&:element).each do |element, essence_pictures| %>
33
+ <% picture_ingredients.group_by(&:element).each do |element, picture_ingredients| %>
34
34
  <li class="<%= cycle('even', 'odd') %>">
35
35
  <% page_link = link_to element.display_name_with_preview_text,
36
36
  edit_admin_page_path(page, anchor: "element_#{element.id}") %>
37
- <% pictures = essence_pictures.collect do |e|
38
- e.content.name_for_label
39
- end.to_sentence %>
37
+ <% ingredients = picture_ingredients.collect(&:translated_role).to_sentence %>
40
38
  <% if element.public? %>
41
39
  <%= render_icon('window-maximize', style: 'regular') %>
42
40
  <% else %>
43
41
  <%= render_icon('window-close') %>
44
42
  <% end %>
45
43
  <p>
46
- <%== Alchemy.t(:pictures_in_page, page: page_link, pictures: pictures) %>
44
+ <%== Alchemy.t(:pictures_in_page, page: page_link, pictures: ingredients) %>
47
45
  </p>
48
46
  </li>
49
47
  <% end %>
@@ -1,6 +1,6 @@
1
1
  <%= form_tag url_for, method: :get, class: 'per-page-select-form' do |f| %>
2
2
  <% search_filter_params.reject { |k, _| k == 'page' || k == 'per_page' }.each do |key, value| %>
3
- <% if value.respond_to?(:keys) %>
3
+ <% if value.is_a? Hash %>
4
4
  <% value.each do |k, v| %>
5
5
  <%= hidden_field_tag "#{key}[#{k}]", v, id: nil %>
6
6
  <% end %>
@@ -3,7 +3,7 @@
3
3
  data: boolean_editor.data_attributes do %>
4
4
  <%= element_form.fields_for(:ingredients, boolean_editor.ingredient) do |f| %>
5
5
  <%= f.label :value, style: "display: inline-block" do %>
6
- <%= f.check_box :value, id: nil %>
6
+ <%= f.check_box :value, id: boolean_editor.form_field_id %>
7
7
  <%= render_ingredient_role(boolean_editor) %>
8
8
  <% end %>
9
9
  <%= render_hint_for(boolean_editor) %>
@@ -6,7 +6,7 @@
6
6
  data: headline_editor.data_attributes do %>
7
7
  <%= element_form.fields_for(:ingredients, headline_editor.ingredient) do |f| %>
8
8
  <%= ingredient_label(headline_editor) %>
9
- <%= f.text_field :value, id: nil %>
9
+ <%= f.text_field :value, id: headline_editor.form_field_id %>
10
10
 
11
11
  <% if headline_editor.settings[:anchor] %>
12
12
  <%= render "alchemy/ingredients/shared/anchor", ingredient_editor: headline_editor %>