alchemy_cms 4.3.2 → 4.4.0

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 (139) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +92 -0
  3. data/.gitignore +1 -0
  4. data/CHANGELOG.md +59 -2
  5. data/Gemfile +6 -5
  6. data/README.md +7 -6
  7. data/alchemy_cms.gemspec +4 -2
  8. data/app/assets/config/alchemy_manifest.js +15 -0
  9. data/app/assets/javascripts/alchemy/admin.js +1 -0
  10. data/app/assets/javascripts/alchemy/alchemy.base.js.coffee +1 -13
  11. data/app/assets/javascripts/alchemy/alchemy.i18n.js.coffee +1 -1
  12. data/app/assets/javascripts/alchemy/alchemy.link_dialog.js.coffee +84 -87
  13. data/app/assets/javascripts/alchemy/alchemy.preview.js.coffee +0 -4
  14. data/app/assets/javascripts/alchemy/alchemy.sitemap.js.coffee +1 -1
  15. data/app/assets/javascripts/alchemy/alchemy.tinymce.js.coffee +2 -2
  16. data/app/assets/javascripts/alchemy/page_select.js +41 -0
  17. data/app/assets/javascripts/alchemy/templates/index.js +1 -0
  18. data/app/assets/javascripts/alchemy/templates/page.hbs +9 -0
  19. data/app/assets/stylesheets/alchemy/_mixins.scss +11 -1
  20. data/app/assets/stylesheets/alchemy/admin.scss +3 -0
  21. data/app/assets/stylesheets/alchemy/elements.scss +1 -0
  22. data/app/assets/stylesheets/alchemy/forms.scss +6 -5
  23. data/app/assets/stylesheets/alchemy/labels.scss +6 -0
  24. data/app/assets/stylesheets/alchemy/nodes.scss +154 -0
  25. data/app/assets/stylesheets/alchemy/page-select.scss +30 -0
  26. data/app/assets/stylesheets/alchemy/selects.scss +39 -22
  27. data/app/assets/stylesheets/alchemy/sitemap.scss +0 -33
  28. data/app/assets/stylesheets/alchemy/tags.scss +0 -3
  29. data/app/controllers/alchemy/admin/base_controller.rb +1 -0
  30. data/app/controllers/alchemy/admin/elements_controller.rb +24 -11
  31. data/app/controllers/alchemy/admin/languages_controller.rb +5 -0
  32. data/app/controllers/alchemy/admin/nodes_controller.rb +43 -0
  33. data/app/controllers/alchemy/admin/pages_controller.rb +1 -21
  34. data/app/controllers/alchemy/admin/resources_controller.rb +1 -1
  35. data/app/controllers/alchemy/admin/tags_controller.rb +1 -2
  36. data/app/controllers/alchemy/api/contents_controller.rb +17 -2
  37. data/app/controllers/alchemy/api/elements_controller.rb +26 -1
  38. data/app/controllers/alchemy/api/pages_controller.rb +78 -7
  39. data/app/controllers/alchemy/messages_controller.rb +23 -8
  40. data/app/helpers/alchemy/admin/contents_helper.rb +1 -1
  41. data/app/helpers/alchemy/admin/elements_helper.rb +6 -0
  42. data/app/helpers/alchemy/admin/essences_helper.rb +23 -4
  43. data/app/helpers/alchemy/elements_block_helper.rb +11 -3
  44. data/app/helpers/alchemy/elements_helper.rb +3 -3
  45. data/app/helpers/alchemy/essences_helper.rb +36 -9
  46. data/app/helpers/alchemy/pages_helper.rb +29 -0
  47. data/app/models/alchemy/content.rb +1 -1
  48. data/app/models/alchemy/element.rb +20 -8
  49. data/app/models/alchemy/element/element_contents.rb +6 -4
  50. data/app/models/alchemy/element/presenters.rb +2 -2
  51. data/app/models/alchemy/essence_page.rb +29 -0
  52. data/app/models/alchemy/essence_picture.rb +8 -3
  53. data/app/models/alchemy/language.rb +10 -2
  54. data/app/models/alchemy/node.rb +48 -0
  55. data/app/models/alchemy/page.rb +74 -3
  56. data/app/models/alchemy/page/page_elements.rb +12 -4
  57. data/app/models/alchemy/page/page_natures.rb +6 -0
  58. data/app/models/alchemy/page/page_scopes.rb +1 -1
  59. data/app/models/alchemy/picture.rb +5 -1
  60. data/app/models/concerns/alchemy/content_touching.rb +1 -1
  61. data/app/serializers/alchemy/element_serializer.rb +7 -1
  62. data/app/serializers/alchemy/page_serializer.rb +0 -4
  63. data/app/views/alchemy/_menubar.html.erb +1 -1
  64. data/app/views/alchemy/admin/elements/_element.html.erb +18 -2
  65. data/app/views/alchemy/admin/leave.html.erb +1 -1
  66. data/app/views/alchemy/admin/nodes/_form.html.erb +39 -0
  67. data/app/views/alchemy/admin/nodes/_node.html.erb +87 -0
  68. data/app/views/alchemy/admin/nodes/edit.html.erb +1 -0
  69. data/app/views/alchemy/admin/nodes/index.html.erb +58 -0
  70. data/app/views/alchemy/admin/nodes/new.html.erb +1 -0
  71. data/app/views/alchemy/admin/pages/_anchor_link.html.erb +22 -0
  72. data/app/views/alchemy/admin/pages/_form.html.erb +1 -1
  73. data/app/views/alchemy/admin/pages/_internal_link.html.erb +7 -11
  74. data/app/views/alchemy/admin/pages/_menu_fields.html.erb +33 -0
  75. data/app/views/alchemy/admin/pages/_sitemap.html.erb +0 -7
  76. data/app/views/alchemy/admin/pages/link.html.erb +4 -0
  77. data/app/views/alchemy/admin/partials/_language_tree_select.html.erb +7 -3
  78. data/app/views/alchemy/admin/partials/_routes.html.erb +3 -3
  79. data/app/views/alchemy/essences/_essence_boolean_view.html.erb +1 -0
  80. data/app/views/alchemy/essences/_essence_date_view.html.erb +1 -0
  81. data/app/views/alchemy/essences/_essence_file_view.html.erb +1 -0
  82. data/app/views/alchemy/essences/_essence_html_view.html.erb +1 -0
  83. data/app/views/alchemy/essences/_essence_link_view.html.erb +1 -0
  84. data/app/views/alchemy/essences/_essence_page_editor.html.erb +23 -0
  85. data/app/views/alchemy/essences/_essence_page_view.html.erb +5 -0
  86. data/app/views/alchemy/essences/_essence_picture_editor.html.erb +2 -0
  87. data/app/views/alchemy/essences/_essence_picture_view.html.erb +1 -0
  88. data/app/views/alchemy/essences/_essence_richtext_view.html.erb +1 -0
  89. data/app/views/alchemy/essences/_essence_select_editor.html.erb +1 -0
  90. data/app/views/alchemy/essences/_essence_select_view.html.erb +1 -0
  91. data/app/views/alchemy/essences/_essence_text_editor.html.erb +3 -0
  92. data/app/views/alchemy/essences/_essence_text_view.html.erb +1 -0
  93. data/config/alchemy/modules.yml +13 -4
  94. data/config/initializers/assets.rb +1 -13
  95. data/config/locales/alchemy.en.yml +27 -9
  96. data/config/routes.rb +11 -3
  97. data/db/migrate/20191016073858_create_alchemy_essence_pages.rb +8 -0
  98. data/db/migrate/20191029212236_create_alchemy_nodes.rb +24 -0
  99. data/lib/alchemy/admin/locale.rb +1 -1
  100. data/lib/alchemy/auth_accessors.rb +8 -2
  101. data/lib/alchemy/cache_digests/template_tracker.rb +8 -5
  102. data/lib/alchemy/elements_finder.rb +17 -14
  103. data/lib/alchemy/engine.rb +4 -0
  104. data/lib/alchemy/essence.rb +40 -2
  105. data/lib/alchemy/permissions.rb +2 -0
  106. data/lib/alchemy/tasks/tidy.rb +1 -1
  107. data/lib/alchemy/test_support/essence_shared_examples.rb +25 -8
  108. data/lib/alchemy/test_support/factories/essence_page_factory.rb +10 -0
  109. data/lib/alchemy/test_support/factories/essence_picture_factory.rb +5 -0
  110. data/lib/alchemy/test_support/factories/node_factory.rb +21 -0
  111. data/lib/alchemy/version.rb +1 -1
  112. data/lib/rails/generators/alchemy/elements/elements_generator.rb +0 -1
  113. data/lib/rails/generators/alchemy/elements/templates/view.html.erb +3 -3
  114. data/lib/rails/generators/alchemy/elements/templates/view.html.haml +3 -3
  115. data/lib/rails/generators/alchemy/elements/templates/view.html.slim +3 -3
  116. data/lib/rails/generators/alchemy/install/files/{_article_view.html.erb → _article.html.erb} +2 -2
  117. data/lib/rails/generators/alchemy/install/files/application.html.erb +13 -10
  118. data/lib/rails/generators/alchemy/install/install_generator.rb +2 -11
  119. data/lib/rails/generators/alchemy/menus/menus_generator.rb +24 -0
  120. data/lib/rails/generators/alchemy/menus/templates/node.html.erb +19 -0
  121. data/lib/rails/generators/alchemy/menus/templates/node.html.haml +16 -0
  122. data/lib/rails/generators/alchemy/menus/templates/node.html.slim +16 -0
  123. data/lib/rails/generators/alchemy/menus/templates/wrapper.html.erb +8 -0
  124. data/lib/rails/generators/alchemy/menus/templates/wrapper.html.haml +6 -0
  125. data/lib/rails/generators/alchemy/menus/templates/wrapper.html.slim +6 -0
  126. data/lib/rails/generators/alchemy/views/views_generator.rb +1 -1
  127. data/lib/tasks/alchemy/convert.rake +60 -0
  128. metadata +79 -20
  129. data/.rspec +0 -1
  130. data/.travis.yml +0 -28
  131. data/app/models/alchemy/page/page_users.rb +0 -60
  132. data/app/views/alchemy/admin/elements/list.html.erb +0 -16
  133. data/app/views/alchemy/admin/pages/_page_for_links.html.erb +0 -53
  134. data/lib/rails/generators/alchemy/elements/templates/editor.html.erb +0 -9
  135. data/lib/rails/generators/alchemy/elements/templates/editor.html.haml +0 -8
  136. data/lib/rails/generators/alchemy/elements/templates/editor.html.slim +0 -8
  137. data/lib/rails/generators/alchemy/install/files/_article_editor.html.erb +0 -5
  138. data/lib/rails/generators/alchemy/install/files/alchemy.de.yml +0 -31
  139. data/lib/rails/generators/alchemy/install/files/alchemy.es.yml +0 -31
