alchemy_cms 6.1.9 → 7.0.0.pre.a

Sign up to get free protection for your applications and to get access to all the features.
Files changed (247) 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 -39
  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 -8
  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 -2
  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/ingredients/_boolean_editor.html.erb +1 -1
  78. data/app/views/alchemy/ingredients/_headline_editor.html.erb +1 -1
  79. data/app/views/alchemy/ingredients/_html_editor.html.erb +1 -1
  80. data/app/views/alchemy/ingredients/_node_editor.html.erb +1 -1
  81. data/app/views/alchemy/ingredients/_picture_editor.html.erb +4 -4
  82. data/app/views/alchemy/ingredients/_select_editor.html.erb +2 -2
  83. data/app/views/alchemy/ingredients/_text_editor.html.erb +1 -1
  84. data/app/views/alchemy/ingredients/shared/_link_tools.html.erb +3 -3
  85. data/app/views/alchemy/pages/_meta_data.html.erb +0 -1
  86. data/app/views/layouts/alchemy/admin.html.erb +5 -1
  87. data/config/alchemy/config.yml +6 -6
  88. data/config/brakeman.ignore +56 -57
  89. data/config/locales/alchemy.en.yml +99 -113
  90. data/config/routes.rb +1 -16
  91. data/db/migrate/20230121212637_alchemy_six_point_one.rb +248 -0
  92. data/lib/alchemy/cache_digests/template_tracker.rb +6 -7
  93. data/lib/alchemy/config.rb +2 -2
  94. data/lib/alchemy/deprecation.rb +1 -1
  95. data/lib/alchemy/errors.rb +0 -11
  96. data/lib/alchemy/hints.rb +10 -10
  97. data/lib/alchemy/permissions.rb +4 -17
  98. data/lib/alchemy/routing_constraints.rb +3 -3
  99. data/lib/alchemy/searchable_resource.rb +38 -0
  100. data/lib/alchemy/seeder.rb +2 -8
  101. data/lib/alchemy/tasks/tidy.rb +0 -38
  102. data/lib/alchemy/test_support/capybara_helpers.rb +69 -0
  103. data/lib/alchemy/test_support/factories/element_factory.rb +0 -6
  104. data/lib/alchemy/test_support/factories/ingredient_factory.rb +1 -1
  105. data/lib/alchemy/test_support/factories/page_factory.rb +4 -2
  106. data/lib/alchemy/test_support/shared_dom_ids_examples.rb +1 -1
  107. data/lib/alchemy/test_support/shared_ingredient_examples.rb +1 -1
  108. data/lib/alchemy/tinymce.rb +1 -18
  109. data/lib/alchemy/upgrader/seven_point_zero.rb +45 -0
  110. data/lib/alchemy/upgrader/tasks/.keep +0 -0
  111. data/lib/alchemy/upgrader.rb +8 -3
  112. data/lib/alchemy/version.rb +1 -1
  113. data/lib/alchemy.rb +0 -19
  114. data/lib/alchemy_cms.rb +1 -2
  115. data/lib/generators/alchemy/elements/elements_generator.rb +0 -1
  116. data/lib/generators/alchemy/elements/templates/view.html.erb +1 -10
  117. data/lib/generators/alchemy/elements/templates/view.html.haml +1 -9
  118. data/lib/generators/alchemy/elements/templates/view.html.slim +1 -9
  119. data/lib/generators/alchemy/install/files/alchemy.en.yml +7 -8
  120. data/lib/generators/alchemy/install/files/application.html.erb +1 -1
  121. data/lib/generators/alchemy/install/install_generator.rb +18 -34
  122. data/lib/generators/alchemy/install/templates/elements.yml.tt +12 -12
  123. data/lib/non_stupid_digest_assets.rb +1 -1
  124. data/lib/tasks/alchemy/thumbnails.rake +2 -21
  125. data/lib/tasks/alchemy/tidy.rake +1 -12
  126. data/lib/tasks/alchemy/upgrade.rake +10 -47
  127. data/package/dist/admin.js +16 -0
  128. data/package/dist/admin.js.map +7 -0
  129. data/package.json +5 -3
  130. metadata +18 -147
  131. data/app/controllers/alchemy/admin/contents_controller.rb +0 -21
  132. data/app/controllers/alchemy/admin/essence_audios_controller.rb +0 -30
  133. data/app/controllers/alchemy/admin/essence_files_controller.rb +0 -31
  134. data/app/controllers/alchemy/admin/essence_pictures_controller.rb +0 -43
  135. data/app/controllers/alchemy/admin/essence_videos_controller.rb +0 -34
  136. data/app/controllers/alchemy/api/contents_controller.rb +0 -52
  137. data/app/decorators/alchemy/content_editor.rb +0 -119
  138. data/app/helpers/alchemy/admin/contents_helper.rb +0 -42
  139. data/app/helpers/alchemy/admin/essences_helper.rb +0 -31
  140. data/app/models/alchemy/content/factory.rb +0 -143
  141. data/app/models/alchemy/content.rb +0 -247
  142. data/app/models/alchemy/element/element_contents.rb +0 -200
  143. data/app/models/alchemy/element/element_essences.rb +0 -133
  144. data/app/models/alchemy/essence_audio.rb +0 -13
  145. data/app/models/alchemy/essence_boolean.rb +0 -20
  146. data/app/models/alchemy/essence_date.rb +0 -25
  147. data/app/models/alchemy/essence_file.rb +0 -49
  148. data/app/models/alchemy/essence_headline.rb +0 -41
  149. data/app/models/alchemy/essence_html.rb +0 -23
  150. data/app/models/alchemy/essence_link.rb +0 -21
  151. data/app/models/alchemy/essence_node.rb +0 -19
  152. data/app/models/alchemy/essence_page.rb +0 -17
  153. data/app/models/alchemy/essence_picture.rb +0 -67
  154. data/app/models/alchemy/essence_picture_view.rb +0 -90
  155. data/app/models/alchemy/essence_richtext.rb +0 -44
  156. data/app/models/alchemy/essence_select.rb +0 -19
  157. data/app/models/alchemy/essence_text.rb +0 -23
  158. data/app/models/alchemy/essence_video.rb +0 -13
  159. data/app/serializers/alchemy/content_serializer.rb +0 -17
  160. data/app/serializers/alchemy/essence_boolean_serializer.rb +0 -10
  161. data/app/serializers/alchemy/essence_date_serializer.rb +0 -10
  162. data/app/serializers/alchemy/essence_file_serializer.rb +0 -13
  163. data/app/serializers/alchemy/essence_html_serializer.rb +0 -10
  164. data/app/serializers/alchemy/essence_link_serializer.rb +0 -13
  165. data/app/serializers/alchemy/essence_picture_serializer.rb +0 -28
  166. data/app/serializers/alchemy/essence_richtext_serializer.rb +0 -11
  167. data/app/serializers/alchemy/essence_select_serializer.rb +0 -10
  168. data/app/serializers/alchemy/essence_text_serializer.rb +0 -22
  169. data/app/views/alchemy/admin/contents/create.js.erb +0 -21
  170. data/app/views/alchemy/admin/essence_audios/edit.html.erb +0 -7
  171. data/app/views/alchemy/admin/essence_files/edit.html.erb +0 -21
  172. data/app/views/alchemy/admin/essence_pictures/destroy.js.erb +0 -5
  173. data/app/views/alchemy/admin/essence_pictures/edit.html.erb +0 -30
  174. data/app/views/alchemy/admin/essence_pictures/save_link.js.erb +0 -3
  175. data/app/views/alchemy/admin/essence_pictures/update.js.erb +0 -8
  176. data/app/views/alchemy/admin/essence_videos/edit.html.erb +0 -12
  177. data/app/views/alchemy/essences/_essence_audio_editor.html.erb +0 -4
  178. data/app/views/alchemy/essences/_essence_audio_view.html.erb +0 -15
  179. data/app/views/alchemy/essences/_essence_boolean_editor.html.erb +0 -11
  180. data/app/views/alchemy/essences/_essence_boolean_view.html.erb +0 -2
  181. data/app/views/alchemy/essences/_essence_date_editor.html.erb +0 -16
  182. data/app/views/alchemy/essences/_essence_date_view.html.erb +0 -10
  183. data/app/views/alchemy/essences/_essence_file_editor.html.erb +0 -54
  184. data/app/views/alchemy/essences/_essence_file_view.html.erb +0 -18
  185. data/app/views/alchemy/essences/_essence_headline_editor.html.erb +0 -36
  186. data/app/views/alchemy/essences/_essence_headline_view.html.erb +0 -10
  187. data/app/views/alchemy/essences/_essence_html_editor.html.erb +0 -10
  188. data/app/views/alchemy/essences/_essence_html_view.html.erb +0 -2
  189. data/app/views/alchemy/essences/_essence_link_editor.html.erb +0 -30
  190. data/app/views/alchemy/essences/_essence_link_view.html.erb +0 -10
  191. data/app/views/alchemy/essences/_essence_node_editor.html.erb +0 -27
  192. data/app/views/alchemy/essences/_essence_node_view.html.erb +0 -1
  193. data/app/views/alchemy/essences/_essence_page_editor.html.erb +0 -26
  194. data/app/views/alchemy/essences/_essence_page_view.html.erb +0 -5
  195. data/app/views/alchemy/essences/_essence_picture_editor.html.erb +0 -59
  196. data/app/views/alchemy/essences/_essence_picture_view.html.erb +0 -6
  197. data/app/views/alchemy/essences/_essence_richtext_editor.html.erb +0 -14
  198. data/app/views/alchemy/essences/_essence_richtext_view.html.erb +0 -4
  199. data/app/views/alchemy/essences/_essence_select_editor.html.erb +0 -28
  200. data/app/views/alchemy/essences/_essence_select_view.html.erb +0 -2
  201. data/app/views/alchemy/essences/_essence_text_editor.html.erb +0 -29
  202. data/app/views/alchemy/essences/_essence_text_view.html.erb +0 -17
  203. data/app/views/alchemy/essences/_essence_video_editor.html.erb +0 -4
  204. data/app/views/alchemy/essences/_essence_video_view.html.erb +0 -19
  205. data/app/views/alchemy/essences/shared/_essence_picture_tools.html.erb +0 -59
  206. data/app/views/alchemy/essences/shared/_linkable_essence_tools.html.erb +0 -20
  207. data/app/views/alchemy/pages/show.rss.builder +0 -21
  208. data/db/migrate/20200226213334_alchemy_four_point_four.rb +0 -313
  209. data/db/migrate/20200423073425_create_alchemy_essence_nodes.rb +0 -11
  210. data/db/migrate/20200504210159_remove_site_id_from_nodes.rb +0 -28
  211. data/db/migrate/20200505215518_add_language_id_foreign_key_to_alchemy_pages.rb +0 -8
  212. data/db/migrate/20200511113603_add_menu_type_to_alchemy_nodes.rb +0 -27
  213. data/db/migrate/20200514091507_make_page_layoutpage_null_false.rb +0 -6
  214. data/db/migrate/20200519073500_remove_visible_from_alchemy_pages.rb +0 -24
  215. data/db/migrate/20200617110713_create_alchemy_picture_thumbs.rb +0 -22
  216. data/db/migrate/20200907111332_remove_tri_state_booleans.rb +0 -33
  217. data/db/migrate/20201207131309_create_page_versions.rb +0 -19
  218. data/db/migrate/20201207135820_add_page_version_id_to_alchemy_elements.rb +0 -76
  219. data/db/migrate/20210205143548_rename_public_on_and_public_until_on_alchemy_pages.rb +0 -10
  220. data/db/migrate/20210326105046_add_sanitized_body_to_alchemy_essence_richtexts.rb +0 -7
  221. data/db/migrate/20210406093436_add_alchemy_essence_headlines.rb +0 -12
  222. data/db/migrate/20210506135919_create_essence_audios.rb +0 -19
  223. data/db/migrate/20210506140258_create_essence_videos.rb +0 -23
  224. data/db/migrate/20210508091432_create_alchemy_ingredients.rb +0 -22
  225. data/db/migrate/20220514072456_restrict_on_delete_page_id_foreign_key_from_alchemy_nodes.rb +0 -13
  226. data/db/migrate/20220622130905_add_playsinline_to_alchemy_essence_videos.rb +0 -9
  227. data/lib/alchemy/essence.rb +0 -250
  228. data/lib/alchemy/tasks/usage.rb +0 -34
  229. data/lib/alchemy/test_support/essence_shared_examples.rb +0 -271
  230. data/lib/alchemy/test_support/factories/content_factory.rb +0 -20
  231. data/lib/alchemy/test_support/factories/essence_audio_factory.rb +0 -7
  232. data/lib/alchemy/test_support/factories/essence_file_factory.rb +0 -7
  233. data/lib/alchemy/test_support/factories/essence_page_factory.rb +0 -7
  234. data/lib/alchemy/test_support/factories/essence_picture_factory.rb +0 -11
  235. data/lib/alchemy/test_support/factories/essence_text_factory.rb +0 -7
  236. data/lib/alchemy/test_support/factories/essence_video_factory.rb +0 -7
  237. data/lib/alchemy/upgrader/five_point_zero.rb +0 -41
  238. data/lib/alchemy/upgrader/six_point_zero.rb +0 -21
  239. data/lib/alchemy/upgrader/tasks/add_page_versions.rb +0 -33
  240. data/lib/alchemy/upgrader/tasks/element_views_updater.rb +0 -34
  241. data/lib/alchemy/upgrader/tasks/harden_gutentag_migrations.rb +0 -29
  242. data/lib/alchemy/upgrader/tasks/ingredients_migrator.rb +0 -73
  243. data/lib/generators/alchemy/essence/essence_generator.rb +0 -49
  244. data/lib/generators/alchemy/essence/templates/editor.html.erb +0 -17
  245. data/lib/generators/alchemy/essence/templates/view.html.erb +0 -2
  246. data/lib/generators/alchemy/install/files/babel.config.js +0 -64
  247. data/lib/tasks/alchemy/usage.rake +0 -44
