alchemy_cms 7.0.0.pre.a → 7.0.0.pre.c

Sign up to get free protection for your applications and to get access to all the features.
Files changed (237) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/brakeman-analysis.yml +2 -2
  3. data/.github/workflows/lint.yml +37 -0
  4. data/.github/workflows/{ci.yml → test.yml} +8 -8
  5. data/.gitignore +0 -5
  6. data/.hound.yml +2 -3
  7. data/.rubocop.yml +4 -350
  8. data/.standard.yml +3 -0
  9. data/CHANGELOG.md +33 -0
  10. data/Gemfile +3 -2
  11. data/README.md +10 -12
  12. data/Rakefile +0 -19
  13. data/alchemy_cms.gemspec +4 -2
  14. data/app/assets/config/alchemy_manifest.js +1 -0
  15. data/app/assets/javascripts/alchemy/admin.js +0 -2
  16. data/app/assets/javascripts/alchemy/alchemy.dirty.js.coffee +1 -1
  17. data/app/assets/javascripts/alchemy/alchemy.initializer.js.coffee +5 -12
  18. data/app/assets/javascripts/alchemy/alchemy.link_dialog.js.coffee +6 -1
  19. data/app/assets/stylesheets/alchemy/base.scss +2 -2
  20. data/app/components/alchemy/ingredients/audio_view.rb +37 -0
  21. data/app/components/alchemy/ingredients/base_view.rb +38 -0
  22. data/app/components/alchemy/ingredients/boolean_view.rb +13 -0
  23. data/app/components/alchemy/ingredients/datetime_view.rb +22 -0
  24. data/app/components/alchemy/ingredients/file_view.rb +40 -0
  25. data/app/components/alchemy/ingredients/headline_view.rb +20 -0
  26. data/app/components/alchemy/ingredients/html_view.rb +9 -0
  27. data/app/components/alchemy/ingredients/link_view.rb +25 -0
  28. data/app/components/alchemy/ingredients/node_view.rb +11 -0
  29. data/app/components/alchemy/ingredients/page_view.rb +15 -0
  30. data/app/components/alchemy/ingredients/picture_view.rb +108 -0
  31. data/app/components/alchemy/ingredients/richtext_view.rb +22 -0
  32. data/app/components/alchemy/ingredients/select_view.rb +6 -0
  33. data/app/components/alchemy/ingredients/text_view.rb +41 -0
  34. data/app/components/alchemy/ingredients/video_view.rb +39 -0
  35. data/app/controllers/alchemy/admin/attachments_controller.rb +3 -3
  36. data/app/controllers/alchemy/admin/base_controller.rb +7 -7
  37. data/app/controllers/alchemy/admin/clipboard_controller.rb +2 -2
  38. data/app/controllers/alchemy/admin/elements_controller.rb +26 -11
  39. data/app/controllers/alchemy/admin/languages_controller.rb +1 -1
  40. data/app/controllers/alchemy/admin/nodes_controller.rb +2 -2
  41. data/app/controllers/alchemy/admin/pages_controller.rb +10 -10
  42. data/app/controllers/alchemy/admin/pictures_controller.rb +14 -14
  43. data/app/controllers/alchemy/admin/resources_controller.rb +27 -28
  44. data/app/controllers/alchemy/admin/styleguide_controller.rb +1 -0
  45. data/app/controllers/alchemy/admin/tags_controller.rb +11 -11
  46. data/app/controllers/alchemy/api/base_controller.rb +2 -2
  47. data/app/controllers/alchemy/api/elements_controller.rb +11 -11
  48. data/app/controllers/alchemy/api/ingredients_controller.rb +1 -1
  49. data/app/controllers/alchemy/api/nodes_controller.rb +1 -1
  50. data/app/controllers/alchemy/api/pages_controller.rb +11 -11
  51. data/app/controllers/alchemy/attachments_controller.rb +3 -3
  52. data/app/controllers/alchemy/base_controller.rb +1 -8
  53. data/app/controllers/alchemy/messages_controller.rb +9 -9
  54. data/app/controllers/alchemy/pages_controller.rb +8 -19
  55. data/app/controllers/concerns/alchemy/admin/archive_overlay.rb +1 -0
  56. data/app/controllers/concerns/alchemy/admin/uploader_responses.rb +5 -7
  57. data/app/controllers/concerns/alchemy/legacy_page_redirects.rb +5 -5
  58. data/app/decorators/alchemy/element_editor.rb +4 -4
  59. data/app/decorators/alchemy/ingredient_editor.rb +6 -6
  60. data/app/helpers/alchemy/admin/attachments_helper.rb +1 -1
  61. data/app/helpers/alchemy/admin/base_helper.rb +21 -22
  62. data/app/helpers/alchemy/admin/elements_helper.rb +1 -1
  63. data/app/helpers/alchemy/admin/form_helper.rb +1 -1
  64. data/app/helpers/alchemy/admin/navigation_helper.rb +7 -7
  65. data/app/helpers/alchemy/admin/pages_helper.rb +2 -2
  66. data/app/helpers/alchemy/admin/tags_helper.rb +3 -3
  67. data/app/helpers/alchemy/base_helper.rb +2 -2
  68. data/app/helpers/alchemy/elements_block_helper.rb +9 -7
  69. data/app/helpers/alchemy/elements_helper.rb +12 -12
  70. data/app/helpers/alchemy/pages_helper.rb +11 -11
  71. data/app/helpers/alchemy/url_helper.rb +1 -1
  72. data/{package/src → app/javascript/alchemy_admin}/datepicker.js +1 -0
  73. data/{package/src → app/javascript/alchemy_admin}/node_tree.js +2 -2
  74. data/{package/src → app/javascript/alchemy_admin}/page_publication_fields.js +1 -1
  75. data/{package/src → app/javascript/alchemy_admin}/page_sorter.js +1 -1
  76. data/{package/src → app/javascript/alchemy_admin}/picture_editors.js +2 -2
  77. data/{package/src → app/javascript/alchemy_admin}/sitemap.js +4 -4
  78. data/app/javascript/alchemy_admin/tinymce.js +142 -0
  79. data/app/javascript/alchemy_admin.js +34 -0
  80. data/app/mailers/alchemy/messages_mailer.rb +1 -1
  81. data/app/models/alchemy/attachment.rb +6 -6
  82. data/app/models/alchemy/base_record.rb +1 -0
  83. data/app/models/alchemy/eager_loading.rb +6 -6
  84. data/app/models/alchemy/element/definitions.rb +1 -1
  85. data/app/models/alchemy/element/element_ingredients.rb +3 -3
  86. data/app/models/alchemy/element.rb +2 -2
  87. data/app/models/alchemy/elements_repository.rb +1 -1
  88. data/app/models/alchemy/image_cropper_settings.rb +2 -2
  89. data/app/models/alchemy/ingredient.rb +14 -12
  90. data/app/models/alchemy/ingredient_validator.rb +1 -1
  91. data/app/models/alchemy/ingredients/datetime.rb +1 -1
  92. data/app/models/alchemy/ingredients/file.rb +5 -5
  93. data/app/models/alchemy/ingredients/headline.rb +4 -4
  94. data/app/models/alchemy/ingredients/picture.rb +27 -9
  95. data/app/models/alchemy/ingredients/richtext.rb +15 -12
  96. data/app/models/alchemy/ingredients/text.rb +6 -6
  97. data/app/models/alchemy/language/code.rb +1 -1
  98. data/app/models/alchemy/language.rb +4 -4
  99. data/app/models/alchemy/legacy_page_url.rb +1 -1
  100. data/app/models/alchemy/node.rb +2 -2
  101. data/app/models/alchemy/page/page_elements.rb +14 -14
  102. data/app/models/alchemy/page/page_naming.rb +4 -4
  103. data/app/models/alchemy/page/page_natures.rb +1 -1
  104. data/app/models/alchemy/page/page_scopes.rb +5 -5
  105. data/app/models/alchemy/page.rb +11 -11
  106. data/app/models/alchemy/picture/calculations.rb +2 -2
  107. data/app/models/alchemy/picture/transformations.rb +2 -2
  108. data/app/models/alchemy/picture/url.rb +4 -4
  109. data/app/models/alchemy/picture.rb +11 -10
  110. data/app/models/alchemy/picture_thumb/create.rb +1 -1
  111. data/app/models/alchemy/picture_thumb.rb +1 -1
  112. data/app/models/alchemy/picture_variant.rb +2 -3
  113. data/app/models/alchemy/tag.rb +8 -0
  114. data/app/models/concerns/alchemy/picture_thumbnails.rb +6 -6
  115. data/app/serializers/alchemy/base_serializer.rb +1 -1
  116. data/app/serializers/alchemy/page_tree_serializer.rb +7 -7
  117. data/app/services/alchemy/duplicate_element.rb +3 -3
  118. data/app/services/alchemy/tag_validations.rb +1 -1
  119. data/app/views/alchemy/_menubar.html.erb +1 -1
  120. data/app/views/alchemy/admin/attachments/_replace_button.html.erb +1 -1
  121. data/app/views/alchemy/admin/attachments/destroy.js.erb +1 -1
  122. data/app/views/alchemy/admin/elements/_element.html.erb +3 -0
  123. data/app/views/alchemy/admin/nodes/index.html.erb +4 -2
  124. data/app/views/alchemy/admin/pages/_page_layout_filter.html.erb +1 -1
  125. data/app/views/alchemy/admin/pages/edit.html.erb +3 -7
  126. data/app/views/alchemy/admin/pages/index.html.erb +1 -1
  127. data/app/views/alchemy/admin/pages/update.js.erb +12 -6
  128. data/app/views/alchemy/admin/pictures/_infos.html.erb +1 -1
  129. data/app/views/alchemy/admin/resources/_filter_bar.html.erb +1 -1
  130. data/app/views/alchemy/admin/styleguide/index.html.erb +1 -1
  131. data/app/views/alchemy/admin/uploader/_button.html.erb +1 -1
  132. data/app/views/alchemy/base/permission_denied.js.erb +1 -1
  133. data/app/views/alchemy/base/redirect.js.erb +1 -1
  134. data/app/views/alchemy/ingredients/_audio_view.html.erb +1 -14
  135. data/app/views/alchemy/ingredients/_boolean_view.html.erb +1 -1
  136. data/app/views/alchemy/ingredients/_datetime_view.html.erb +3 -9
  137. data/app/views/alchemy/ingredients/_file_view.html.erb +3 -16
  138. data/app/views/alchemy/ingredients/_headline_view.html.erb +4 -10
  139. data/app/views/alchemy/ingredients/_html_view.html.erb +1 -1
  140. data/app/views/alchemy/ingredients/_link_view.html.erb +4 -9
  141. data/app/views/alchemy/ingredients/_node_view.html.erb +1 -1
  142. data/app/views/alchemy/ingredients/_page_view.html.erb +1 -4
  143. data/app/views/alchemy/ingredients/_picture_view.html.erb +4 -5
  144. data/app/views/alchemy/ingredients/_richtext_editor.html.erb +11 -2
  145. data/app/views/alchemy/ingredients/_richtext_view.html.erb +3 -3
  146. data/app/views/alchemy/ingredients/_select_view.html.erb +1 -1
  147. data/app/views/alchemy/ingredients/_text_view.html.erb +3 -19
  148. data/app/views/alchemy/ingredients/_video_view.html.erb +3 -18
  149. data/app/views/alchemy/ingredients/shared/_link_tools.html.erb +1 -0
  150. data/app/views/alchemy/ingredients/shared/_picture_tools.html.erb +1 -0
  151. data/app/views/layouts/alchemy/admin.html.erb +9 -15
  152. data/bin/importmap +4 -0
  153. data/bin/setup +28 -0
  154. data/bin/start +17 -0
  155. data/config/brakeman.ignore +0 -46
  156. data/config/importmap.rb +8 -0
  157. data/config/initializers/assets.rb +1 -0
  158. data/config/initializers/dragonfly.rb +1 -0
  159. data/config/initializers/mime_types.rb +1 -0
  160. data/config/initializers/mini_profiler.rb +1 -0
  161. data/config/initializers/simple_form.rb +3 -2
  162. data/config/locales/alchemy.en.yml +1 -1
  163. data/config/routes.rb +21 -20
  164. data/config/spring.rb +1 -0
  165. data/db/migrate/20230121212637_alchemy_six_point_one.rb +8 -8
  166. data/db/migrate/20230505132743_add_indexes_to_alchemy_pictures.rb +6 -0
  167. data/lib/alchemy/admin/locale.rb +3 -3
  168. data/lib/alchemy/admin/preview_url.rb +2 -2
  169. data/lib/alchemy/auth_accessors.rb +1 -1
  170. data/lib/alchemy/config.rb +1 -1
  171. data/lib/alchemy/controller_actions.rb +4 -4
  172. data/lib/alchemy/deprecation.rb +1 -0
  173. data/lib/alchemy/dragonfly/processors/thumbnail.rb +1 -1
  174. data/lib/alchemy/element_definition.rb +2 -2
  175. data/lib/alchemy/engine.rb +16 -1
  176. data/lib/alchemy/filetypes.rb +7 -7
  177. data/lib/alchemy/forms/builder.rb +4 -4
  178. data/lib/alchemy/i18n.rb +6 -4
  179. data/lib/alchemy/install/tasks.rb +2 -1
  180. data/lib/alchemy/name_conversions.rb +1 -1
  181. data/lib/alchemy/page_layout.rb +1 -1
  182. data/lib/alchemy/permissions.rb +5 -4
  183. data/lib/alchemy/resource.rb +10 -10
  184. data/lib/alchemy/resources_helper.rb +7 -7
  185. data/lib/alchemy/routing_constraints.rb +2 -2
  186. data/lib/alchemy/seeder.rb +12 -5
  187. data/lib/alchemy/shell.rb +2 -1
  188. data/lib/alchemy/taggable.rb +3 -2
  189. data/lib/alchemy/tasks/tidy.rb +1 -0
  190. data/lib/alchemy/test_support/capybara_helpers.rb +1 -1
  191. data/lib/alchemy/test_support/config_stubbing.rb +1 -0
  192. data/lib/alchemy/test_support/factories/element_factory.rb +4 -0
  193. data/lib/alchemy/test_support/factories/page_factory.rb +2 -2
  194. data/lib/alchemy/test_support/having_crop_action_examples.rb +9 -9
  195. data/lib/alchemy/test_support/having_picture_thumbnails_examples.rb +33 -33
  196. data/lib/alchemy/test_support/integration_helpers.rb +4 -3
  197. data/lib/alchemy/test_support/shared_contexts.rb +2 -1
  198. data/lib/alchemy/test_support/shared_dom_ids_examples.rb +9 -9
  199. data/lib/alchemy/test_support/shared_ingredient_examples.rb +12 -6
  200. data/lib/alchemy/test_support/shared_uploader_examples.rb +1 -0
  201. data/lib/alchemy/tinymce.rb +3 -26
  202. data/lib/alchemy/upgrader/seven_point_zero.rb +13 -23
  203. data/lib/alchemy/upgrader.rb +1 -11
  204. data/lib/alchemy/version.rb +1 -1
  205. data/lib/alchemy.rb +5 -0
  206. data/lib/alchemy_cms.rb +3 -1
  207. data/lib/generators/alchemy/base.rb +3 -2
  208. data/lib/generators/alchemy/elements/elements_generator.rb +2 -1
  209. data/lib/generators/alchemy/ingredient/ingredient_generator.rb +1 -0
  210. data/lib/generators/alchemy/install/files/application.html.erb +2 -2
  211. data/lib/generators/alchemy/install/install_generator.rb +2 -25
  212. data/lib/generators/alchemy/module/module_generator.rb +1 -0
  213. data/lib/generators/alchemy/page_layouts/page_layouts_generator.rb +1 -0
  214. data/lib/generators/alchemy/site_layouts/site_layouts_generator.rb +1 -0
  215. data/lib/generators/alchemy/views/views_generator.rb +2 -1
  216. data/lib/tasks/alchemy/thumbnails.rake +5 -5
  217. data/lib/tasks/alchemy/tidy.rake +1 -0
  218. data/lib/tasks/alchemy/upgrade.rake +10 -15
  219. data/package.json +6 -26
  220. metadata +80 -31
  221. data/app/assets/javascripts/alchemy/alchemy.tinymce.js.coffee +0 -93
  222. data/app/presenters/alchemy/picture_view.rb +0 -88
  223. data/app/views/alchemy/admin/pages/_tinymce_custom_config.html.erb +0 -10
  224. data/package/admin.js +0 -32
  225. data/package/dist/admin.js +0 -16
  226. data/package/dist/admin.js.map +0 -7
  227. data/package/src/__tests__/i18n.spec.js +0 -93
  228. data/package/src/utils/__tests__/ajax.spec.js +0 -168
  229. data/package/src/utils/__tests__/events.spec.js +0 -38
  230. /data/{package/src → app/javascript/alchemy_admin}/file_editors.js +0 -0
  231. /data/{package/src → app/javascript/alchemy_admin}/i18n.js +0 -0
  232. /data/{package/src → app/javascript/alchemy_admin}/image_cropper.js +0 -0
  233. /data/{package/src → app/javascript/alchemy_admin}/image_loader.js +0 -0
  234. /data/{package/src → app/javascript/alchemy_admin}/ingredient_anchor_link.js +0 -0
  235. /data/{package/src → app/javascript/alchemy_admin}/translations.js +0 -0
  236. /data/{package/src → app/javascript/alchemy_admin}/utils/ajax.js +0 -0
  237. /data/{package/src → app/javascript/alchemy_admin}/utils/events.js +0 -0