@@ -38,7 +38,7 @@ module Alchemy
38
38
 
39
39
  # Renders the label and a remove link for a content.
40
40
  def content_label(content)
41
- content_tag :label do
41
+ content_tag :label, for: content.form_field_id do
42
42
  [render_hint_for(content), render_content_name(content)].compact.join(' ').html_safe
43
43
  end
44
44
  end
@@ -48,12 +48,18 @@ module Alchemy
48
48
  # @note If the partial is not found
49
49
  # <tt>alchemy/elements/_editor_not_found.html.erb</tt> gets rendered.
50
50
  #
51
+ # @deprecated Using element editor partials is deprecated and will be removed in Alchemy 5.0
51
52
  def render_editor(element)
52
53
  if element.nil?
53
54
  warning('Element is nil')
54
55
  render "alchemy/elements/editor_not_found", {name: 'nil'}
55
56
  return
56
57
  end
58
+ Alchemy::Deprecation.warn <<~WARN
59
+ Using element editor partials is deprecated and will be removed in Alchemy 5.0.
60
+ You can delete the `app/views/alchemy/elements/_#{element.name}_editor` partial
61
+ and Alchemy will render the content editors for you.
62
+ WARN
57
63
 
58
64
  render "alchemy/elements/#{element.name}_editor", element: element
