alchemy_cms 8.0.0.a → 8.0.0.c

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 (216) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -0
  3. data/app/assets/builds/alchemy/admin/page-select.css +1 -1
  4. data/app/assets/builds/alchemy/admin.css +1 -1
  5. data/app/assets/builds/alchemy/dark-theme.css +1 -0
  6. data/app/assets/builds/alchemy/light-theme.css +1 -0
  7. data/app/assets/builds/alchemy/theme.css +1 -0
  8. data/app/assets/builds/alchemy/welcome.css +1 -1
  9. data/app/assets/builds/tinymce/skins/content/alchemy/content.min.css +1 -1
  10. data/app/assets/builds/tinymce/skins/content/alchemy-dark/content.min.css +1 -0
  11. data/app/assets/builds/tinymce/skins/ui/alchemy/skin.min.css +1 -1
  12. data/app/assets/builds/tinymce/skins/ui/alchemy-dark/content.min.css +1 -0
  13. data/app/assets/builds/tinymce/skins/ui/alchemy-dark/skin.min.css +1 -0
  14. data/app/assets/images/alchemy/element_icons/layout-bottom-2-line.svg +1 -0
  15. data/app/assets/images/alchemy/icons-sprite.svg +1 -1
  16. data/app/components/alchemy/admin/element_select.rb +39 -0
  17. data/app/components/alchemy/admin/link_dialog/tabs.rb +1 -1
  18. data/app/components/alchemy/admin/locale_select.rb +38 -0
  19. data/app/components/alchemy/ingredients/datetime_view.rb +4 -2
  20. data/app/controllers/alchemy/admin/attachments_controller.rb +2 -0
  21. data/app/controllers/alchemy/admin/elements_controller.rb +2 -0
  22. data/app/controllers/alchemy/admin/pages_controller.rb +3 -1
  23. data/app/controllers/alchemy/admin/pictures_controller.rb +26 -34
  24. data/app/controllers/alchemy/admin/resources_controller.rb +1 -1
  25. data/app/controllers/alchemy/pages_controller.rb +19 -2
  26. data/app/controllers/concerns/alchemy/admin/resource_filter.rb +1 -0
  27. data/app/decorators/alchemy/ingredient_editor.rb +9 -1
  28. data/app/helpers/alchemy/admin/attachments_helper.rb +5 -5
  29. data/app/helpers/alchemy/admin/base_helper.rb +0 -7
  30. data/app/helpers/alchemy/admin/form_helper.rb +2 -1
  31. data/app/helpers/alchemy/pages_helper.rb +1 -1
  32. data/app/javascript/alchemy_admin/components/auto_submit.js +20 -0
  33. data/app/javascript/alchemy_admin/components/datepicker.js +8 -5
  34. data/app/javascript/alchemy_admin/components/element_editor/delete_element_button.js +3 -2
  35. data/app/javascript/alchemy_admin/components/element_editor.js +25 -15
  36. data/app/javascript/alchemy_admin/components/element_select.js +43 -0
  37. data/app/javascript/alchemy_admin/components/index.js +5 -0
  38. data/app/javascript/alchemy_admin/components/link_buttons.js +6 -2
  39. data/app/javascript/alchemy_admin/components/remote_select.js +5 -1
  40. data/app/javascript/alchemy_admin/components/tinymce.js +93 -16
  41. data/app/javascript/alchemy_admin/dialog.js +1 -1
  42. data/app/javascript/alchemy_admin/file_editors.js +1 -1
  43. data/app/javascript/alchemy_admin/image_loader.js +4 -2
  44. data/app/javascript/alchemy_admin/picture_editors.js +7 -4
  45. data/app/javascript/alchemy_admin/picture_selector.js +4 -4
  46. data/app/jobs/alchemy/delete_picture_job.rb +12 -0
  47. data/app/models/alchemy/attachment.rb +2 -9
  48. data/app/models/alchemy/element.rb +1 -0
  49. data/app/models/alchemy/element_definition.rb +31 -0
  50. data/app/models/alchemy/ingredient.rb +1 -1
  51. data/app/models/alchemy/ingredients/boolean.rb +2 -1
  52. data/app/models/alchemy/language.rb +2 -7
  53. data/app/models/alchemy/page/page_naming.rb +4 -11
  54. data/app/models/alchemy/page/page_natures.rb +16 -11
  55. data/app/models/alchemy/page/publisher.rb +1 -1
  56. data/app/models/alchemy/page.rb +1 -6
  57. data/app/models/alchemy/page_definition.rb +1 -1
  58. data/app/models/alchemy/picture.rb +6 -17
  59. data/app/models/alchemy/resource.rb +15 -2
  60. data/app/models/alchemy/site/layout.rb +1 -0
  61. data/app/models/alchemy/site.rb +1 -6
  62. data/app/models/alchemy/storage_adapter/dragonfly/picture_url.rb +7 -2
  63. data/app/models/alchemy/storage_adapter/dragonfly.rb +24 -2
  64. data/app/models/concerns/alchemy/relatable_resource.rb +28 -0
  65. data/app/stylesheets/alchemy/_custom-properties.scss +162 -0
  66. data/app/stylesheets/alchemy/_mixins.scss +12 -24
  67. data/app/stylesheets/alchemy/_themes.scss +540 -0
  68. data/app/stylesheets/alchemy/admin/archive.scss +28 -8
  69. data/app/stylesheets/alchemy/admin/attachments.scss +10 -33
  70. data/app/stylesheets/alchemy/admin/base.scss +4 -1
  71. data/app/stylesheets/alchemy/admin/buttons.scss +7 -32
  72. data/app/stylesheets/alchemy/admin/dashboard.scss +13 -0
  73. data/app/stylesheets/alchemy/admin/dialogs.scss +17 -7
  74. data/app/stylesheets/alchemy/admin/element-select.scss +11 -0
  75. data/app/stylesheets/alchemy/admin/elements.scss +95 -34
  76. data/app/stylesheets/alchemy/admin/filters.scss +8 -9
  77. data/app/stylesheets/alchemy/admin/flatpickr.scss +12 -27
  78. data/app/stylesheets/alchemy/admin/form_fields.scss +0 -15
  79. data/app/stylesheets/alchemy/admin/forms.scss +3 -8
  80. data/app/stylesheets/alchemy/admin/frame.scss +5 -7
  81. data/app/stylesheets/alchemy/admin/icons.scss +0 -9
  82. data/app/stylesheets/alchemy/admin/image_library.scss +13 -55
  83. data/app/stylesheets/alchemy/admin/navigation.scss +1 -11
  84. data/app/stylesheets/alchemy/admin/node-select.scss +1 -10
  85. data/app/stylesheets/alchemy/admin/nodes.scss +6 -2
  86. data/app/stylesheets/alchemy/admin/notices.scss +5 -4
  87. data/app/stylesheets/alchemy/admin/page-select.scss +16 -0
  88. data/app/stylesheets/alchemy/admin/pagination.scss +1 -8
  89. data/app/stylesheets/alchemy/admin/preview_window.scss +12 -1
  90. data/app/stylesheets/alchemy/admin/resource_info.scss +106 -3
  91. data/app/stylesheets/alchemy/admin/search.scss +1 -1
  92. data/app/stylesheets/alchemy/admin/selects.scss +58 -31
  93. data/app/stylesheets/alchemy/admin/shoelace.scss +32 -62
  94. data/app/stylesheets/alchemy/admin/sitemap.scss +7 -18
  95. data/app/stylesheets/alchemy/admin/tables.scss +3 -3
  96. data/app/stylesheets/alchemy/admin/tags.scss +18 -35
  97. data/app/stylesheets/alchemy/admin/toolbar.scss +0 -6
  98. data/app/stylesheets/alchemy/admin/typography.scss +2 -5
  99. data/app/stylesheets/alchemy/admin.scss +1 -1
  100. data/app/stylesheets/alchemy/dark-theme.scss +5 -0
  101. data/app/stylesheets/alchemy/light-theme.scss +6 -0
  102. data/app/stylesheets/alchemy/theme.scss +13 -0
  103. data/app/stylesheets/tinymce/skins/content/alchemy/content.scss +8 -8
  104. data/app/stylesheets/tinymce/skins/content/alchemy-dark/content.scss +70 -0
  105. data/app/stylesheets/tinymce/skins/ui/alchemy/skin.scss +28 -43
  106. data/app/stylesheets/tinymce/skins/ui/alchemy-dark/content.scss +1 -0
  107. data/app/stylesheets/tinymce/skins/ui/alchemy-dark/skin.scss +3784 -0
  108. data/app/views/alchemy/admin/attachments/_files_list.html.erb +20 -10
  109. data/app/views/alchemy/admin/attachments/assign.js.erb +4 -3
  110. data/app/views/alchemy/admin/attachments/show.html.erb +55 -43
  111. data/app/views/alchemy/admin/crop.html.erb +1 -1
  112. data/app/views/alchemy/admin/dashboard/index.html.erb +1 -1
  113. data/app/views/alchemy/admin/dashboard/info.html.erb +36 -6
  114. data/app/views/alchemy/admin/elements/_form.html.erb +9 -9
  115. data/app/views/alchemy/admin/elements/_header.html.erb +12 -10
  116. data/app/views/alchemy/admin/ingredients/_picture_fields.html.erb +1 -1
  117. data/app/views/alchemy/admin/nodes/_form.html.erb +5 -1
  118. data/app/views/alchemy/admin/pages/info.html.erb +1 -1
  119. data/app/views/alchemy/admin/partials/_search_form.html.erb +1 -0
  120. data/app/views/alchemy/admin/pictures/_archive.html.erb +13 -23
  121. data/app/views/alchemy/admin/pictures/_archive_overlay.html.erb +1 -6
  122. data/app/views/alchemy/admin/pictures/_form.html.erb +10 -5
  123. data/app/views/alchemy/admin/pictures/_infos.html.erb +21 -52
  124. data/app/views/alchemy/admin/pictures/_library_sidebar.html.erb +7 -0
  125. data/app/views/alchemy/admin/pictures/_picture.html.erb +15 -16
  126. data/app/views/alchemy/admin/pictures/_picture_to_assign.html.erb +20 -16
  127. data/app/views/alchemy/admin/pictures/_sorting_select.html.erb +13 -0
  128. data/app/views/alchemy/admin/pictures/_tag_list.html.erb +1 -1
  129. data/app/views/alchemy/admin/pictures/edit_multiple.html.erb +1 -6
  130. data/app/views/alchemy/admin/pictures/index.html.erb +3 -12
  131. data/app/views/alchemy/admin/pictures/show.html.erb +17 -14
  132. data/app/views/alchemy/admin/pictures/update.turbo_stream.erb +1 -1
  133. data/app/views/alchemy/admin/resources/_filter_bar.html.erb +5 -15
  134. data/app/views/alchemy/admin/resources/_resource_usage_info.html.erb +36 -0
  135. data/app/views/alchemy/admin/styleguide/index.html.erb +118 -66
  136. data/app/views/alchemy/admin/uploader/_button.html.erb +1 -1
  137. data/app/views/alchemy/base/error_notice.html.erb +1 -1
  138. data/app/views/alchemy/ingredients/_page_editor.html.erb +0 -1
  139. data/app/views/alchemy/ingredients/_richtext_editor.html.erb +0 -1
  140. data/app/views/alchemy/ingredients/_select_editor.html.erb +1 -2
  141. data/app/views/layouts/alchemy/admin.html.erb +25 -23
  142. data/config/locales/alchemy.en.yml +26 -8
  143. data/db/migrate/20250905140323_add_created_at_index_to_pictures_and_attachments.rb +14 -0
  144. data/lib/alchemy/configuration/base_option.rb +18 -5
  145. data/lib/alchemy/configuration/boolean_option.rb +2 -5
  146. data/lib/alchemy/configuration/collection_option.rb +69 -0
  147. data/lib/alchemy/configuration/configuration_option.rb +35 -0
  148. data/lib/alchemy/configuration/pathname_option.rb +12 -0
  149. data/lib/alchemy/configuration.rb +44 -6
  150. data/lib/alchemy/configurations/format_matchers.rb +1 -1
  151. data/lib/alchemy/configurations/importmap.rb +11 -0
  152. data/lib/alchemy/configurations/mailer.rb +2 -2
  153. data/lib/alchemy/configurations/main.rb +148 -3
  154. data/lib/alchemy/configurations/page_cache.rb +19 -0
  155. data/lib/alchemy/configurations/uploader.rb +2 -2
  156. data/lib/alchemy/deprecation.rb +1 -1
  157. data/lib/alchemy/engine.rb +43 -21
  158. data/lib/alchemy/install/tasks.rb +0 -12
  159. data/lib/alchemy/name_conversions.rb +6 -0
  160. data/lib/alchemy/tasks/tidy.rb +18 -0
  161. data/lib/alchemy/test_support/config_stubbing.rb +13 -4
  162. data/lib/alchemy/test_support/factories/language_factory.rb +8 -4
  163. data/lib/alchemy/test_support/factories/page_factory.rb +1 -0
  164. data/lib/alchemy/test_support/factories/picture_factory.rb +1 -0
  165. data/lib/alchemy/test_support/relatable_resource_examples.rb +58 -0
  166. data/lib/alchemy/tinymce.rb +0 -1
  167. data/lib/alchemy/version.rb +1 -1
  168. data/lib/alchemy.rb +18 -171
  169. data/lib/generators/alchemy/install/install_generator.rb +21 -10
  170. data/lib/generators/alchemy/install/templates/alchemy.rb.tt +88 -13
  171. data/lib/tasks/alchemy/assets.rake +1 -1
  172. data/lib/tasks/alchemy/tidy.rake +6 -0
  173. data/lib/tasks/alchemy/usage.rake +2 -0
  174. data/vendor/assets/stylesheets/tinymce/skins/content/dark/content.min.css +1 -0
  175. data/vendor/assets/stylesheets/tinymce/skins/content/default/content.min.css +1 -0
  176. data/vendor/assets/stylesheets/tinymce/skins/ui/oxide/skin.min.css +1 -0
  177. data/vendor/assets/stylesheets/tinymce/skins/ui/oxide-dark/content.min.css +1 -0
  178. data/vendor/assets/stylesheets/tinymce/skins/ui/oxide-dark/skin.min.css +1 -0
  179. data/vendor/javascript/clipboard.min.js +1 -1
  180. data/vendor/javascript/cropperjs.min.js +1 -1
  181. data/vendor/javascript/handlebars.min.js +3 -3
  182. data/vendor/javascript/jquery.min.js +1 -1
  183. data/vendor/javascript/select2.min.js +3 -3
  184. data/vendor/javascript/shoelace.min.js +92 -76
  185. data/vendor/javascript/sortable.min.js +2 -2
  186. data/vendor/javascript/tinymce.min.js +1 -1
  187. data/vendor/javascript/ungap-custom-elements.min.js +2 -2
  188. metadata +51 -36
  189. data/CHANGELOG.md +0 -2100
  190. data/CODE_OF_CONDUCT.md +0 -13
  191. data/CONTRIBUTING.md +0 -73
  192. data/Gemfile +0 -78
  193. data/Rakefile +0 -102
  194. data/SECURITY.md +0 -13
  195. data/alchemy_cms.gemspec +0 -97
  196. data/app/assets/builds/alchemy/custom-properties.css +0 -1
  197. data/app/helpers/alchemy/admin/elements_helper.rb +0 -25
  198. data/app/stylesheets/alchemy/custom-properties.css +0 -244
  199. data/bin/importmap +0 -4
  200. data/bin/rails +0 -9
  201. data/bin/rspec +0 -3
  202. data/bin/setup +0 -30
  203. data/bin/start +0 -17
  204. data/bun.lockb +0 -0
  205. data/bundles/remixicon.mjs +0 -153
  206. data/bundles/shoelace.js +0 -12
  207. data/bundles/tinymce.js +0 -22
  208. data/eslint.config.js +0 -18
  209. data/lib/alchemy/configuration/class_set_option.rb +0 -46
  210. data/lib/alchemy/configuration/integer_list_option.rb +0 -13
  211. data/lib/alchemy/configuration/list_option.rb +0 -22
  212. data/lib/alchemy/configuration/string_list_option.rb +0 -13
  213. data/lib/alchemy/upgrader/.keep +0 -0
  214. data/lib/alchemy/upgrader/tasks/.keep +0 -0
  215. data/rollup.config.mjs +0 -108
  216. data/vitest.config.js +0 -21