data/Rakefile CHANGED
@@ -41,17 +41,12 @@ namespace :alchemy do
41
41
  task :prepare do
42
42
  system(
43
43
  <<~BASH
44
- yarn install && \
45
- yarn link && \
46
44
  cd spec/dummy && \
47
- yarn link @alchemy_cms/admin && \
48
45
  export RAILS_ENV=test && \
49
46
  bin/rake db:create && \
50
47
  bin/rake db:environment:set && \
51
48
  bin/rake db:migrate:reset && \
52
- bin/rails javascript:install:esbuild && \
53
49
  bin/rails g alchemy:install --skip --skip-demo-files --auto-accept --skip-db-create && \
54
- bin/rails javascript:build && \
55
50
  cd -
56
51
  BASH
57
52
  ) || fail
@@ -81,18 +76,4 @@ namespace :alchemy do
81
76
  File.delete(backup)
82
77
  end
83
78
  end
84
-
85
- desc "Release a new Ruby gem and npm package in one command"
86
- task :release do
87
- require "json"
88
- require_relative "lib/alchemy/version"
89
- package = File.read("package.json")
90
- unless JSON.parse(package)["version"] == Alchemy.version
91
- abort "Ruby gem and npm package versions are out of sync! Please fix."
92
- end
93
- # Release the Ruby gem with bundler
94
- Rake::Task["release"].invoke
95
- # Publish npm package via CLI
96
- system "npm publish"
97
- end
98
79
  end
