alchemy_cms 4.3.2 → 4.4.0

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -95,6 +95,7 @@ module Alchemy
95
95
  :alchemy_admin_attachments,
96
96
  :alchemy_admin_dashboard,
97
97
  :alchemy_admin_layoutpages,
98
+ :alchemy_admin_nodes,
98
99
  :alchemy_admin_pages,
99
100
  :alchemy_admin_pictures,
100
101
  :alchemy_admin_tags,
@@ -116,6 +117,7 @@ module Alchemy
116
117
  can :manage, Alchemy::EssenceFile
117
118
  can :manage, Alchemy::EssencePicture
118
119
  can :manage, Alchemy::LegacyPageUrl
120
+ can :manage, Alchemy::Node
119
121
  can :read, Alchemy::Picture
120
122
  can [:read, :autocomplete], Alchemy::Tag
121
123
  can(:edit_content, Alchemy::Page) { |p| p.editable_by?(@user) }
@@ -45,7 +45,7 @@ module Alchemy
45
45
 
46
46
  def remove_orphaned_elements
47
47
  puts "\n## Removing orphaned elements"
48
- elements = Alchemy::Element.unscoped.all
48
+ elements = Alchemy::Element.unscoped.not_nested
49
49
  if elements.any?
50
50
  orphaned_elements = elements.select do |element|
51
51
  element.page.nil? && element.page_id.present?
@@ -1,16 +1,33 @@
1
- require 'rails_helper'
1
+ # frozen_string_literal: true
2
2
 
3
- shared_examples_for "an essence" do
3
+ require 'shoulda-matchers'
4
+ require 'alchemy/test_support/factories/page_factory'
5
+ require 'alchemy/test_support/factories/element_factory'
6
+ require 'alchemy/test_support/factories/content_factory'
7
+
8
+ RSpec.shared_examples_for "an essence" do
4
9
  let(:element) { Alchemy::Element.new }
5
10
  let(:content) { Alchemy::Content.new(name: 'foo') }
6
11
  let(:content_definition) { {'name' => 'foo'} }
7
12
 
13
+ describe 'eager loading' do
14
+ before do
15
+ 2.times { described_class.create! }
16
+ end
17
+
18
+ it 'does not throw error if eager loaded' do
19
+ expect {
20
+ described_class.all.includes(:ingredient_association).to_a
21
+ }.to_not raise_error
22
+ end
23
+ end
24
+
8
25
  it "touches the content after update" do
9
- element = create(:alchemy_element)
10
- content = create(:alchemy_content, element: element, essence: essence, essence_type: essence.class.name)
26
+ element = FactoryBot.create(:alchemy_element)
27
+ content = FactoryBot.create(:alchemy_content, element: element, essence: essence, essence_type: essence.class.name)
11
28
 
12
29
  content.update_column(:updated_at, 3.days.ago)
13
- content.essence.update_attributes(essence.ingredient_column.to_sym => ingredient_value)
30
+ content.essence.update(essence.ingredient_column.to_sym => ingredient_value)
14
31
 
15
32
  content.reload
16
33
  expect(content.updated_at).to be_within(3.seconds).of(Time.current)
@@ -153,7 +170,7 @@ shared_examples_for "an essence" do
153
170
 
154
171
  describe 'uniqueness' do
155
172
  before do
156
- allow(essence).to receive(:element).and_return(build_stubbed(:alchemy_element))
173
+ allow(essence).to receive(:element).and_return(FactoryBot.build_stubbed(:alchemy_element))
157
174
  essence.update(essence.ingredient_column.to_sym => ingredient_value)
158
175
  end
159
176
 
@@ -256,8 +273,8 @@ shared_examples_for "an essence" do
256
273
  end
257
274
 
258
275
  describe 'essence relations' do
259
- let(:page) { create(:alchemy_page, :restricted) }
260
- let(:element) { create(:alchemy_element) }
276
+ let(:page) { FactoryBot.create(:alchemy_page, :restricted) }
277
+ let(:element) { FactoryBot.create(:alchemy_element) }
261
278
 
262
279
  it "registers itself on page as essence relation" do
263
280
  expect(page.respond_to?(essence.class.model_name.route_key)).to be(true)
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'factory_bot'
4
+ require 'alchemy/test_support/factories/page_factory'
5
+
6
+ FactoryBot.define do
7
+ factory :alchemy_essence_page, class: 'Alchemy::EssencePage' do
8
+ page factory: :alchemy_page
9
+ end
10
+ end
@@ -1,10 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'factory_bot'
4
+ require 'alchemy/test_support/factories/content_factory'
4
5
  require 'alchemy/test_support/factories/picture_factory'