59
65
  rescue ActionView::MissingTemplate => e
@@ -8,9 +8,23 @@ module Alchemy
8
8
 
9
9
  # Renders the Content editor partial from the given Content.
10
10
  # For options see -> render_essence
11
+ # @deprecated
11
12
  def render_essence_editor(content, options = {}, html_options = {})
13
+ if !options.empty?
14
+ Alchemy::Deprecation.warn <<~WARN
15
+ Passing options to `render_essence_editor` is deprecated and will be removed from Alchemy 5.0.
16
+ Add your static `#{options.keys}` options to the `#{content.name}` content definitions `settings` of `#{content.element&.name}` element.
17
+ For dynamic options consider adding your own essence class.
18
+ WARN
19
+ end
20
+ if !html_options.empty?
21
+ Alchemy::Deprecation.warn <<~WARN
22
+ Passing html_options to `render_essence_editor` is deprecated and will be removed in Alchemy 5.0 without replacement.
23
+ WARN
24
+ end
12
25
  render_essence(content, :editor, {for_editor: options}, html_options)
13
26
  end
27
+ deprecate :render_essence_editor, deprecator: Alchemy::Deprecation
14
28
 
15
29
  # Renders the Content editor partial found in views/contents/ for the content with name inside the passed Element.
16
30
  # For options see -> render_essence