data/alchemy_cms.gemspec CHANGED
@@ -14,7 +14,7 @@ Gem::Specification.new do |gem|
14
14
  gem.summary = "A powerful, userfriendly and flexible CMS for Rails"
15
15
  gem.description = "Alchemy is a powerful, userfriendly and flexible Rails CMS."
16
16
  gem.requirements << "ImageMagick (libmagick), v6.6 or greater."
17
- gem.required_ruby_version = ">= 2.5.0"
17
+ gem.required_ruby_version = ">= 3.0.0"
18
18
  gem.license = "BSD New"
19
19
  gem.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^spec/}) }
20
20
  gem.require_paths = ["lib"]
@@ -41,6 +41,7 @@ Gem::Specification.new do |gem|
41
41
  gem.add_runtime_dependency "dragonfly_svg", ["~> 0.0.4"]
42
42
  gem.add_runtime_dependency "gutentag", ["~> 2.2", ">= 2.2.1"]
43
43
  gem.add_runtime_dependency "handlebars_assets", ["~> 0.23"]
44
+ gem.add_runtime_dependency "importmap-rails", ["~> 1.2", ">= 1.2.1"]
44
45
  gem.add_runtime_dependency "jquery-rails", ["~> 4.0", ">= 4.0.4"]
45
46
  gem.add_runtime_dependency "jquery-ui-rails", ["~> 6.0"]