5
6
 
6
7
  FactoryBot.define do
7
8
  factory :alchemy_essence_picture, class: 'Alchemy::EssencePicture' do
8
9
  picture factory: :alchemy_picture
10
+
11
+ trait :with_content do
12
+ association :content, factory: :alchemy_content
13
+ end
9
14
  end
10
15
  end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'factory_bot'
4
+ require 'alchemy/test_support/factories/language_factory'
5
+ require 'alchemy/test_support/factories/page_factory'
6
+
7
+ FactoryBot.define do
8
+ factory :alchemy_node, class: 'Alchemy::Node' do
9
+ language { Alchemy::Language.default }
10
+ name { 'A Node' }
11
+
12
+ trait :with_page do
13
+ association :page, factory: :alchemy_page
14
+ name { nil }
15
+ end
16
+
17
+ trait :with_url do
18
+ url { 'https://example.com' }
19
+ end
20
+ end
21
+ end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Alchemy
4
- VERSION = "4.3.2"
4
+ VERSION = "4.4.0"
5
5
 
6
6
  def self.version
7
7
  VERSION
@@ -19,7 +19,6 @@ module Alchemy
19
19
  raise "Element name '#{element['name']}' has wrong format. Only lowercase and non whitespace characters allowed."
20
20
  end
21
21
 
22
- conditional_template "editor.html.#{template_engine}", "#{elements_dir}/_#{@element_name}_editor.html.#{template_engine}"
23
22
  conditional_template "view.html.#{template_engine}", "#{elements_dir}/_#{@element_name}_view.html.#{template_engine}"
24
23
  end
25
24
  end
@@ -1,5 +1,5 @@
1
- <%%- cache(<%= @element_name %>_view) do -%>
2
- <%%= element_view_for(<%= @element_name %>_view) do |el| -%>
1
+ <%%- cache(<%= @element_name %>) do -%>
2
+ <%%= element_view_for(<%= @element_name %>) do |el| -%>
3
3
  <%- @contents.each do |content| -%>
4
4
  <%- if @contents.length > 1 -%>
5
5
  <div class="<%= content["name"] %>">
@@ -10,7 +10,7 @@
10
10
  <%- end -%>
11
11
  <%- end -%>
12
12
  <%- if @element['nestable_elements'].present? -%>
13
- <%%= render <%= @element_name %>_view.nested_elements %>
13
+ <%%= render <%= @element_name %>.nested_elements.available %>
14
14
  <%- end -%>
15
15
  <%%- end -%>
16
16
  <%%- end -%>
@@ -1,5 +1,5 @@
1
- - cache(<%= @element_name -%>_view) do
2
- = element_view_for(<%= @element_name -%>_view) do |el|
1
+ - cache(<%= @element_name -%>) do
2
+ = element_view_for(<%= @element_name -%>) do |el|
3
3
  <%- @contents.each do |content| -%>
4
4
  <%- if @contents.length > 1 -%>
5
5
  .<%= content["name"] %>
@@ -9,5 +9,5 @@
9
9
  <%- end -%>
10
10
  <%- end -%>
11
11
  <%- if @element['nestable_elements'].present? -%>
12
- = render <%= @element_name -%>_view.nested_elements
12
+ = render <%= @element_name -%>.nested_elements.available
13
13
  <%- end -%>
@@ -1,5 +1,5 @@
1
- - cache(<%= @element_name -%>_view) do
2
- = element_view_for(<%= @element_name -%>_view) do |el|
1
+ - cache(<%= @element_name -%>) do
2
+ = element_view_for(<%= @element_name -%>) do |el|
3
3
  <%- @contents.each do |content| -%>
4
4
  <%- if @contents.length > 1 -%>
5
5
  .<%= content["name"] %>
@@ -9,5 +9,5 @@
9
9
  <%- end -%>
10
10
  <%- end -%>
11
11
  <%- if @element['nestable_elements'].present? -%>
12
- = render <%= @element_name -%>_view.nested_elements
12
+ = render <%= @element_name -%>.nested_elements.available
13
13
  <%- end -%>