@@ -73,6 +73,24 @@ module Alchemy
73
73
  log "Deleted #{count} duplicate legacy URLs"
74
74
  end
75
75
 
76
+ def remove_legacy_essence_tables
77
+ puts "\n## Removing legacy essence tables"
78
+ matching_tables = ActiveRecord::Base.connection.tables.select do |table|
79
+ table.start_with?("alchemy_essence_")
80
+ end
81
+ if matching_tables.length.zero?
82
+ log "No legacy essence tables found", :skip
83
+ nil
84
+ else
85
+ matching_tables.each do |table|
86
+ ActiveRecord::Base.connection.drop_table(table, if_exists: true)
87
+ print "."
88
+ end
89
+ puts "\n"
90
+ log "Deleted #{matching_tables.length} legacy essence tables"
91
+ end
92
+ end
93
+
76
94
  private
77
95
 
78
96
  def destroy_orphaned_records(records, class_name)
@@ -13,11 +13,20 @@ module Alchemy
13
13
  module ConfigStubbing
14
14
  # Stub a key from the Alchemy config
15
15
  #
16
- # @param key [Symbol] The configuration key you want to stub
17
- # @param value [Object] The value you want to return instead of the original one
16
+ # @param hash [Hash] The keys you would like to stub along with their values
18
17
  #