46
47
  gem.add_runtime_dependency "kaminari", ["~> 1.1"]
@@ -51,7 +52,8 @@ Gem::Specification.new do |gem|
51
52
  gem.add_runtime_dependency "sassc-rails", ["~> 2.1"]
52
53
  gem.add_runtime_dependency "simple_form", [">= 4.0", "< 6"]
53
54
  gem.add_runtime_dependency "sprockets", [">= 3.0", "< 5"]
54
- gem.add_runtime_dependency "turbolinks", [">= 2.5"]
55
+ gem.add_runtime_dependency "turbo-rails", [">= 1.4"]
56
+ gem.add_runtime_dependency "view_component", ["~> 3.0"]
55
57
 
56
58
  gem.add_development_dependency "capybara", ["~> 3.0"]
57
59
  gem.add_development_dependency "capybara-screenshot", ["~> 1.0"]
@@ -13,3 +13,4 @@
13
13
  //= link_tree ../images/alchemy/
14
14
  //= link_tree ../../../vendor/assets/fonts/
15
15
  //= link_tree ../../../vendor/assets/images/
16
+ //= link_tree ../../javascript .js
@@ -2,7 +2,6 @@
2
2
  // ------------------------------
3
3
  //= require jquery2
4
4
  //= require jquery_ujs