@@ -1,5 +1,5 @@
1
- <%- cache(element) do -%>
2
- <%= element_view_for(element, tag: 'article') do |el| -%>
1
+ <%- cache(article) do -%>
2
+ <%= element_view_for(article, tag: 'article') do |el| -%>
3
3
  <h2><%= el.render :headline %></h2>
4
4
  <%= el.render :picture, size: '1200x600' %>
5
5
  <%= el.render :text %>
@@ -1,13 +1,16 @@
1
1
  <!DOCTYPE html>
2
2
  <html lang="<%= @page.language_code %>">
3
- <head>
4
- <%= render "alchemy/pages/meta_data" %>
5
- <%= stylesheet_link_tag 'application', media: 'all' %>
6
- <%= javascript_include_tag 'application' %>
7
- <%= csrf_meta_tags %>
8
- </head>
9
- <body>
10
- <%= yield %>
11
- <%= render "alchemy/edit_mode" %>
12
- </body>
3
+ <head>
4
+ <%= render "alchemy/pages/meta_data" %>
5
+ <%= csrf_meta_tags %>
6
+ <%= csp_meta_tag %>
7
+
8
+ <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
9
+ <%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
10
+ </head>
11
+
12
+ <body>
13
+ <%= yield %>
14
+ <%= render "alchemy/edit_mode" %>
15
+ </body>
13
16
  </html>
@@ -41,18 +41,9 @@ module Alchemy
41
41
  create_file "app/assets/stylesheets/application.css", "/*\n#{stylesheet_require} */\n"
42
42
  end
43
43
 
44
- [
45
- "_article_editor.html.erb",
46
- "_article_view.html.erb"
47
- ].each do |file|
48
- copy_file file, "app/views/alchemy/elements/#{file}"
49
- end
50
-
44
+ copy_file "_article.html.erb", "app/views/alchemy/elements/_article.html.erb"
51
45
  copy_file "_standard.html.erb", "app/views/alchemy/page_layouts/_standard.html.erb"
52
-
53
- %w(de en es).each do |locale|
54
- copy_file "alchemy.#{locale}.yml", "config/locales/alchemy.#{locale}.yml"
55
- end
46
+ copy_file "alchemy.en.yml", "config/locales/alchemy.en.yml"
56
47
  end
57
48
 
58
49
  def copy_dragonfly_config
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../base'
4
+
5
+ module Alchemy
6
+ module Generators
7
+ class MenusGenerator < Base
8
+ desc "This generator generates Alchemy menu partials."
9
+ source_root File.expand_path('templates', __dir__)
10
+
11
+ def create_partials
12
+ menus = Alchemy::Node.roots
13
+ return unless menus
14
+
15
+ menus.each do |menu|
16
+ conditional_template "wrapper.html.#{template_engine}",
17
+ "app/views/#{menu.view_folder_name}/_wrapper.html.#{template_engine}"
18
+ conditional_template "node.html.#{template_engine}",
19
+ "app/views/#{menu.view_folder_name}/_node.html.#{template_engine}"
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,19 @@
1
+ <%% cache node do %>
2
+ <%%= content_tag :li, class: ['nav-item', node.children.any? ? 'dropdown' : nil].compact do %>
3
+ <%%= link_to_if node.url,
4
+ node.name,
5
+ @preview_mode ? 'javascript: void(0)' : node.url,
6
+ class: ['nav-link', current_page?(node.url) ? 'active' : nil].compact,
7
+ title: node.title,
8
+ target: node.external? ? '_blank' : nil,
9
+ rel: node.nofollow? ? 'nofollow' : nil %>
10
+ <%% if node.children.any? %>
11
+ <ul class="dropdown-menu">
12
+ <%%= render partial: options[:node_partial_name],
13
+ collection: node.children.includes(:page, :children),
14
+ locals: { options: options },
15
+ as: 'node' %>
16
+ </ul>
17
+ <%% end %>
18
+ <%% end %>
19
+ <%% end %>
@@ -0,0 +1,16 @@
1
+ - cache node do
2
+ = content_tag :li,
3
+ class: ['nav-item', node.children.any? ? 'dropdown' : nil].compact do
4
+ = link_to_if node.url,
5
+ node.name,
6
+ @preview_mode ? 'javascript: void(0)' : node.url,
7
+ class: ['nav-link', current_page?(node.url) ? 'active' : nil].compact,
8
+ title: node.title,
9
+ target: node.external? ? '_blank' : nil,
10
+ rel: node.nofollow? ? 'nofollow' : nil
11
+ - if node.children.any?
12
+ %ul.dropdown-menu
13
+ = render partial: options[:node_partial_name],
14
+ collection: node.children.includes(:page, :children),
15
+ locals: { options: options },
16
+ as: 'node'
@@ -0,0 +1,16 @@
1
+ - cache node do
2
+ = content_tag :li,
3
+ class: ['nav-item', node.children.any? ? 'dropdown' : nil].compact do
4
+ = link_to_if node.url,
5
+ node.name,
6
+ @preview_mode ? 'javascript: void(0)' : node.url,
7
+ class: ['nav-link', current_page?(node.url) ? 'active' : nil].compact,
8
+ title: node.title,
9
+ target: node.external? ? '_blank' : nil,
10
+ rel: node.nofollow? ? 'nofollow' : nil
11
+ - if node.children.any?
12
+ ul.dropdown-menu
13
+ = render partial: options[:node_partial_name],
14
+ collection: node.children.includes(:page, :children),
15
+ locals: { options: options },
16
+ as: 'node'
@@ -0,0 +1,8 @@
1
+ <%% cache menu do %>
2
+ <ul class="nav">
3
+ <%%= render partial: options[:node_partial_name],
4
+ collection: menu.children.includes(:page, :children),
5
+ locals: { options: options },
6
+ as: 'node' %>
7
+ </ul>
8
+ <%% end %>
@@ -0,0 +1,6 @@
1
+ - cache menu do
2
+ %ul.nav
3
+ = render partial: options[:node_partial_name],
4
+ collection: menu.children.includes(:page, :children),
5
+ locals: { options: options },
6
+ as: 'node' %>
@@ -0,0 +1,6 @@
1
+ - cache menu do
2
+ ul.nav
3
+ = render partial: options[:node_partial_name],
4
+ collection: menu.children.includes(:page, :children),
5
+ locals: { options: options },
6
+ as: 'node' %>
@@ -3,7 +3,7 @@ require 'rails'
3
3
  module Alchemy