@@ -18,7 +32,7 @@ module Alchemy
18
32
  # Content creation on the fly:
19
33
  #
20
34
  # If you update the elements.yml file after creating an element this helper displays a error message with an option to create the content.
21
- #
35
+ # @deprecated
22
36
  def render_essence_editor_by_name(element, name, options = {}, html_options = {})
23
37
  if element.blank?
24
38
  return warning('Element is nil', Alchemy.t(:no_element_given))
@@ -30,6 +44,7 @@ module Alchemy
30
44
  render_essence_editor(content, options, html_options)
31
45
  end
32
46
  end
47
+ deprecate :render_essence_editor_by_name, deprecator: Alchemy::Deprecation
33
48
 
34
49
  # Returns all public pages from current language as an option tags string suitable or the Rails +select_tag+ helper.
35
50
  #
@@ -41,7 +56,7 @@ module Alchemy
41
56
  # Used as prompt message in the select tag
42
57
  # @param [Symbol]
43
58
  # Method that is called on the page object to get the value that is passed with the params of the form.
44
- #
59
+ # @deprecated
45
60
  def pages_for_select(pages = nil, selected = nil, prompt = "Choose page", page_attribute = :id)
46
61
  values = [[Alchemy.t(prompt), ""]]
47
62
  pages ||= begin
@@ -51,12 +66,14 @@ module Alchemy
51
66
  values += pages_attributes_for_select(pages, page_attribute, nested)
52
67
  options_for_select(values, selected.to_s)
53
68
  end
69
+ deprecate :pages_for_select, deprecator: Alchemy::Deprecation
54
70
 
55
71
  # Renders the missing content partial
56
- #
72
+ # @deprecated
57
73
  def render_missing_content(element, name, options)
58
74
  render 'alchemy/admin/contents/missing', {element: element, name: name, options: options}
59
75
  end
76
+ deprecate :render_missing_content, deprecator: Alchemy::Deprecation
60
77
 
61
78
  # Renders a thumbnail for given EssencePicture content with correct cropping and size
62
79
  def essence_picture_thumbnail(content, options = {})
@@ -101,6 +118,7 @@ module Alchemy
101
118
  ]
102
119
  end
103
120
  end
121
+ deprecate :pages_attributes_for_select, deprecator: Alchemy::Deprecation
104
122
 
105
123
  # Returns the page name for pages_for_select helper
106
124
  #
@@ -111,11 +129,12 @@ module Alchemy
111
129
  #
112
130
  def page_name_attribute_for_select(page, indent = false)
113
131
  if indent
114
- ("&nbsp;&nbsp;" * (page.level - 1) + page.name).html_safe
132
+ ("&nbsp;&nbsp;" * (page.depth - 1) + page.name).html_safe
115
133
  else
116
134
  page.name
117
135
  end
118
136
  end
137
+ deprecate :page_name_attribute_for_select, deprecator: Alchemy::Deprecation
119
138
  end
120
139
  end
121
140
  end
@@ -26,8 +26,15 @@ module Alchemy
26
26
  class ElementViewHelper < BlockHelper
27
27
  # Renders one of the element's contents.
28
28
  #
29
- def render(name, *args)
30
- helpers.render_essence_view_by_name(element, name.to_s, *args)
29
+ def render(name, options = {}, html_options = {})
30
+ content = element.content_by_name(name)
31
+ return if content.nil?
32
+
33
+ helpers.render(content, {
34
+ content: content,
35
+ options: options,
36
+ html_options: html_options
37
+ })
31
38
  end
32
39
 
33
40
  # Returns one of the element's contents (ie. essence instances).
@@ -56,11 +63,12 @@ module Alchemy
56
63
  end
57
64
 
58
65
  # Block-level helper class for element editors.
59
- #
66
+ # @deprecated
60
67
  class ElementEditorHelper < BlockHelper
61
68
  def edit(name, *args)
62
69
  helpers.render_essence_editor_by_name(element, name.to_s, *args)