5
- //= require turbolinks
6
5
  //= require jquery-ui/effects/effect-fade
7
6
  //= require jquery-ui/widgets/draggable
8
7
  //= require jquery-ui/widgets/sortable
@@ -38,7 +37,6 @@
38
37
  //= require alchemy/alchemy.uploader
39
38
  //= require alchemy/alchemy.preview_window
40
39
  //= require alchemy/alchemy.spinner
41
- //= require alchemy/alchemy.tinymce
42
40
  //= require alchemy/alchemy.tooltips
43
41
  //= require alchemy/page_select
44
42
  //= require alchemy/node_select
@@ -40,7 +40,7 @@ $.extend Alchemy,
40
40
  $form.submit()
41
41
  else if $(element).is("a")
42
42
  callback = ->
43
- Turbolinks.visit(element.pathname)
43
+ Turbo.visit(element.pathname)
44
44
  if Alchemy.isPageDirty()
45
45
  Alchemy.openConfirmDialog Alchemy.t('page_dirty_notice'),
46
46
  title: Alchemy.t('warning')
@@ -1,6 +1,6 @@
1
1
  # Initialize all onload scripts at once.
2
2
  #
3
- # Called at jQuery ready event and Turbolinks page change event.
3
+ # Called at jQuery ready event and Turbo page change event.
4
4
  #
5
5
  Alchemy.Initializer = ->
6
6
 
@@ -26,13 +26,13 @@ Alchemy.Initializer = ->
26
26
  $('select#change_locale').on 'change', (e) ->
27
27
  url = window.location.pathname
28
28
  delimiter = if url.match(/\?/) then '&' else '?'
29
- Turbolinks.visit "#{url}#{delimiter}admin_locale=#{$(this).val()}"
29
+ Turbo.visit "#{url}#{delimiter}admin_locale=#{$(this).val()}"
30
30
 
31
31
  # Site select handler
32
32
  $('select#change_site').on 'change', (e) ->
33
33
  url = window.location.pathname
34
34
  delimiter = if url.match(/\?/) then '&' else '?'
35
- Turbolinks.visit "#{url}#{delimiter}site_id=#{$(this).val()}"
35
+ Turbo.visit "#{url}#{delimiter}site_id=#{$(this).val()}"
36
36
 
37
37
  # Submit forms of selects with `data-autosubmit="true"`
38
38
  $('select[data-auto-submit="true"]').on 'change', (e) ->
@@ -46,18 +46,11 @@ Alchemy.Initializer = ->
46
46
  tagName = (event.target || event.srcElement).tagName
47
47
  key.isPressed('esc') || !(tagName == 'INPUT' || tagName == 'SELECT' || tagName == 'TEXTAREA')
48
48
 
49
- # Enabling the Turbolinks Progress Bar for v2.5
50
- Turbolinks.enableProgressBar() if Turbolinks.enableProgressBar
51
-
52
- # Turbolinks DOM Ready.
53
- # Handle both v2.5(page:change), and v.5.0 (turbolinks:load)
54
- $(document).on 'page:change turbolinks:load', ->
49
+ $(document).on 'turbo:load', ->
55
50
  Alchemy.Initializer()
56
51
  return
57
52
 
58
- # Turbolinks before parsing a new page
59
- # Handle both v2.5(page:receive), and v.5.0 (turbolinks:request-end)
60
- $(document).on 'page:receive turbolinks:request-end', ->
53
+ $(document).on 'turbo:before-fetch-request', ->
61
54
  # Ensure that all tinymce editors get removed before parsing a new page
62
55
  Alchemy.Tinymce.removeFrom $('.has_tinymce')
63
56
  return
@@ -59,12 +59,17 @@ class window.Alchemy.LinkDialog extends Alchemy.Dialog
59
59
  initPageSelect: ->
60
60
  pageTemplate = HandlebarsTemplates.page