4
4
  module Generators
5
5
  class ViewsGenerator < ::Rails::Generators::Base
6
- ALCHEMY_VIEWS = %w(breadcrumb language_links messages_mailer navigation)
6
+ ALCHEMY_VIEWS = %w(breadcrumb language_links messages_mailer)
7
7
 
8
8
  desc "Generates Alchemy views for #{ALCHEMY_VIEWS.to_sentence}."
9
9
 
@@ -31,5 +31,65 @@ namespace :alchemy do
31
31
  puts "Done."
32
32
  end
33
33
  end
34
+
35
+ namespace :page_trees do
36
+ desc "Converts the page tree into a menu."
37
+ task to_menus: [:environment] do
38
+ if Alchemy::Node.roots.exists?
39
+ abort "\n⨯ There are already menus present in your database. Aborting!"
40
+ end
41
+
42
+ def name_for_node(page)
43
+ if page.visible? && page.public? && !page.redirects_to_external?
44
+ nil
45
+ else
46
+ page.name
47
+ end
48
+ end
49
+
50
+ def page_for_node(page)
51
+ if page.visible? && page.public? && !page.redirects_to_external?
52
+ page
53
+ elsif Alchemy::Config.get(:redirect_to_public_child) && page.visible? && !page.public? && page.children.published.any?
54
+ page.children.published.first
55
+ end
56
+ end
57
+
58
+ def convert_to_nodes(children, node:)
59
+ children.each do |page|
60
+ has_children = page.children.any?
61
+ next unless page.visible || has_children
62
+
63
+ Alchemy::Deprecation.silence do
64
+ new_node = node.children.create!(
65
+ name: name_for_node(page),
66
+ page: page_for_node(page),
67
+ url: page.redirects_to_external? ? page.urlname : nil,
68
+ external: page.redirects_to_external? && Alchemy::Config.get(:open_external_links_in_new_tab),
69
+ language_id: page.language_id
70
+ )
71
+ print "."
72
+ if has_children
73
+ convert_to_nodes(page.children, node: new_node)
74
+ end
75
+ end
76
+ end
77
+ end
78
+
79
+ menu_count = Alchemy::Language.count
80
+ puts "\n- Converting #{menu_count} page #{'tree'.pluralize(menu_count)} into #{'menu'.pluralize(menu_count)}."
81
+ Alchemy::BaseRecord.transaction do
82
+ Alchemy::Language.all.each do |language|
83
+ locale = language.locale.presence || I18n.default_locale
84
+ menu_name = I18n.t('Main Navigation', scope: 'alchemy.menu_names', default: 'Main Navigation', locale: locale)
85
+ root_node = Alchemy::Node.create(language: language, name: menu_name)
86
+ language.pages.language_roots.each do |root_page|
87
+ convert_to_nodes(root_page.children, node: root_node)
88
+ end
89
+ end
90
+ end
91
+ puts "\n✓ Done."
92
+ end
93
+ end
34
94
  end