63
70
  end
71
+ deprecate :edit, deprecator: Alchemy::Deprecation
64
72
  end
65
73
 
66
74
  # Block-level helper for element views. Constructs a DOM element wrapping
@@ -123,7 +123,7 @@ module Alchemy
123
123
  #
124
124
  # == View partial naming
125
125
  #
126
- # The partial has to be named after the name of the element as defined in the <tt>elements.yml</tt> file and has to be suffixed with <tt>_view</tt>.
126
+ # The partial has to be named after the name of the element as defined in the <tt>elements.yml</tt> file.
127
127
  #
128
128
  # === Example
129
129
  #
@@ -137,7 +137,7 @@ module Alchemy
137
137
  #
138
138
  # Then your element view partial has to be named like:
139
139
  #
140
- # app/views/alchemy/elements/_headline_view.html.{erb|haml|slim}
140
+ # app/views/alchemy/elements/_headline.html.{erb|haml|slim}
141
141
  #
142
142
  # === Element partials generator
143
143
  #
@@ -147,7 +147,7 @@ module Alchemy
147
147
  #
148
148
  # == Usage
149
149
  #
150
- # <%= render_element(Alchemy::Element.published.named(:headline).first) %>
150
+ # <%= render_element(Alchemy::Element.available.named(:headline).first) %>
151
151
  #
152
152
  # @param [Alchemy::Element] element
153
153
  # The element you want to render the view for
@@ -27,6 +27,7 @@ module Alchemy
27
27
  #
28
28
  # And the +render_essence_editor_by_name+ helper for Alchemy backend views.
29
29
  #
30
+ # @deprecated Use Rails' `render(content)` method directly instead
30
31
  module EssencesHelper
31
32
  # Renders the +Essence+ view partial from +Element+ by name.
32
33
  #
@@ -38,14 +39,22 @@ module Alchemy
38
39
  #
39
40
  # <%= render_essence_view_by_name(element, "intro") %>
40
41
  #
42
+ # @deprecated Use Rails' `render(content)` method directly instead
41
43
  def render_essence_view_by_name(element, name, options = {}, html_options = {})
42
44
  if element.blank?
43
- warning('Element is nil')
44
- return ""
45
+ warning('Element is nil') && return
45
46
  end
46
47
  content = element.content_by_name(name)
47
- render_essence_view(content, options, html_options)
48
+ return if content.nil?
49
+
50
+ render content, {
51
+ content: content,
52
+ options: options,
53
+ html_options: html_options
54
+ }
48
55
  end
56
+ deprecate render_essence_view_by_name: "Use Rails' `render(content)` method directly instead",
57
+ deprecator: Alchemy::Deprecation
49
58
 
50
59
  # Renders the +Esssence+ partial for given +Content+.
51
60
  #
@@ -62,6 +71,7 @@ module Alchemy
62
71
  # for_view: {}
63
72
  # for_editor: {}
64
73
  #
74
+ # @deprecated Use Rails' `render(content)` method directly instead
65
75
  def render_essence(content, part = :view, options = {}, html_options = {})
66
76
  options = {for_view: {}, for_editor: {}}.update(options)
67
77
  if content.nil?
@@ -69,12 +79,22 @@ module Alchemy
69
79
  elsif content.essence.nil?
70
80
  return part == :view ? "" : warning('Essence is nil', Alchemy.t(:content_essence_not_found))
71
81
  end
72
- render partial: "alchemy/essences/#{content.essence_partial_name}_#{part}", locals: {
73
- content: content,
74
- options: options["for_#{part}".to_sym],
75
- html_options: html_options
76
- }
82
+ if part == :view
83
+ render content, {
84
+ content: content,
85
+ options: options[:for_view],
86
+ html_options: html_options
87
+ }
88
+ else
89
+ render "alchemy/essences/#{content.essence_partial_name}_editor", {
90
+ content: content,
91
+ options: options,
92
+ html_options: html_options
93
+ }
94
+ end
77
95
  end
96
+ deprecate render_essence: "Use Rails' `render(content)` method directly instead",
97
+ deprecator: Alchemy::Deprecation
78
98
 
79
99
  # Renders the +Esssence+ view partial for given +Content+.
80
100
  #
@@ -85,8 +105,15 @@ module Alchemy
85
105
  # :show_caption => false # Pass Boolean to show/hide the caption of an EssencePicture. [Default true]