61
61
  element_anchor_placeholder = @$element_anchor.attr('placeholder')
62
+ if @link_object.dataset.languageId
63
+ api_url = "#{Alchemy.routes.api_pages_path}?language_id=#{@link_object.dataset.languageId}"
64
+ else
65
+ api_url = Alchemy.routes.api_pages_path
66
+
62
67
  @$internal_link.select2
63
68
  placeholder: Alchemy.t('Search page')
64
69
  allowClear: true
65
70
  minimumInputLength: 3
66
71
  ajax:
67
- url: Alchemy.routes.api_pages_path
72
+ url: api_url
68
73
  datatype: 'json'
69
74
  quietMillis: 300
70
75
  data: (term, page) ->
@@ -3,8 +3,8 @@ html {
3
3
  height: 100%;
4
4
  font-size: $base-font-size;
5
5
 
6
- &.turbolinks-progress-bar::before,
7
- .turbolinks-progress-bar {
6
+ &.turbo-progress-bar::before,
7
+ .turbo-progress-bar {
8
8
  background-color: $blue !important;
9
9
  z-index: 400001;
10
10
  }
@@ -0,0 +1,37 @@
1
+ module Alchemy
2
+ module Ingredients
3
+ class AudioView < BaseView
4
+ def call
5
+ content_tag(:audio, **html_options) do
6
+ tag(:source, src: src, type: type)
7
+ end
8
+ end
9
+
10
+ def render?
11
+ !!ingredient.attachment
12
+ end
13
+
14
+ private
15
+
16
+ def src
17
+ alchemy.show_attachment_path(
18
+ ingredient.attachment,
19
+ format: ingredient.attachment.suffix
20
+ )
21
+ end
22
+
23
+ def type
24
+ ingredient.attachment.file_mime_type
25
+ end
26
+
27
+ def html_options
28
+ {
29
+ controls: ingredient.controls,
30
+ autoplay: ingredient.autoplay,
31
+ loop: ingredient.loop,
32
+ muted: ingredient.muted
33
+ }
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,38 @@
1
+ module Alchemy
2
+ module Ingredients
3
+ class BaseView < ViewComponent::Base
4
+ attr_reader :ingredient, :html_options
5
+
6
+ delegate :alchemy, to: :helpers
7
+ delegate :settings, :value, to: :ingredient
8
+
9
+ # @param ingredient [Alchemy::Ingredient]
10
+ # @param html_options [Hash] Options that will be passed to the wrapper tag.
11
+ def initialize(ingredient, html_options: {})
12
+ raise ArgumentError, "Ingredient missing!" if ingredient.nil?
13
+
14
+ @ingredient = ingredient
15
+ @html_options = html_options
16
+ end
17
+
18
+ def call
19
+ value
20
+ end
21
+
22
+ def render?
23
+ value.present?
24
+ end
25
+
26
+ private
27
+
28
+ # Fetches value from ingredient settings and allows to merge a value on top of it
29
+ #
30
+ # @param key [Symbol] - The settings key you want to fetch the value from
31
+ # @param value [Object] - A optional value that can override the setting.
32
+ # Normally passed as into the ingredient view.
33
+ def settings_value(key, value: nil, default: nil)
34
+ value.nil? ? settings.fetch(key, default) : value
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,13 @@
1
+ module Alchemy
2
+ module Ingredients
3
+ class BooleanView < BaseView
4
+ def call
5
+ Alchemy.t(value, scope: "ingredient_values.boolean")
6
+ end
7
+
8
+ def render?
9
+ !value.nil?
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,22 @@
1
+ module Alchemy
2
+ module Ingredients
3
+ class DatetimeView < BaseView
4
+ attr_reader :date_format
5
+
6
+ # @param ingredient [Alchemy::Ingredient]
7
+ # @param date_format [String] The date format to use. Use either a strftime format string, a I18n format symbol or "rfc822".
8
+ def initialize(ingredient, date_format: nil, html_options: {})
9
+ super(ingredient)
10
+ @date_format = settings_value(:date_format, value: date_format)
11
+ end
12
+
13
+ def call
14
+ if date_format == "rfc822"
15
+ ingredient.value.to_s(:rfc822)
16
+ else
17
+ ::I18n.l(ingredient.value, format: date_format)
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,40 @@
1
+ module Alchemy
2
+ module Ingredients
3
+ class FileView < BaseView
4
+ delegate :attachment, to: :ingredient
5
+
6
+ # @param ingredient [Alchemy::Ingredient]
7
+ # @param link_text [String] The link text. If not given, the ingredients link_text setting or the attachments name will be used.
8
+ # @param html_options [Hash] Options that will be passed to the a tag.
9
+ def initialize(ingredient, link_text: nil, html_options: {})
10
+ super(ingredient, html_options: html_options)
11
+ @link_text = settings_value(:link_text, value: link_text, default: attachment&.name)
12
+ end
13
+
14
+ def call
15
+ link_to(
16
+ link_text,
17
+ attachment.url(
18
+ download: true,
19
+ name: attachment.slug,
20
+ format: attachment.suffix
21
+ ),
22
+ {
23
+ class: ingredient.css_class.presence,
24
+ title: ingredient.title.presence
25
+ }.merge(html_options)
26
+ )
27
+ end
28
+
29
+ def render?
30
+ !attachment.nil?
31
+ end
32
+
33
+ private
34
+
35
+ def link_text
36
+ ingredient.link_text.presence || @link_text
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,20 @@
1
+ module Alchemy
2
+ module Ingredients
3
+ class HeadlineView < BaseView
4
+ def initialize(ingredient, level: nil, html_options: {})
5
+ super(ingredient, html_options: html_options)
6
+ @level = level
7
+ end
8
+
9
+ def call
10
+ content_tag "h#{@level || ingredient.level}",
11
+ ingredient.value,
12
+ id: ingredient.dom_id.presence,
13
+ class: [
14
+ ingredient.size ? "h#{ingredient.size}" : nil,
15
+ html_options[:class]
16
+ ]
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,9 @@
1
+ module Alchemy
2
+ module Ingredients
3
+ class HtmlView < BaseView
4
+ def call
5
+ value.to_s.html_safe
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,25 @@
1
+ module Alchemy
2
+ module Ingredients
3
+ class LinkView < BaseView
4
+ attr_reader :link_text
5
+
6
+ # @param ingredient [Alchemy::Ingredient]
7
+ # @param text [String] The link text. If not given, the ingredient's text setting or the value will be used.
8
+ # @param html_options [Hash] Options that will be passed to the a tag.
9
+ def initialize(ingredient, text: nil, html_options: {})
10
+ super(ingredient, html_options: html_options)
11
+ @link_text = settings_value(:text, value: text, default: value)
12
+ end
13
+
14
+ def call
15
+ link_to(link_text, value, {target: link_target}.merge(html_options))
16
+ end
17
+
18
+ private
19
+
20
+ def link_target
21
+ (ingredient.link_target == "blank") ? "_blank" : nil
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,11 @@
1
+ module Alchemy
2
+ module Ingredients
3
+ class NodeView < BaseView
4
+ delegate :node, to: :ingredient
5
+
6
+ def call
7
+ render(node)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,15 @@
1
+ module Alchemy
2
+ module Ingredients
3
+ class PageView < BaseView
4
+ delegate :page, to: :ingredient
5
+
6
+ def call
7
+ link_to page.name, alchemy.show_page_path(urlname: page.urlname)
8
+ end
9
+
10
+ def render?
11
+ !!page
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,108 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Alchemy
4
+ module Ingredients
5
+ # Renders a picture ingredient view
6
+ class PictureView < BaseView
7
+ attr_reader :ingredient,
8
+ :show_caption,
9
+ :disable_link,
10
+ :srcset,
11
+ :sizes,
12
+ :html_options,
13
+ :picture_options,
14
+ :picture
15
+
16
+ # @param ingredient [Alchemy::Ingredient]
17
+ # @param show_caption [Boolean] (true) Whether to show a caption or not, even if present on the picture.
18
+ # @param disable_link [Boolean] (false) Whether to disable the link even if the picture has a link.
19
+ # @param srcset [Array<String>] An array of srcset sizes that will generate variants of the picture.
20
+ # @param sizes [Array<String>] An array of sizes that will be passed to the img tag.
21
+ # @param picture_options [Hash] Options that will be passed to the picture url. See {Alchemy::PictureVariant} for options.
22
+ # @param html_options [Hash] Options that will be passed to the img tag.
23
+ # @see Alchemy::PictureVariant
24
+ def initialize(
25
+ ingredient,
26
+ show_caption: nil,
27
+ disable_link: nil,
28
+ srcset: nil,
29
+ sizes: nil,
30
+ picture_options: {},
31
+ html_options: {}
32
+ )
33
+ super(ingredient)
34
+ @show_caption = settings_value(:show_caption, value: show_caption, default: true)
35
+ @disable_link = settings_value(:disable_link, value: disable_link, default: false)
36
+ @srcset = settings_value(:srcset, value: srcset, default: [])
37
+ @sizes = settings_value(:sizes, value: sizes, default: [])
38
+ @picture_options = picture_options || {}
39
+ @html_options = html_options || {}
40
+ @picture = ingredient.picture
41
+ end
42
+
43
+ def call
44
+ return if picture.blank?
45
+
46
+ output = caption ? img_tag + caption : img_tag
47
+
48
+ if is_linked?
49
+ output = link_to(output, url_for(ingredient.link), {
50
+ title: ingredient.link_title.presence,
51
+ target: (ingredient.link_target == "blank") ? "_blank" : nil,
52
+ data: {link_target: ingredient.link_target.presence}
53
+ })
54
+ end
55
+
56
+ if caption
57
+ content_tag(:figure, output, {class: ingredient.css_class.presence}.merge(html_options))
58
+ else
59
+ output
60
+ end
61
+ end
62
+
63
+ private
64
+
65
+ def caption
66
+ return unless show_caption?
67
+
68
+ @_caption ||= content_tag(:figcaption, ingredient.caption)
69
+ end
70
+
71
+ def src
72
+ ingredient.picture_url(picture_options)
73
+ end
74
+
75
+ def img_tag
76
+ @_img_tag ||= image_tag(
77
+ src, {
78
+ alt: alt_text,
79
+ title: ingredient.title.presence,
80
+ class: caption ? nil : ingredient.css_class.presence,
81
+ srcset: srcset_options.join(", ").presence,
82
+ sizes: sizes.join(", ").presence
83
+ }.merge(caption ? {} : html_options)
84
+ )
85
+ end
86
+
87
+ def show_caption?
88
+ show_caption && ingredient.caption.present?
89
+ end
90
+
91
+ def is_linked?
92
+ !disable_link && ingredient.link.present?
93
+ end
94
+
95
+ def srcset_options
96
+ srcset.map do |size|
97
+ url = ingredient.picture_url(size: size)
98
+ width, height = size.split("x")
99
+ width.present? ? "#{url} #{width}w" : "#{url} #{height}h"
100
+ end
101
+ end
102
+
103
+ def alt_text
104
+ ingredient.alt_tag.presence || html_options.delete(:alt) || ingredient.picture.name&.humanize
105
+ end
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,22 @@
1
+ module Alchemy
2
+ module Ingredients
3
+ class RichtextView < BaseView
4
+ attr_reader :plain_text
5
+
6
+ # @param ingredient [Alchemy::Ingredient]
7
+ # @param plain_text [Boolean] (false) Whether to show as plain text or with markup
8
+ def initialize(ingredient, plain_text: nil, html_options: {})
9
+ super(ingredient)
10
+ @plain_text = settings_value(:plain_text, value: plain_text, default: false)
11
+ end
12
+
13
+ def call
14
+ if plain_text
15
+ ingredient.stripped_body
16
+ else
17
+ value.to_s.html_safe
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,6 @@
1
+ module Alchemy
2
+ module Ingredients
3
+ class SelectView < BaseView
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,41 @@
1
+ module Alchemy
2
+ module Ingredients
3
+ class TextView < BaseView
4
+ attr_reader :disable_link
5
+
6
+ delegate :dom_id, :link, :link_title, :link_target,
7
+ to: :ingredient
8
+
9
+ # @param ingredient [Alchemy::Ingredient]
10
+ # @param disable_link [Boolean] (false) Whether to disable the link even if the picture has a link.
11
+ # @param html_options [Hash] Options that will be passed to the a tag.
12
+ def initialize(ingredient, disable_link: nil, html_options: {})
13
+ super(ingredient, html_options: html_options)
14
+ @disable_link = settings_value(:disable_link, value: disable_link, default: false)
15
+ end
16
+
17
+ def call
18
+ if disable_link?
19
+ dom_id.present? ? anchor : value
20
+ else
21
+ link_to(value, url_for(link), {
22
+ id: dom_id.presence,
23
+ title: link_title,
24
+ target: ((link_target == "blank") ? "_blank" : nil),
25
+ data: {link_target: link_target}
26
+ }.merge(html_options))
27
+ end
28
+ end
29
+
30
+ private
31
+
32
+ def anchor
33
+ content_tag(:a, value, {id: dom_id}.merge(html_options))
34
+ end
35
+
36
+ def disable_link?
37
+ link.blank? || disable_link
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,39 @@
1
+ module Alchemy
2
+ module Ingredients
3
+ class VideoView < BaseView
4
+ delegate :attachment, to: :ingredient
5
+
6
+ def call
7
+ content_tag(:video, html_options) do
8
+ tag(:source, src: src, type: attachment.file_mime_type)
9
+ end
10
+ end
11
+
12
+ def render?
13
+ !attachment.nil?
14
+ end
15
+
16
+ private
17
+
18
+ def src
19
+ alchemy.show_attachment_path(
20
+ attachment,
21
+ format: attachment.suffix
22
+ )
23
+ end
24
+
25
+ def html_options
26
+ {
27
+ controls: ingredient.controls,
28
+ autoplay: ingredient.autoplay,
29
+ loop: ingredient.loop,
30
+ muted: ingredient.muted,
31
+ playsinline: ingredient.playsinline,
32
+ preload: ingredient.preload.presence,
33
+ width: ingredient.width.presence,
34
+ height: ingredient.height.presence
35
+ }.merge(@html_options)
36
+ end
37
+ end
38
+ end
39
+ end
@@ -52,7 +52,7 @@ module Alchemy
52
52
  render_errors_or_redirect(
53
53
  @attachment,
54
54
  admin_attachments_path(search_filter_params),
55
- Alchemy.t("File successfully updated"),
55
+ Alchemy.t("File successfully updated")
56
56
  )
57
57
  end
58
58
  end
@@ -68,7 +68,7 @@ module Alchemy
68
68
  @attachment = Attachment.find(params[:id])
69
69
  send_file @attachment.file.path, {
70
70
  filename: @attachment.file_name,
71
- type: @attachment.file_mime_type,
71
+ type: @attachment.file_mime_type
72
72
  }
73
73
  end
74
74
 
@@ -77,7 +77,7 @@ module Alchemy
77
77
  def search_filter_params
78
78
  @_search_filter_params ||= params.except(*COMMON_SEARCH_FILTER_EXCLUDES + [:attachment]).permit(
79
79
  *common_search_filter_includes + [
80
- :form_field_id,
80
+ :form_field_id
81
81
  ]
82
82
  )
83
83
  end