35
95
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: alchemy_cms
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.3.2
4
+ version: 4.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Thomas von Deyen
@@ -13,7 +13,7 @@ authors:
13
13
  autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
- date: 2019-11-08 00:00:00.000000000 Z
16
+ date: 2020-01-06 00:00:00.000000000 Z
17
17
  dependencies:
18
18
  - !ruby/object:Gem::Dependency
19
19
  name: active_model_serializers
@@ -33,16 +33,22 @@ dependencies:
33
33
  name: acts_as_list
34
34
  requirement: !ruby/object:Gem::Requirement
35
35
  requirements:
36
- - - "~>"
36
+ - - ">="
37
37
  - !ruby/object:Gem::Version
38
38
  version: '0.3'
39
+ - - "<"
40
+ - !ruby/object:Gem::Version
41
+ version: '2'
39
42
  type: :runtime
40
43
  prerelease: false
41
44
  version_requirements: !ruby/object:Gem::Requirement
42
45
  requirements:
43
- - - "~>"
46
+ - - ">="
44
47
  - !ruby/object:Gem::Version
45
48
  version: '0.3'
49
+ - - "<"
50
+ - !ruby/object:Gem::Version
51
+ version: '2'
46
52
  - !ruby/object:Gem::Dependency
47
53
  name: awesome_nested_set
48
54
  requirement: !ruby/object:Gem::Requirement
@@ -355,6 +361,26 @@ dependencies:
355
361
  - - "<"
356
362
  - !ruby/object:Gem::Version
357
363
  version: '6'
364
+ - !ruby/object:Gem::Dependency
365
+ name: sprockets
366
+ requirement: !ruby/object:Gem::Requirement
367
+ requirements:
368
+ - - ">="
369
+ - !ruby/object:Gem::Version
370
+ version: '3.0'
371
+ - - "<"
372
+ - !ruby/object:Gem::Version
373
+ version: '5'
374
+ type: :runtime
375
+ prerelease: false
376
+ version_requirements: !ruby/object:Gem::Requirement
377
+ requirements:
378
+ - - ">="
379
+ - !ruby/object:Gem::Version
380
+ version: '3.0'
381
+ - - "<"
382
+ - !ruby/object:Gem::Version
383
+ version: '5'
358
384
  - !ruby/object:Gem::Dependency
359
385
  name: turbolinks
360
386
  requirement: !ruby/object:Gem::Requirement
@@ -411,6 +437,20 @@ dependencies:
411
437
  - - "~>"
412
438
  - !ruby/object:Gem::Version
413
439
  version: '5.0'
440
+ - !ruby/object:Gem::Dependency
441
+ name: puma
442
+ requirement: !ruby/object:Gem::Requirement
443
+ requirements:
444
+ - - "~>"
445
+ - !ruby/object:Gem::Version
446
+ version: '4.0'
447
+ type: :development
448
+ prerelease: false
449
+ version_requirements: !ruby/object:Gem::Requirement
450
+ requirements:
451
+ - - "~>"
452
+ - !ruby/object:Gem::Version
453
+ version: '4.0'
414
454
  - !ruby/object:Gem::Dependency
415
455
  name: rails-controller-testing
416
456
  requirement: !ruby/object:Gem::Requirement
@@ -454,19 +494,19 @@ dependencies:
454
494
  - !ruby/object:Gem::Version
455
495
  version: 4.0.0.beta2
456
496
  - !ruby/object:Gem::Dependency
457
- name: selenium-webdriver
497
+ name: webdrivers
458
498
  requirement: !ruby/object:Gem::Requirement
459
499
  requirements:
460
500
  - - "~>"
461
501
  - !ruby/object:Gem::Version
462
- version: '3.8'
502
+ version: '4.0'
463
503
  type: :development
464
504
  prerelease: false
465
505
  version_requirements: !ruby/object:Gem::Requirement
466
506
  requirements:
467
507
  - - "~>"