86
106
  # :disable_link => true # You can surpress the link of an EssencePicture. Default false
87
107
  #
108
+ # @deprecated Use Rails' `render(content)` method directly instead
88
109
  def render_essence_view(content, options = {}, html_options = {})
89
- render_essence(content, :view, {for_view: options}, html_options)
110
+ render content, {
111
+ content: content,
112
+ options: options,
113
+ html_options: html_options
114
+ }
90
115
  end
116
+ deprecate render_essence_view: "Use Rails' `render(content)` method directly instead",
117
+ deprecator: Alchemy::Deprecation
91
118
  end
92
119
  end
@@ -73,6 +73,7 @@ module Alchemy
73
73
  end
74
74
 
75
75
  # Renders the navigation.
76
+ # @deprecated
76
77
  #
77
78
  # It produces a html <ul><li></li></ul> structure with all necessary classes so you can produce every navigation the web uses today.
78
79
  # I.E. dropdown-navigations, simple mainnavigations or even complex nested ones.
@@ -177,6 +178,33 @@ module Alchemy
177
178
  pages: pages,
178
179
  html_options: html_options
179
180
  end
181
+ deprecate render_navigation: 'Create a menu and use render_menu instead', deprecator: Alchemy::Deprecation
182
+
183
+ # Renders a menu partial
184
+ #
185
+ # Menu partials are placed in the `app/views/alchemy/menus` folder
186
+ # Use the `rails g alchemy:menus` generator to create the partials
187
+ #
188
+ # @param [String] - Name of the menu
189
+ # @param [Hash] - A set of options available in your menu partials
190
+ def render_menu(name, options = {})
191
+ root_node = Alchemy::Node.roots.find_by(name: name)
192
+ if root_node.nil?
193
+ warning("Menu with name #{name} not found!")
194
+ return
195
+ end
196
+
197
+ options = {
198
+ node_partial_name: "#{root_node.view_folder_name}/node"
199
+ }.merge(options)
200
+
201
+ render(root_node, menu: root_node, node: root_node, options: options)
202
+ rescue ActionView::MissingTemplate => e
203
+ warning <<~WARN
204
+ Menu partial not found for #{name}.
205
+ #{e}
206
+ WARN
207
+ end
180
208
 
181
209
  # Renders navigation the children and all siblings of the given page (standard is the current page).
182
210
  #
@@ -206,6 +234,7 @@ module Alchemy
206
234
  return nil
207
235
  end
208
236
  end
237
+ deprecate :render_subnavigation, deprecator: Alchemy::Deprecation
209
238
 
210
239
  # Returns true if page is in the active branch
211
240
  def page_active?(page)
@@ -24,7 +24,7 @@ module Alchemy
24
24
  # Concerns
25
25
  include Alchemy::Content::Factory
26
26
 
27
- belongs_to :essence, polymorphic: true, dependent: :destroy
27
+ belongs_to :essence, polymorphic: true, dependent: :destroy, inverse_of: :content
28
28
  belongs_to :element, touch: true, inverse_of: :contents
29
29
  has_one :page, through: :element
30
30
 
@@ -33,7 +33,8 @@ module Alchemy
33
33
  "contents",
34
34
  "hint",
35
35
  "taggable",
36
- "compact"
36
+ "compact",
37
+ "message"
37
38
  ].freeze
38
39
 