@@ -1,119 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Alchemy
4
- class ContentEditor < SimpleDelegator
5
- alias_method :content, :__getobj__
6
-
7
- def to_partial_path
8
- "alchemy/essences/#{essence_partial_name}_editor"
9
- end
10
-
11
- def css_classes
12
- [
13
- "content_editor",
14
- essence_partial_name,
15
- deprecated? ? "deprecated" : nil,
16
- ].compact
17
- end
18
-
19
- def data_attributes
20
- {
21
- content_id: id,
22
- content_name: name,
23
- }
24
- end
25
-
26
- # Returns a string to be passed to Rails form field tags to ensure we have same params layout everywhere.
27
- #
28
- # === Example:
29
- #
30
- # <%= text_field_tag content_editor.form_field_name, content_editor.ingredient %>
31
- #
32
- # === Options:
33
- #
34
- # You can pass an Essence column_name. Default is 'ingredient'
35
- #
36
- # ==== Example:
37
- #
38
- # <%= text_field_tag content_editor.form_field_name(:link), content_editor.ingredient %>
39
- #
40
- def form_field_name(essence_column = "ingredient")
41
- "contents[#{id}][#{essence_column}]"
42
- end
43
-
44
- def form_field_id(essence_column = "ingredient")
45
- "contents_#{id}_#{essence_column}"
46
- end
47
-
48
- # Fixes Rails partial renderer calling to_model on the object
49
- # which reveals the delegated content instead of this decorator.
50
- def respond_to?(method_name)
51
- return false if method_name == :to_model
52
-
53
- super
54
- end
55
-
56
- def has_warnings?
57
- definition.blank? || deprecated?
58
- end
59
-
60
- def warnings
61
- return unless has_warnings?
62
-
63
- if definition.blank?
64
- Logger.warn("Content #{name} is missing its definition", caller(1..1))
65
- Alchemy.t(:content_definition_missing)
66
- else
67
- deprecation_notice
68
- end
69
- end
70
-
71
- # Returns a deprecation notice for contents marked deprecated
72
- #
73
- # You can either use localizations or pass a String as notice
74
- # in the content definition.
75
- #
76
- # == Custom deprecation notices
77
- #
78
- # Use general content deprecation notice
79
- #
80
- # - name: element_name
81
- # contents:
82
- # - name: old_content
83
- # type: EssenceText
84
- # deprecated: true
85
- #
86
- # Add a translation to your locale file for a per content notice.
87
- #
88
- # en:
89
- # alchemy:
90
- # content_deprecation_notices:
91
- # element_name:
92
- # old_content: Foo baz widget is deprecated
93
- #
94
- # or use the global translation that apply to all deprecated contents.
95
- #
96
- # en:
97
- # alchemy:
98
- # content_deprecation_notice: Foo baz widget is deprecated
99
- #
100
- # or pass string as deprecation notice.
101
- #
102
- # - name: element_name
103
- # contents:
104
- # - name: old_content
105
- # type: EssenceText
106
- # deprecated: This content will be removed soon.
107
- #
108
- def deprecation_notice
109
- case definition["deprecated"]
110
- when String
111
- definition["deprecated"]
112
- when TrueClass
113
- Alchemy.t(name,
114
- scope: [:content_deprecation_notices, element.name],
115
- default: Alchemy.t(:content_deprecated))
116
- end
117
- end
118
- end
119
- end
@@ -1,42 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Alchemy
4
- module Admin
5
- module ContentsHelper
6
- include Alchemy::Admin::BaseHelper
7
-
8
- # Renders the name of elements content.
9
- #
10
- # Displays a warning icon if content is missing its definition.
11
- #
12
- # Displays a mandatory field indicator, if the content has validations.
13
- #
14
- def render_content_name(content)
15
- if content.blank?
16
- warning("Content is nil")
17
- return
18
- end
19
-
20
- content_name = content.name_for_label
21
-
22
- if content.has_warnings?
23
- icon = hint_with_tooltip(content.warnings)
24
- content_name = "#{icon} #{content_name}".html_safe
25
- end
26
-
27
- if content.has_validations?
28
- "#{content_name}<span class='validation_indicator'>*</span>".html_safe
29
- else
30
- content_name
31
- end
32
- end
33
-
34
- # Renders the label and a remove link for a content.
35
- def content_label(content)
36
- content_tag :label, for: content.form_field_id do
37
- [render_content_name(content), render_hint_for(content)].compact.join("&nbsp;").html_safe
38
- end
39
- end
40
- end
41
- end
42
- end
@@ -1,31 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Alchemy
4
- module Admin
5
- module EssencesHelper
6
- # Renders a thumbnail for given EssencePicture content with correct cropping and size
7
- def essence_picture_thumbnail(content)
8
- picture = content.ingredient
9
- essence = content.essence
10
-
11
- return if picture.nil?
12
-
13
- image_tag(
14
- essence.thumbnail_url,
15
- alt: picture.name,
16
- class: "img_paddingtop",
17
- title: Alchemy.t(:image_name, name: picture.name),
18
- )
19
- end
20
-
21
- # Size value for edit picture dialog
22
- def edit_picture_dialog_size(content)
23
- if content.settings[:caption_as_textarea]
24
- content.settings[:sizes] ? "380x320" : "380x300"
25
- else
26
- content.settings[:sizes] ? "380x290" : "380x255"
27
- end
28
- end
29
- end
30
- end
31
- end
@@ -1,143 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Alchemy
4
- # Holds everything concerning the building and creating of contents and the related essence object.
5
- #
6
- class Content < BaseRecord
7
- module Factory
8
- extend ActiveSupport::Concern
9
-
10
- module ClassMethods
11
- SKIPPED_ATTRIBUTES_ON_COPY = %w(position created_at updated_at creator_id updater_id element_id id)
12
-
13
- # Builds a new content as descriped in the elements.yml file.
14
- #
15
- # @param [Hash]
16
- # The content definition used for finding the content in +elements.yml+ file
17
- #
18
- def new(attributes = {})
19
- element = attributes[:element] || Element.find_by(id: attributes[:element_id])
20
- return super if attributes.empty? || element.nil?
21
-
22
- definition = element.content_definition_for(attributes[:name])
23
- if definition.blank? && attributes[:essence_type].nil?
24
- raise ContentDefinitionError, "No definition found in elements.yml for #{attributes.inspect} and #{element.inspect}"
25
- end
26
-
27
- super(
28
- name: attributes[:name],
29
- essence_type: attributes[:essence_type] || normalize_essence_type(definition[:type]),
30
- element: element,
31
- ).tap(&:build_essence)
32
- end
33
-
34
- # Creates a new content from elements definition in the +elements.yml+ file.
35
- #
36
- # 1. It builds the content
37
- # 2. It creates the essence record (content object gets saved)
38
- #
39
- # @return [Alchemy::Content]
40
- #
41
- def create(attributes = {})
42
- new(attributes).tap do |content|
43
- content.essence.save && content.save
44
- end
45
- end
46
-
47
- # Creates a copy of source and also copies the associated essence.
48
- #
49
- # You can pass a differences hash to update the attributes of the copy.
50
- #
51
- # === Example
52
- #
53
- # @copy = Alchemy::Content.copy(@content, {element_id: 3})
54
- # @copy.element_id # => 3
55
- #
56
- def copy(source, differences = {})
57
- Content.new(
58
- source.attributes.with_indifferent_access.
59
- except(*SKIPPED_ATTRIBUTES_ON_COPY).
60
- merge(differences.with_indifferent_access)
61
- ).tap do |new_content|
62
- new_content.build_essence(
63
- source.essence.attributes.
64
- except(*SKIPPED_ATTRIBUTES_ON_COPY)
65
- )
66
- new_content.save
67
- end
68
- end
69
-
70
- # Returns all content definitions from elements.yml
71
- #
72
- def definitions
73
- definitions = Element.definitions.flat_map { |e| e["contents"] }
74
- definitions.compact!
75
- definitions
76
- end
77
-
78
- # Returns a normalized Essence type
79
- #
80
- # Adds Alchemy module name in front of given essence type
81
- # unless there is a Class with the specified name that is an essence.
82
- #
83
- # @param [String]
84
- # the essence type to normalize
85
- #
86
- def normalize_essence_type(essence_type)
87
- essence_type = essence_type.classify
88
- return essence_type if is_an_essence?(essence_type)
89
-
90
- "Alchemy::#{essence_type}"
91
- end
92
-
93
- private
94
-
95
- def is_an_essence?(essence_type)
96
- klass = Module.const_get(essence_type)
97
- klass.is_a?(Class) && klass.new.acts_as_essence?
98
- rescue NameError
99
- false
100
- end
101
- end
102
-
103
- # Instance Methods
104
-
105
- # Returns the definition hash from +elements.yml+ file.
106
- #
107
- def definition
108
- if element.blank?
109
- log_warning "Content with id #{id} is missing its Element."
110
- return {}
111
- end
112
- element.content_definition_for(name) || {}
113
- end
114
-
115
- # Build essence from definition.
116
- #
117
- # If an optional type is passed, this type of essence gets created.
118
- #
119
- def build_essence(attributes = {})
120
- self.essence = essence_class.new(
121
- { content: self, ingredient: default_value }.merge(attributes)
122
- )
123
- end
124
-
125
- # Creates essence from definition.
126
- #
127
- # If an optional type is passed, this type of essence gets created.
128
- #
129
- def create_essence!(attrs = {})
130
- build_essence(attrs).save!
131
- save!
132
- end
133
-
134
- private
135
-
136
- # Returns a class constant from definition's type field or the essence_type column
137
- #
138
- def essence_class
139
- (essence_type || Content.normalize_essence_type(definition["type"])).constantize
140
- end
141
- end
142
- end
143
- end
@@ -1,247 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # == Schema Information
4
- #
5
- # Table name: alchemy_contents
6
- #
7
- # id :integer not null, primary key
8
- # name :string
9
- # essence_type :string not null
10
- # essence_id :integer not null
11
- # element_id :integer not null
12
- # position :integer
13
- # created_at :datetime not null
14
- # updated_at :datetime not null
15
- # creator_id :integer
16
- # updater_id :integer
17
- #
18
-
19
- require_dependency "alchemy/content/factory"
20
-
21
- module Alchemy
22
- # @deprecated
23
- class Content < BaseRecord
24
- include Alchemy::Logger
25
- include Alchemy::Hints
26
-
27
- # Concerns
28
- include Factory
29
-
30
- belongs_to :essence, polymorphic: true, dependent: :destroy, inverse_of: :content
31
- belongs_to :element, touch: true, inverse_of: :contents
32
- has_one :page, through: :element
33
-
34
- # Essence scopes
35
- scope :essence_booleans, -> { where(essence_type: "Alchemy::EssenceBoolean") }
36
- scope :essence_dates, -> { where(essence_type: "Alchemy::EssenceDate") }
37
- scope :essence_files, -> { where(essence_type: "Alchemy::EssenceFile") }
38
- scope :essence_htmls, -> { where(essence_type: "Alchemy::EssenceHtml") }
39
- scope :essence_links, -> { where(essence_type: "Alchemy::EssenceLink") }
40
- scope :essence_pictures, -> { where(essence_type: "Alchemy::EssencePicture") }
41
- scope :essence_richtexts, -> { where(essence_type: "Alchemy::EssenceRichtext") }
42
- scope :essence_selects, -> { where(essence_type: "Alchemy::EssenceSelect") }
43
- scope :essence_texts, -> { where(essence_type: "Alchemy::EssenceText") }
44
- scope :named, ->(name) { where(name: name) }
45
- scope :available, -> { published }
46
- scope :published, -> { joins(:element).merge(Element.published) }
47
- scope :not_restricted, -> { joins(:element).merge(Element.not_restricted) }
48
-
49
- delegate :restricted?, to: :page, allow_nil: true
50
- delegate :public?, to: :element, allow_nil: true
51
-
52
- class << self
53
- # Returns the translated label for a content name.
54
- #
55
- # Translate it in your locale yml file:
56
- #
57
- # alchemy:
58
- # content_names:
59
- # foo: Bar
60
- #
61
- # Optionally you can scope your content name to an element:
62
- #
63
- # alchemy:
64
- # content_names:
65
- # article:
66
- # foo: Baz
67
- #
68
- def translated_label_for(content_name, element_name = nil)
69
- Alchemy.t(
70
- content_name,
71
- scope: "content_names.#{element_name}",
72
- default: Alchemy.t("content_names.#{content_name}", default: content_name.humanize),
73
- )
74
- end
75
- end
76
-
77
- # The content's view partial is dependent from its name
78
- #
79
- # == Define contents
80
- #
81
- # Contents are defined in the +config/alchemy/elements.yml+ file
82
- #
83
- # - name: article
84
- # contents:
85
- # - name: headline
86
- # type: EssenceText
87
- #
88
- # == Override the view
89
- #
90
- # Content partials live in +app/views/alchemy/essences+
91
- #
92
- def to_partial_path
93
- "alchemy/essences/#{essence_partial_name}_view"
94
- end
95
-
96
- # Settings from the elements.yml definition
97
- def settings
98
- return {} if definition.blank?
99
-
100
- @settings ||= definition.fetch(:settings, {})
101
- end
102
-
103
- # Fetches value from settings
104
- #
105
- # @param key [Symbol] - The hash key you want to fetch the value from
106
- # @param options [Hash] - An optional Hash that can override the settings.
107
- # Normally passed as options hash into the content
108
- # editor view.
109
- def settings_value(key, options = {})
110
- settings.update(options || {}).symbolize_keys[key.to_sym]
111
- end
112
-
113
- def siblings
114
- return [] if !element
115
-
116
- element.contents
117
- end
118
-
119
- # Gets the ingredient from essence
120
- def ingredient
121
- return nil if essence.nil?
122
-
123
- essence.ingredient
124
- end
125
-
126
- # Serialized object representation for json api
127
- #
128
- def serialize
129
- {
130
- name: name,
131
- value: serialized_ingredient,
132
- link: essence.try(:link),
133
- }.delete_if { |_k, v| v.blank? }
134
- end
135
-
136
- # Ingredient value from essence for json api
137
- #
138
- # If the essence responds to +serialized_ingredient+ method it takes this
139
- # otherwise it uses the ingredient column.
140
- #
141
- def serialized_ingredient
142
- essence.try(:serialized_ingredient) || ingredient
143
- end
144
-
145
- # Sets the ingredient from essence
146
- def ingredient=(value)
147
- raise EssenceMissingError if essence.nil?
148
-
149
- essence.ingredient = value
150
- end
151
-
152
- # Updates the essence.
153
- #
154
- # Called from +Alchemy::Element#update_contents+
155
- #
156
- # Adds errors to self.base if essence validation fails.
157
- #
158
- def update_essence(params = {})
159
- raise EssenceMissingError if essence.nil?
160
-
161
- if essence.update(params)
162
- true
163
- else
164
- errors.add(:essence, :validation_failed)
165
- false
166
- end
167
- end
168
-
169
- def essence_validation_failed?
170
- essence.errors.any?
171
- end
172
-
173
- def has_validations?
174
- definition["validate"].present?
175
- end
176
-
177
- # Returns a string used as dom id on html elements.
178
- def dom_id
179
- return "" if essence.nil?
180
-
181
- "#{essence_partial_name}_#{id}"
182
- end
183
-
184
- # Returns the translated name for displaying in labels, etc.
185
- def name_for_label
186
- self.class.translated_label_for(name, element.name)
187
- end
188
-
189
- def linked?
190
- essence && !essence.link.blank?
191
- end
192
-
193
- def deprecated?
194
- !!definition["deprecated"]
195
- end
196
-
197
- # Returns true if this content should be taken for element preview.
198
- def preview_content?
199
- !!definition["as_element_title"]
200
- end
201
-
202
- # Proxy method that returns the preview text from essence.
203
- #
204
- def preview_text(maxlength = 30)
205
- essence.preview_text(maxlength)
206
- end
207
-
208
- def essence_partial_name
209
- return "" if essence.nil?
210
-
211
- essence.partial_name
212
- end
213
-
214
- def normalized_essence_type
215
- self.class.normalize_essence_type(essence_type)
216
- end
217
-
218
- # Returns true if there is a tinymce setting defined on the content definiton
219
- # or if the +essence.has_tinymce?+ returns true.
220
- def has_tinymce?
221
- settings[:tinymce].present? || (essence.present? && essence.has_tinymce?)
222
- end
223
-
224
- # Returns true if there is a tinymce setting defined that contains settings.
225
- def has_custom_tinymce_config?
226
- settings[:tinymce].is_a?(Hash)
227
- end
228
-
229
- # Returns css class names for the content textarea.
230
- def tinymce_class_name
231
- "has_tinymce" + (has_custom_tinymce_config? ? " #{element.name}_#{name}" : "")
232
- end
233
-
234
- # Returns the default value from content definition
235
- #
236
- # If the value is a symbol it gets passed through i18n
237
- # inside the +alchemy.default_content_texts+ scope
238
- def default_value(default = definition[:default])
239
- case default
240
- when Symbol
241
- Alchemy.t(default, scope: :default_content_texts)
242
- else
243
- default
244
- end
245
- end
246
- end
247
- end