468
508
  - !ruby/object:Gem::Version
469
- version: '3.8'
509
+ version: '4.0'
470
510
  - !ruby/object:Gem::Dependency
471
511
  name: shoulda-matchers
472
512
  requirement: !ruby/object:Gem::Requirement
@@ -493,12 +533,11 @@ files:
493
533
  - ".github/FUNDING.yml"
494
534
  - ".github/ISSUE_TEMPLATE/Bug_report.md"
495
535
  - ".github/ISSUE_TEMPLATE/Feature_request.md"
536
+ - ".github/workflows/ci.yml"
496
537
  - ".gitignore"
497
538
  - ".hound.yml"
498
539
  - ".localeapp/config.rb"
499
- - ".rspec"
500
540
  - ".rubocop.yml"
501
- - ".travis.yml"
502
541
  - ".yardopts"
503
542
  - CHANGELOG.md
504
543
  - CODE_OF_CONDUCT.md
@@ -509,6 +548,7 @@ files:
509
548
  - README.md
510
549
  - Rakefile
511
550
  - alchemy_cms.gemspec
551
+ - app/assets/config/alchemy_manifest.js
512
552
  - app/assets/images/alchemy/alchemy-logo.png
513
553
  - app/assets/images/alchemy/alchemy-logo.svg
514
554
  - app/assets/images/alchemy/favicon.ico
@@ -552,8 +592,10 @@ files:
552
592
  - app/assets/javascripts/alchemy/alchemy.trash_window.js.coffee
553
593
  - app/assets/javascripts/alchemy/alchemy.uploader.js.coffee
554
594
  - app/assets/javascripts/alchemy/menubar.js.coffee
595
+ - app/assets/javascripts/alchemy/page_select.js
555
596
  - app/assets/javascripts/alchemy/preview.js
556
597
  - app/assets/javascripts/alchemy/templates/index.js
598
+ - app/assets/javascripts/alchemy/templates/page.hbs
557
599
  - app/assets/javascripts/alchemy/templates/spinner.hbs
558
600
  - app/assets/javascripts/tinymce/plugins/alchemy_link/plugin.min.js
559
601
  - app/assets/stylesheets/alchemy/_defaults.scss
@@ -581,10 +623,13 @@ files:
581
623
  - app/assets/stylesheets/alchemy/icons.scss
582
624
  - app/assets/stylesheets/alchemy/image_library.scss
583
625
  - app/assets/stylesheets/alchemy/jquery-ui.scss
626
+ - app/assets/stylesheets/alchemy/labels.scss
584
627
  - app/assets/stylesheets/alchemy/lists.scss
585
628
  - app/assets/stylesheets/alchemy/menubar.scss
586
629
  - app/assets/stylesheets/alchemy/navigation.scss
630
+ - app/assets/stylesheets/alchemy/nodes.scss
587
631
  - app/assets/stylesheets/alchemy/notices.scss
632
+ - app/assets/stylesheets/alchemy/page-select.scss
588
633
  - app/assets/stylesheets/alchemy/pagination.scss
589
634
  - app/assets/stylesheets/alchemy/preview_window.scss
590
635
  - app/assets/stylesheets/alchemy/print.scss
@@ -623,6 +668,7 @@ files:
623
668
  - app/controllers/alchemy/admin/languages_controller.rb
624
669
  - app/controllers/alchemy/admin/layoutpages_controller.rb
625
670
  - app/controllers/alchemy/admin/legacy_page_urls_controller.rb
671
+ - app/controllers/alchemy/admin/nodes_controller.rb
626
672
  - app/controllers/alchemy/admin/pages_controller.rb
627
673
  - app/controllers/alchemy/admin/pictures_controller.rb
628
674
  - app/controllers/alchemy/admin/resources_controller.rb
@@ -678,6 +724,7 @@ files:
678
724
  - app/models/alchemy/essence_file.rb
679
725
  - app/models/alchemy/essence_html.rb
680
726
  - app/models/alchemy/essence_link.rb
727
+ - app/models/alchemy/essence_page.rb
681
728
  - app/models/alchemy/essence_picture.rb
682
729
  - app/models/alchemy/essence_picture_view.rb
683
730
  - app/models/alchemy/essence_richtext.rb
@@ -688,13 +735,13 @@ files:
688
735
  - app/models/alchemy/language/code.rb
689
736
  - app/models/alchemy/legacy_page_url.rb