39
40
  SKIPPED_ATTRIBUTES_ON_COPY = [
@@ -61,10 +62,10 @@ module Alchemy
61
62
 
62
63
  # Content positions are scoped by their essence_type, so positions can be the same for different contents.
63
64
  # In order to get contents in creation order we also order them by id.
64
- has_many :contents, -> { order(:position, :id) }, dependent: :destroy
65
+ has_many :contents, -> { order(:position, :id) }, dependent: :destroy, inverse_of: :element
65
66
 
66
67
  has_many :all_nested_elements,
67
- -> { order(:position) },
68
+ -> { order(:position).not_trashed },
68
69
  class_name: 'Alchemy::Element',
69
70
  foreign_key: :parent_element_id,
70
71
  dependent: :destroy
@@ -73,15 +74,17 @@ module Alchemy
73
74
  -> { order(:position).available },
74
75
  class_name: 'Alchemy::Element',
75
76
  foreign_key: :parent_element_id,
76
- dependent: :destroy
77
+ dependent: :destroy,
78
+ inverse_of: :parent_element
77
79
 
78
- belongs_to :page, touch: true, inverse_of: :all_elements
80
+ belongs_to :page, touch: true, inverse_of: :elements
79
81
 
80
82
  # A nested element belongs to a parent element.
81
83
  belongs_to :parent_element,
82
84
  class_name: 'Alchemy::Element',
83
85
  optional: true,
84
- touch: true
86
+ touch: true,
87
+ inverse_of: :nested_elements
85
88
 
86
89
  has_and_belongs_to_many :touchable_pages, -> { distinct },
87
90
  class_name: 'Alchemy::Page',
@@ -216,7 +219,6 @@ module Alchemy
216
219
  return true if page.nil?
217
220
  unless touchable_pages.include? page
218
221
  touchable_pages << page
219
- save
220
222
  end
221
223
  end
222
224
 
@@ -261,7 +263,17 @@ module Alchemy
261
263
  # Element partials live in +app/views/alchemy/elements+
262
264
  #
263
265
  def to_partial_path
264
- "alchemy/elements/#{name}_view"
266
+ if Alchemy::LOOKUP_CONTEXT.exists?("#{name}_view", ["elements"], true)
267
+ Alchemy::Deprecation.warn <<~WARN
268
+ Having the `_view` suffix on your element view partials is deprecated
269
+ and will not be supported in Alchemy 5.0 anymore. You can safely remove the suffix now.
270
+
271
+ Please also rename the local `element` or `#{name}_view` variable into `#{name}`.
272
+ WARN
273
+ "alchemy/elements/#{name}_view"
274
+ else
275
+ "alchemy/elements/#{name}"
276
+ end
265
277
  end
266
278
 
267
279
  # Returns the key that's taken for cache path.
@@ -16,13 +16,15 @@ module Alchemy
16
16
 
17
17
  # All contents from element by given name.
18
18
  def contents_by_name(name)
19
- contents.where(name: name)
19
+ contents.select { |content| content.name == name.to_s }
20
20
  end
21
21
  alias_method :all_contents_by_name, :contents_by_name
22
22
 
23
23
  # All contents from element by given essence type.
24
24
  def contents_by_type(essence_type)
25
- contents.where(essence_type: Content.normalize_essence_type(essence_type))
25
+ contents.select do |content|
26
+ content.essence_type == Content.normalize_essence_type(essence_type)
27
+ end
26
28
  end
27
29
  alias_method :all_contents_by_type, :contents_by_type
28
30
 
@@ -99,7 +101,7 @@ module Alchemy
99
101
  log_warning "Element #{name} is missing the content definition for #{content_name}"
100
102
  nil
101
103
  else
102
- content_definitions.detect { |d| d['name'] == content_name }
104
+ content_definitions.detect { |d| d['name'] == content_name.to_s }
103
105
  end
104
106
  end
105
107
 
@@ -134,7 +136,7 @@ module Alchemy
134
136
  def content_for_rss_meta(type)
135
137
  definition = content_definitions.detect { |c| c["rss_#{type}"] }
136
138
  return if definition.blank?
137
- contents.find_by(name: definition['name'])
139
+ contents.detect { |content| content.name == definition['name'] }
138
140
  end
139
141
 
140
142
  # creates the contents for this element as described in the elements.yml
@@ -96,8 +96,8 @@ module Alchemy
96
96
  private
97
97
 
98
98
  def preview_text_from_nested_elements(maxlength)
99
- return unless nested_elements.present?
100
- nested_elements.first.preview_text(maxlength)
99
+ return if all_nested_elements.empty?
100
+ all_nested_elements.first.preview_text(maxlength)
101
101
  end
102
102
 
103
103
  def preview_text_from_preview_content(maxlength)
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Alchemy
4
+ class EssencePage < BaseRecord
5
+ PAGE_ID = /\A\d+\z/
6
+
7
+ acts_as_essence(
8
+ ingredient_column: :page,
9
+ preview_text_method: :name,
10
+ belongs_to: {
11
+ class_name: 'Alchemy::Page',
12
+ foreign_key: :page_id,
13
+ inverse_of: :essence_pages,
14
+ optional: true
15
+ }
16
+ )
17
+
18
+ def ingredient=(page)
19
+ case page
20
+ when PAGE_ID
21
+ self.page = Alchemy::Page.new(id: page)
22
+ when Alchemy::Page
23
+ self.page = page
24
+ else
25
+ super
26
+ end
27
+ end
28
+ end
29
+ end