19
- def stub_alchemy_config(key, value)
20
- allow(Alchemy.config).to receive(key).and_return(value)
18
+ def stub_alchemy_config(hash)
19
+ stub_config(Alchemy.config, hash)
20
+ end
21
+
22
+ def stub_config(config, hash)
23
+ hash.each do |key, value|
24
+ if value.is_a?(Hash)
25
+ stub_config(config.send(key), value)
26
+ else
27
+ allow(config).to receive(key).and_return(value)
28
+ end
29
+ end
21
30
  end
22
31
  end
23
32
  end
@@ -3,7 +3,8 @@
3
3
  FactoryBot.define do
4
4
  factory :alchemy_language, class: "Alchemy::Language" do
5
5
  name { "Your Language" }
6
- code { ::I18n.available_locales.first.to_s }
6
+ language_code { "en" }
7
+ locale { ::I18n.default_locale }
7
8
  default { true }
8
9
  frontpage_name { "Intro" }
9
10
  page_layout { Alchemy.config.default_language.page_layout }
@@ -14,20 +15,23 @@ FactoryBot.define do
14
15
 
15
16
  trait :klingon do
16
17
  name { "Klingon" }
17
- code { "kl" }
18
+ language_code { "kl" }
19
+ locale { :kl }
18
20
  frontpage_name { "Tuq" }