690
737
  - app/models/alchemy/message.rb
738
+ - app/models/alchemy/node.rb
691
739
  - app/models/alchemy/page.rb
692
740
  - app/models/alchemy/page/fixed_attributes.rb
693
741
  - app/models/alchemy/page/page_elements.rb
694
742
  - app/models/alchemy/page/page_naming.rb
695
743
  - app/models/alchemy/page/page_natures.rb
696
744
  - app/models/alchemy/page/page_scopes.rb
697
- - app/models/alchemy/page/page_users.rb
698
745
  - app/models/alchemy/picture.rb
699
746
  - app/models/alchemy/picture/transformations.rb
700
747
  - app/models/alchemy/picture/url.rb
@@ -757,7 +804,6 @@ files:
757
804
  - app/views/alchemy/admin/elements/create.js.erb
758
805
  - app/views/alchemy/admin/elements/fold.js.erb
759
806
  - app/views/alchemy/admin/elements/index.html.erb
760
- - app/views/alchemy/admin/elements/list.html.erb
761
807
  - app/views/alchemy/admin/elements/new.html.erb
762
808
  - app/views/alchemy/admin/elements/order.js.erb
763
809
  - app/views/alchemy/admin/elements/publish.js.erb
@@ -786,6 +832,12 @@ files:
786
832
  - app/views/alchemy/admin/legacy_page_urls/create.js.erb
787
833
  - app/views/alchemy/admin/legacy_page_urls/destroy.js.erb
788
834
  - app/views/alchemy/admin/legacy_page_urls/update.js.erb
835
+ - app/views/alchemy/admin/nodes/_form.html.erb
836
+ - app/views/alchemy/admin/nodes/_node.html.erb
837
+ - app/views/alchemy/admin/nodes/edit.html.erb
838
+ - app/views/alchemy/admin/nodes/index.html.erb
839
+ - app/views/alchemy/admin/nodes/new.html.erb
840
+ - app/views/alchemy/admin/pages/_anchor_link.html.erb
789
841
  - app/views/alchemy/admin/pages/_create_language_form.html.erb
790
842
  - app/views/alchemy/admin/pages/_current_page.html.erb
791
843
  - app/views/alchemy/admin/pages/_external_link.html.erb
@@ -794,9 +846,9 @@ files:
794
846
  - app/views/alchemy/admin/pages/_internal_link.html.erb
795
847
  - app/views/alchemy/admin/pages/_legacy_urls.html.erb
796
848
  - app/views/alchemy/admin/pages/_locked_page.html.erb
849
+ - app/views/alchemy/admin/pages/_menu_fields.html.erb
797
850
  - app/views/alchemy/admin/pages/_new_page_form.html.erb
798
851
  - app/views/alchemy/admin/pages/_page.html.erb
799
- - app/views/alchemy/admin/pages/_page_for_links.html.erb
800
852
  - app/views/alchemy/admin/pages/_page_infos.html.erb
801
853
  - app/views/alchemy/admin/pages/_page_status.html.erb
802
854
  - app/views/alchemy/admin/pages/_publication_fields.html.erb
@@ -889,6 +941,8 @@ files:
889
941
  - app/views/alchemy/essences/_essence_html_view.html.erb
890
942
  - app/views/alchemy/essences/_essence_link_editor.html.erb
891
943
  - app/views/alchemy/essences/_essence_link_view.html.erb
944
+ - app/views/alchemy/essences/_essence_page_editor.html.erb
945
+ - app/views/alchemy/essences/_essence_page_view.html.erb
892
946
  - app/views/alchemy/essences/_essence_picture_editor.html.erb
893
947
  - app/views/alchemy/essences/_essence_picture_view.html.erb
894
948
  - app/views/alchemy/essences/_essence_richtext_editor.html.erb
@@ -938,6 +992,8 @@ files:
938
992
  - db/migrate/20180226123013_alchemy_four_point_zero.rb
939
993
  - db/migrate/20180227224537_migrate_tags_to_gutentag.rb
940
994
  - db/migrate/20180519204655_add_fixed_to_alchemy_elements.rb
995
+ - db/migrate/20191016073858_create_alchemy_essence_pages.rb
996
+ - db/migrate/20191029212236_create_alchemy_nodes.rb
941
997
  - lib/alchemy/ability_helper.rb
942
998
  - lib/alchemy/admin/locale.rb
943
999
  - lib/alchemy/auth_accessors.rb
@@ -981,9 +1037,11 @@ files:
981
1037
  - lib/alchemy/test_support/factories/dummy_user_factory.rb
982
1038
  - lib/alchemy/test_support/factories/element_factory.rb
983
1039
  - lib/alchemy/test_support/factories/essence_file_factory.rb
1040
+ - lib/alchemy/test_support/factories/essence_page_factory.rb
984
1041
  - lib/alchemy/test_support/factories/essence_picture_factory.rb
985
1042
  - lib/alchemy/test_support/factories/essence_text_factory.rb
986
1043
  - lib/alchemy/test_support/factories/language_factory.rb
1044
+ - lib/alchemy/test_support/factories/node_factory.rb
987
1045
  - lib/alchemy/test_support/factories/page_factory.rb
988
1046
  - lib/alchemy/test_support/factories/picture_factory.rb
989
1047
  - lib/alchemy/test_support/factories/site_factory.rb
@@ -1007,21 +1065,15 @@ files:
1007
1065
  - lib/kaminari/scoped_pagination_url_helper.rb
1008
1066
  - lib/rails/generators/alchemy/base.rb
1009
1067
  - lib/rails/generators/alchemy/elements/elements_generator.rb
1010
- - lib/rails/generators/alchemy/elements/templates/editor.html.erb
1011
- - lib/rails/generators/alchemy/elements/templates/editor.html.haml
1012
- - lib/rails/generators/alchemy/elements/templates/editor.html.slim
1013
1068
  - lib/rails/generators/alchemy/elements/templates/view.html.erb
1014
1069
  - lib/rails/generators/alchemy/elements/templates/view.html.haml
1015
1070
  - lib/rails/generators/alchemy/elements/templates/view.html.slim
1016
1071
  - lib/rails/generators/alchemy/essence/essence_generator.rb
1017
1072
  - lib/rails/generators/alchemy/essence/templates/editor.html.erb
1018
1073
  - lib/rails/generators/alchemy/essence/templates/view.html.erb
1019
- - lib/rails/generators/alchemy/install/files/_article_editor.html.erb
1020
- - lib/rails/generators/alchemy/install/files/_article_view.html.erb
1074
+ - lib/rails/generators/alchemy/install/files/_article.html.erb
1021
1075
  - lib/rails/generators/alchemy/install/files/_standard.html.erb
1022
- - lib/rails/generators/alchemy/install/files/alchemy.de.yml
1023
1076
  - lib/rails/generators/alchemy/install/files/alchemy.en.yml
1024
- - lib/rails/generators/alchemy/install/files/alchemy.es.yml
1025
1077
  - lib/rails/generators/alchemy/install/files/all.css
1026
1078
  - lib/rails/generators/alchemy/install/files/all.js
1027
1079
  - lib/rails/generators/alchemy/install/files/application.html.erb
@@ -1030,6 +1082,13 @@ files:
1030
1082
  - lib/rails/generators/alchemy/install/templates/dragonfly.rb.tt
1031
1083
  - lib/rails/generators/alchemy/install/templates/elements.yml.tt
1032
1084
  - lib/rails/generators/alchemy/install/templates/page_layouts.yml.tt
1085
+ - lib/rails/generators/alchemy/menus/menus_generator.rb
1086
+ - lib/rails/generators/alchemy/menus/templates/node.html.erb
1087
+ - lib/rails/generators/alchemy/menus/templates/node.html.haml
1088
+ - lib/rails/generators/alchemy/menus/templates/node.html.slim
1089
+ - lib/rails/generators/alchemy/menus/templates/wrapper.html.erb
1090
+ - lib/rails/generators/alchemy/menus/templates/wrapper.html.haml
1091
+ - lib/rails/generators/alchemy/menus/templates/wrapper.html.slim
1033
1092
  - lib/rails/generators/alchemy/module/module_generator.rb
1034
1093
  - lib/rails/generators/alchemy/module/templates/ability.rb.tt
1035
1094
  - lib/rails/generators/alchemy/module/templates/controller.rb.tt
@@ -1129,7 +1188,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
1129
1188
  version: '0'
1130
1189
  requirements:
1131
1190
  - ImageMagick (libmagick), v6.6 or greater.
1132
- rubygems_version: 3.0.3
1191
+ rubygems_version: 3.1.2
1133
1192
  signing_key:
1134
1193
  specification_version: 4
1135
1194
  summary: A powerful, userfriendly and flexible CMS for Rails 5