19
21
  default { false }
20
22
  end
21
23
 
22
24
  trait :english do
23
25
  name { "English" }
24
- code { "en" }
26
+ language_code { "en" }
27
+ locale { :en }
25
28
  default { false }
26
29
  end
27
30
 
28
31
  trait :german do
29
32
  name { "Deutsch" }
30
- code { "de" }
33
+ language_code { "de" }
34
+ locale { :de }
31
35
  default { false }
32
36
  end
33
37
  end
@@ -32,6 +32,7 @@ FactoryBot.define do
32
32
  public_on { Time.current }
33
33
  public_until { nil }
34
34
  end
35
+ published_at { Time.current }
35
36
  after(:build) do |page, evaluator|
36
37
  page.build_public_version(
37
38
  public_on: evaluator.public_on,
@@ -29,6 +29,7 @@ FactoryBot.define do
29
29
  when :dragonfly
30
30
  picture.image_file = acc.image_file
31
31
  picture.image_file_size = acc.image_file.size
32
+ picture.image_file_format = File.extname(acc.image_file_name).delete(".").downcase
32
33
  end
33
34
  end
34
35
  end
@@ -0,0 +1,58 @@
1
+ RSpec.shared_examples_for "a relatable resource" do |args|
2
+ it { is_expected.to have_many(:related_ingredients) }
3
+ it { is_expected.to have_many(:elements).through(:related_ingredients) }
4
+ it { is_expected.to have_many(:pages).through(:elements) }
5
+
6
+ describe ".deletable" do
7
+ subject { described_class.deletable }
8
+
9
+ let!(:assigned_resource) { create(:"alchemy_#{args[:resource_name]}") }
10
+ let!(:unassigned_resource) { create(:"alchemy_#{args[:resource_name]}") }
11
+ let!(:ingredient1) { create(:"alchemy_ingredient_#{args[:ingredient_type]}", related_object: assigned_resource) }
12
+ let!(:ingredient2) { create(:"alchemy_ingredient_#{args[:ingredient_type]}", related_object: nil) }
13
+
14
+ it "should return all records that are not assigned to an ingredient" do
15
+ is_expected.to eq [unassigned_resource]
16
+ end
17
+ end
18
+
19
+ describe "#related_ingredients" do
20
+ subject { resource.related_ingredients }
21
+
22
+ context "with other related resources with same id" do
23
+ let!(:resource) { create(:"alchemy_#{args[:resource_name]}") }
24
+ let!(:ingredient1) { create(:"alchemy_ingredient_#{args[:ingredient_type]}", related_object: resource) }
25
+ let!(:ingredient2) { create(:"alchemy_ingredient_#{args[:ingredient_type]}", related_object_type: "Event", related_object_id: resource.id) }
26
+
27
+ it "are not included" do
28
+ is_expected.to eq [ingredient1]
29
+ end
30
+ end
31
+
32
+ context "with other related resources with same type" do
33
+ let!(:resource) { create(:"alchemy_#{args[:resource_name]}") }
34
+ let!(:ingredient1) { create(:"alchemy_ingredient_#{args[:ingredient_type]}", related_object: resource) }
35
+ let!(:ingredient2) { create(:"alchemy_ingredient_#{args[:ingredient_type]}", related_object_type: described_class) }
36
+
37
+ it "are not included" do
38
+ is_expected.to eq [ingredient1]
39
+ end
40
+ end
41
+ end
42
+
43
+ describe "#deletable?" do
44
+ let(:resource) { create(:"alchemy_#{args[:resource_name]}") }
45
+
46
+ subject { resource.deletable? }
47
+
48
+ context "if related to ingredient" do
49
+ let!(:ingredient) { create(:"alchemy_ingredient_#{args[:ingredient_type]}", related_object: resource) }
50
+
51
+ it { is_expected.to be(false) }
52
+ end
53
+
54
+ context "if not related to ingredient" do
55
+ it { is_expected.to be(true) }
56
+ end
57
+ end
58
+ end
@@ -18,7 +18,6 @@ module Alchemy
18
18
  @@plugins = DEFAULT_PLUGINS
19
19
  @@init = {
20
20
  skin: "alchemy",
21
- content_css: "/assets/tinymce/skins/content/alchemy/content.min.css",
22
21
  icons: "remixicons",
23
22
  width: "auto",
24
23
  resize: true,
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Alchemy
4
- VERSION = "8.0.0.a"
4
+ VERSION = "8.0.0.c"
5
5
 
6
6
  def self.version
7
7
  VERSION
data/lib/alchemy.rb CHANGED
@@ -15,6 +15,8 @@ module Alchemy
15
15
  singleton_class.attr_accessor :importmap
16
16
  self.importmap = Importmap::Map.new
17
17
 
18
+ mattr_accessor :storage_adapter
19
+
18
20
  class << self
19
21
  def config
20
22
  @_config ||= Alchemy::Configurations::Main.new
@@ -33,181 +35,26 @@ module Alchemy
33
35
  end
34
36
  deprecate enable_searchable: enable_searchable_deprecation_msg, deprecator: Alchemy::Deprecation
35
37
 
36
- # Define page preview sources
37
- #
38
- # A preview source is a Ruby class returning an URL
39
- # that is used as source for the preview frame in the
40
- # admin UI.
41
- #
42
- # == Example
43
- #
44
- # # lib/acme/preview_source.rb
45
- # class Acme::PreviewSource < Alchemy::Admin::PreviewUrl
46
- # def url_for(page)
47
- # if page.site.name == "Next"
48
- # "https://user:#{ENV['PREVIEW_HTTP_PASS']}@next.acme.com"
49
- # else
50
- # "https://www.acme.com"
51
- # end
52
- # end
53
- # end
54
- #
55
- # # config/initializers/alchemy.rb
56
- # require "acme/preview_source"
57
- # Alchemy.preview_sources << Acme::PreviewSource
58
- #
59
- # # config/locales/de.yml
60
- # de:
61
- # activemodel:
62
- # models:
63
- # acme/preview_source: Acme Vorschau
64
- #
65
- def preview_sources
66
- @_preview_sources ||= Set.new << Alchemy::Admin::PreviewUrl
67
- end
38
+ delegate :preview_sources, to: :config
39
+ delegate :preview_sources=, to: :config
40
+ deprecate preview_sources: "Use `Alchemy.config.preview_sources` instead.", deprecator: Alchemy::Deprecation
41
+ deprecate :preview_sources= => "Use `Alchemy.config.preview_sources=` instead.", :deprecator => Alchemy::Deprecation
68
42
 
69
- def preview_sources=(sources)
70
- @_preview_sources = Array(sources)
71
- end
43
+ delegate :admin_js_imports, to: :config
44
+ delegate :admin_js_imports=, to: :config
45
+ deprecate admin_js_imports: "Use `Alchemy.config.admin_js_imports` instead", deprecator: Alchemy::Deprecation
46
+ deprecate :admin_js_imports= => "Use `Alchemy.config.admin_js_imports=` instead", :deprecator => Alchemy::Deprecation
72
47
 
73
- # Additional JS modules to be imported in the Alchemy admin UI
74
- #
75
- # Be sure to also pin the modules with +Alchemy.importmap+.
76
- #
77
- # == Example
78
- #
79
- # Alchemy.importmap.pin "flatpickr/de",
80
- # to: "https://ga.jspm.io/npm:flatpickr@4.6.13/dist/l10n/de.js"
81
- #
82
- # Alchemy.admin_js_imports << "flatpickr/de"
83
- #
84
- def admin_js_imports
85
- @_admin_js_imports ||= Set.new
86
- end
48
+ delegate :admin_importmaps, to: :config
49
+ deprecate admin_importmaps: "Use Alchemy.config.admin_importmaps instead", deprecator: Alchemy::Deprecation
87
50
 
88
- def admin_js_imports=(sources)
89
- @_admin_js_imports = Set[sources]
90
- end
91
-
92
- # Additional importmaps to be included in the Alchemy admin UI
93
- #
94
- # Be sure to also pin modules with +Alchemy.importmap+.
95
- #
96
- # == Example
97
- #
98
- # # config/alchemy/importmap.rb
99
- # Alchemy.importmap.pin "alchemy_solidus", to: "alchemy_solidus.js", preload: true
100
- # Alchemy.importmap.pin_all_from Alchemy::Solidus::Engine.root.join("app/javascript/alchemy_solidus"),
101
- # under: "alchemy_solidus",
102
- # preload: true
103
- #
104
- # # lib/alchemy/solidus/engine.rb
105
- # initializer "alchemy_solidus.assets", before: "alchemy.importmap" do |app|
106
- # Alchemy.admin_importmaps.add({
107
- # importmap_path: root.join("config/importmap.rb"),
108
- # source_paths: [
109
- # root.join("app/javascript")
110
- # ],
111
- # name: "alchemy_solidus"
112
- # })
113
- # app.config.assets.precompile << "alchemy_solidus/manifest.js"
114
- # end
115
- #
116
- # @return [Set<Hash>]
117
- def admin_importmaps
118
- @_admin_importmaps ||= Set.new([{
119
- importmap_path: Engine.root.join("config/importmap.rb"),
120
- source_paths: [
121
- Engine.root.join("app/javascript"),
122
- Engine.root.join("vendor/javascript")
123
- ],
124
- name: "alchemy_admin"
125
- }])
126
- end
51
+ delegate :admin_stylesheets, to: :config
52
+ deprecate admin_stylesheets: "Use Alchemy.config.admin_stylesheets instead", deprecator: Alchemy::Deprecation
127
53
 
128
- # Additional stylesheets to be included in the Alchemy admin UI
129
- #
130
- # == Example
131
- #
132
- # # lib/alchemy/devise/engine.rb
133
- # initializer "alchemy.devise.stylesheets", before: "alchemy.admin_stylesheets" do
134
- # Alchemy.admin_stylesheets << "alchemy/devise/admin.css"
135
- # end
136
- #
137
- # @return [Set<String>]
138
- def admin_stylesheets
139
- @_admin_stylesheets ||= Set.new(["alchemy/admin/custom.css"])
140
- end
141
-
142
- # Define page publish targets
143
- #
144
- # A publish target is a ActiveJob that gets performed
145
- # whenever a user clicks the publish page button.
146
- #
147
- # Use this to trigger deployment hooks of external
148
- # services in an asychronous way.
149
- #
150
- # == Example
151
- #
152
- # # app/jobs/publish_job.rb
153
- # class PublishJob < ApplicationJob
154
- # def perform(page)
155
- # RestClient.post(ENV['BUILD_HOOK_URL'])
156
- # end
157
- # end
158
- #
159
- # # config/initializers/alchemy.rb
160
- # Alchemy.publish_targets << PublishJob
161
- #
162
- def publish_targets
163
- @_publish_targets ||= Set.new
164
- end
165
-
166
- # Configure tabs in the link dialog
167
- #
168
- # With this configuration that tabs in the link dialog can be extended
169
- # without overwriting or defacing the Admin Interface.
170
- #
171
- # == Example
172
- #
173
- # # components/acme/link_tab.rb
174
- # module Acme
175
- # class LinkTab < ::Alchemy::Admin::LinkDialog::BaseTab
176
- # def title
177
- # "Awesome Tab Title"
178
- # end
179
- #
180
- # def name
181
- # :unique_name
182
- # end
183
- #
184
- # def fields
185
- # [ title_input, target_select ]
186
- # end
187
- # end
188
- # end
189
- #
190
- # # config/initializers/alchemy.rb
191
- # Alchemy.link_dialog_tabs << Acme::LinkTab
192
- #
193
- def link_dialog_tabs
194
- @_link_dialog_tabs ||= Set.new([
195
- Alchemy::Admin::LinkDialog::InternalTab,
196
- Alchemy::Admin::LinkDialog::AnchorTab,
197
- Alchemy::Admin::LinkDialog::ExternalTab,
198
- Alchemy::Admin::LinkDialog::FileTab
199
- ])
200
- end
201
- end
54
+ delegate :publish_targets, to: :config
55
+ deprecate publish_targets: "Use Alchemy.config.publish_targets instead", deprecator: Alchemy::Deprecation
202
56
 
203
- # The storage adapter for Pictures and Attachments
204
- #
205
- # Chose between 'active_storage' (default) or 'dragonfly' (legacy)
206
- #
207
- # Can be set via 'ALCHEMY_STORAGE_ADAPTER' env var.
208
- def self.storage_adapter
209
- @_storage_adapter ||= Alchemy::StorageAdapter.new(
210
- ENV.fetch("ALCHEMY_STORAGE_ADAPTER", Alchemy.config.storage_adapter)
211
- )
57
+ delegate :link_dialog_tabs, to: :config
58
+ deprecate link_dialog_tabs: "Use Alchemy.config.link_dialog_tabs instead", deprecator: Alchemy::Deprecation
212
59
  end
213
60
  end
@@ -60,7 +60,17 @@ module Alchemy
60
60
  end
61
61
 
62
62
  def copy_config_rb
63
- @default_config = Alchemy::Configurations::Main.new
63
+ @default_language = get_primary_language(
64
+ code: options[:default_language_code],
65
+ name: options[:default_language_name],
66
+ auto_accept: options[:auto_accept]
67
+ )
68
+ @default_config = Alchemy::Configurations::Main.new(
69
+ default_language: {
70
+ name: @default_language[:name],
71
+ code: @default_language[:code]
72
+ }
73
+ )
64
74
  template "#{__dir__}/templates/alchemy.rb.tt", app_config_path.join("initializers", "alchemy.rb")
65
75
  end
66
76
 
@@ -89,15 +99,6 @@ module Alchemy
89
99
  rake "gutentag:install:migrations"
90
100
  end
91
101
 
92
- def set_primary_language
93
- header
94
- install_tasks.set_primary_language(
95
- code: options[:default_language_code],
96
- name: options[:default_language_name],
97
- auto_accept: options[:auto_accept]
98
- )
99
- end
100
-
101
102
  def setup_database
102
103
  rake("db:create", abort_on_failure: true) unless options[:skip_db_create]
103
104
  # We can't invoke this rake task, because Rails will use wrong engine names otherwise
@@ -118,6 +119,16 @@ module Alchemy
118
119
 
119
120
  private
120
121
 
122
+ def get_primary_language(code: "en", name: "English", auto_accept: false)
123
+ unless options[:auto_accept]
124
+ code = ask("- What is the language code of your site's primary language?", default: code)
125
+ end
126
+ unless options[:auto_accept]
127
+ name = ask("- What is the name of your site's primary language?", default: name)
128
+ end
129
+ {code:, name:}
130
+ end
131
+
121
132
  def header
122
133
  return if options[:auto_accept]
123
134
 
@@ -97,12 +97,12 @@ Alchemy.configure do |config|
97
97
  # end
98
98
 
99
99
  # This is the default language when seeding.
100
- # config.default_language.tap do |default_language|
101
- # default_language.code = <%= @default_config.default_language.code.inspect %>
102
- # default_language.name = <%= @default_config.default_language.name.inspect %>
103
- # default_language.page_layout = <%= @default_config.default_language.page_layout.inspect %>
104
- # default_language.frontpage_name = <%= @default_config.default_language.frontpage_name.inspect %>
105
- # end
100
+ config.default_language.tap do |default_language|
101
+ default_language.code = <%= @default_config.default_language.code.inspect %>
102
+ default_language.name = <%= @default_config.default_language.name.inspect %>
103
+ # default_language.page_layout = <%= @default_config.default_language.page_layout.inspect %>
104
+ # default_language.frontpage_name = <%= @default_config.default_language.frontpage_name.inspect %>
105
+ end
106
106
 
107
107
  # === Mailer Settings:
108
108
  #
@@ -137,8 +137,8 @@ Alchemy.configure do |config|
137
137
  # mailer.mail_from = <%= @default_config.mailer.mail_from.inspect %>
138
138
  # mailer.mail_to = <%= @default_config.mailer.mail_to.inspect %>
139
139
  # mailer.subject = <%= @default_config.mailer.subject.inspect %>
140
- # mailer.fields = <%= @default_config.mailer.fields.inspect %>
141
- # mailer.validate_fields = <%= @default_config.mailer.validate_fields.inspect %>
140
+ # mailer.fields = <%= @default_config.mailer.fields.map(&:to_s) %>
141
+ # mailer.validate_fields = <%= @default_config.mailer.validate_fields.map(&:to_s) %>
142
142
  # end
143
143
 
144
144
  # === User roles
@@ -157,7 +157,7 @@ Alchemy.configure do |config|
157
157
  # user_roles:
158
158
  # rolename: Name of the role
159
159
  #
160
- # config.user_roles = <%= @default_config.user_roles.inspect %>
160
+ # config.user_roles = <%= @default_config.user_roles.map(&:to_s) %>
161
161
 
162
162
  # === Uploader Settings
163
163
  #
@@ -170,8 +170,8 @@ Alchemy.configure do |config|
170
170
  # uploader.upload_limit = <%= @default_config.uploader.upload_limit.inspect %>
171
171
  # uploader.file_size_limit = <%= @default_config.uploader.file_size_limit.inspect %>
172
172
  # uploader.allowed_filetypes.tap do |file_types|
173
- # file_types.alchemy_attachments = <%= @default_config.uploader.allowed_filetypes.alchemy_attachments.inspect %>
174
- # file_types.alchemy_pictures = <%= @default_config.uploader.allowed_filetypes.alchemy_pictures.inspect %>
173
+ # file_types.alchemy_attachments = <%= @default_config.uploader.allowed_filetypes.alchemy_attachments.map(&:to_s) %>
174
+ # file_types.alchemy_pictures = <%= @default_config.uploader.allowed_filetypes.alchemy_pictures.map(&:to_s) %>
175
175
  # end
176
176
  # end
177
177
 
@@ -186,11 +186,86 @@ Alchemy.configure do |config|
186
186
  #
187
187
  # jQuery(a[data-link-target="overlay"]).dialog();
188
188
  #
189
- # config.link_target_options = <%= @default_config.link_target_options.inspect %>
189
+ # config.link_target_options = <%= @default_config.link_target_options.map(&:to_s) %>
190
+
191
+ # === Format matchers
192
+ #
193
+ # Named aliases for regular expressions that can be used in various places.
194
+ # The most common use case is the format validation of ingredients, or attribute validations of your individual models.
195
+ #
196
+ # == Example:
197
+ #
198
+ # validates_format_of :url, with: Alchemy.config.format_matchers.url
199
+ #
200
+ # config.format_matchers.tap do |format|
201
+ # format.email = <%= @default_config.format_matchers.email.inspect %>
202
+ # format.url = <%= @default_config.format_matchers.url.inspect %>
203
+ # format.link_url = <%= @default_config.format_matchers.link_url.inspect %>
204
+ # end
190
205
 
191
206
  # The layout used for rendering the +alchemy/admin/pages#show+ action.
192
207
  # config.admin_page_preview_layout = <%= @default_config.admin_page_preview_layout.inspect %>
193
208
 
194
209
  # The sizes for the preview size select in the page editor.
195
- # config.page_preview_sizes = <%= @default_config.page_preview_sizes.inspect %>
210
+ # config.page_preview_sizes = <%= @default_config.page_preview_sizes.map(&:to_s) %>
211
+
212
+ # Enable full text search configuration
213
+ #
214
+ # It enables a searchable checkbox in the page form to toggle
215
+ # the searchable field. These information can used in a search
216
+ # plugin (e.g. https://github.com/AlchemyCMS/alchemy-pg_search).
217
+ #
218
+ # == Example
219
+ #
220
+ # # config/initializers/alchemy.rb
221
+ # Alchemy.config.page_searchable_checkbox = true
222
+ #
223
+ # config.show_page_searchable_checkbox = <%= @default_config.show_page_searchable_checkbox.inspect %>
224
+
225
+ # The storage adapter for Pictures and Attachments
226
+ #
227
+ config.storage_adapter = <%= @default_config.storage_adapter.inspect %>
228
+
229
+ # Additional JS modules to be imported in the Alchemy admin UI
230
+ #
231
+ # Be sure to also pin the modules with +Alchemy.importmap+.
232
+ #
233
+ # == Example
234
+ #
235
+ # Alchemy.importmap.pin "flatpickr/de",
236
+ # to: "https://ga.jspm.io/npm:flatpickr@4.6.13/dist/l10n/de.js"
237
+ #
238
+ # config.admin_js_imports << "flatpickr/de"
239
+
240
+ # Additional importmaps to be included in the Alchemy admin UI
241
+ #
242
+ # Be sure to also pin modules with +Alchemy.importmap+.
243
+ #
244
+ # config.admin_importmaps.add(
245
+ # importmap_path: root.join("config/importmap.rb"),
246
+ # source_paths: [
247
+ # root.join("app/javascript")
248
+ # ],
249
+ # name: "admin_extension"
250
+ # )
251
+
252
+ # Additional stylesheets to be included in the Alchemy admin UI
253
+ # config.admin_stylesheets.add("my_app/admin_extension")
254
+
255
+ # Define page publish targets
256
+ #
257
+ # A publish target is a ActiveJob that gets performed
258
+ # whenever a user clicks the publish page button.
259
+ #
260
+ # Use this to trigger deployment hooks of external
261
+ # services in an asychronous way.
262
+ #
263
+ # config.publish_targets << "MyPublishJob"
264
+
265
+ # Configure tabs in the link dialog
266
+ #
267
+ # With this configuration that tabs in the link dialog can be extended
268
+ # without overwriting or defacing the Admin Interface.
269
+ #
270
+ # config.link_dialog_tabs << "Acme::LinkTab"
196
271
  end
@@ -6,7 +6,7 @@ if Rake::Task.task_defined?("assets:precompile") && defined?(Propshaft)
6
6
  manifest.select { |k| k.include?("tinymce/") }.each do |k, v|
7
7
  Propshaft.logger.info "Copying #{v} to #{k}"
8
8
  FileUtils.cp(
9
- assets_path.join(v),
9
+ assets_path.join(v.dig("digested_path") || v),
10
10
  assets_path.join(k)
11
11
  )
12
12
  end
@@ -10,6 +10,7 @@ namespace :alchemy do
10
10
  Rake::Task["alchemy:tidy:remove_orphaned_records"].invoke
11
11
  Rake::Task["alchemy:tidy:remove_trashed_elements"].invoke
12
12
  Rake::Task["alchemy:tidy:remove_duplicate_legacy_urls"].invoke
13
+ Rake::Task["alchemy:tidy:remove_legacy_essence_tables"].invoke
13
14
  end
14
15
 
15
16
  desc "Fixes element positions."
@@ -37,6 +38,11 @@ namespace :alchemy do
37
38
  Alchemy::Tidy.remove_duplicate_legacy_urls
38
39
  end
39
40
 
41
+ desc "Remove all legacy essence tables"
42
+ task remove_legacy_essence_tables: [:environment] do
43
+ Alchemy::Tidy.remove_legacy_essence_tables
44
+ end
45
+
40
46
  desc "List Alchemy elements usage"
41
47
  task elements_usage: :environment do
42
48
  puts "\n"
@@ -7,6 +7,7 @@ namespace :alchemy do
7
7
  desc "List Alchemy elements usage"
8
8
  task page_usage: :environment do
9
9
  include ActionView::Helpers::NumberHelper
10
+
10
11
  puts "\n Alchemy pages usage"
11
12
  results = Alchemy::Tasks::Usage.pages_count_by_type
12
13
  if results.any?
@@ -24,6 +25,7 @@ namespace :alchemy do
24
25
  desc "List Alchemy elements usage"
25
26
  task element_usage: :environment do
26
27
  include ActionView::Helpers::NumberHelper
28
+
27
29
  puts "\n Alchemy elements usage"
28
30
  results = Alchemy::Tasks::Usage.elements_count_by_name
29
31
  if results.any?
@@ -0,0 +1 @@
1
+ body{background-color:#222f3e;color:#fff;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen,Ubuntu,Cantarell,'Open Sans','Helvetica Neue',sans-serif;line-height:1.4;margin:1rem}a{color:#4099ff}table{border-collapse:collapse}table:not([cellpadding]) td,table:not([cellpadding]) th{padding:.4rem}table[border]:not([border="0"]):not([style*=border-width]) td,table[border]:not([border="0"]):not([style*=border-width]) th{border-width:1px}table[border]:not([border="0"]):not([style*=border-style]) td,table[border]:not([border="0"]):not([style*=border-style]) th{border-style:solid}table[border]:not([border="0"]):not([style*=border-color]) td,table[border]:not([border="0"]):not([style*=border-color]) th{border-color:#6d737b}figure{display:table;margin:1rem auto}figure figcaption{color:#8a8f97;display:block;margin-top:.25rem;text-align:center}hr{border-color:#6d737b;border-style:solid;border-width:1px 0 0 0}code{background-color:#6d737b;border-radius:3px;padding:.1rem .2rem}.mce-content-body:not([dir=rtl]) blockquote{border-left:2px solid #6d737b;margin-left:1.5rem;padding-left:1rem}.mce-content-body[dir=rtl] blockquote{border-right:2px solid #6d737b;margin-right:1.5rem;padding-right:1rem}
@@ -0,0 +1 @@
1
+ body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen,Ubuntu,Cantarell,'Open Sans','Helvetica Neue',sans-serif;line-height:1.4;margin:1rem}table{border-collapse:collapse}table:not([cellpadding]) td,table:not([cellpadding]) th{padding:.4rem}table[border]:not([border="0"]):not([style*=border-width]) td,table[border]:not([border="0"]):not([style*=border-width]) th{border-width:1px}table[border]:not([border="0"]):not([style*=border-style]) td,table[border]:not([border="0"]):not([style*=border-style]) th{border-style:solid}table[border]:not([border="0"]):not([style*=border-color]) td,table[border]:not([border="0"]):not([style*=border-color]) th{border-color:#ccc}figure{display:table;margin:1rem auto}figure figcaption{color:#999;display:block;margin-top:.25rem;text-align:center}hr{border-color:#ccc;border-style:solid;border-width:1px 0 0 0}code{background-color:#e8e8e8;border-radius:3px;padding:.1rem .2rem}.mce-content-body:not([dir=rtl]) blockquote{border-left:2px solid #ccc;margin-left:1.5rem;padding-left:1rem}.mce-content-body[dir=rtl] blockquote{border-right:2px solid #ccc;margin-right:1.5rem;padding-right:1rem}