alchemy_cms 7.0.0.pre.a → 7.0.0.pre.b
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/brakeman-analysis.yml +2 -2
- data/.github/workflows/ci.yml +7 -7
- data/.github/workflows/lint.yml +17 -0
- data/.hound.yml +2 -3
- data/.rubocop.yml +4 -350
- data/.standard.yml +3 -0
- data/CHANGELOG.md +29 -0
- data/Gemfile +3 -1
- data/README.md +7 -9
- data/Rakefile +1 -1
- data/alchemy_cms.gemspec +2 -1
- data/app/assets/javascripts/alchemy/admin.js +0 -1
- data/app/assets/javascripts/alchemy/alchemy.link_dialog.js.coffee +6 -1
- data/app/components/alchemy/ingredients/audio_view.rb +37 -0
- data/app/components/alchemy/ingredients/base_view.rb +38 -0
- data/app/components/alchemy/ingredients/boolean_view.rb +13 -0
- data/app/components/alchemy/ingredients/datetime_view.rb +22 -0
- data/app/components/alchemy/ingredients/file_view.rb +40 -0
- data/app/components/alchemy/ingredients/headline_view.rb +20 -0
- data/app/components/alchemy/ingredients/html_view.rb +9 -0
- data/app/components/alchemy/ingredients/link_view.rb +25 -0
- data/app/components/alchemy/ingredients/node_view.rb +11 -0
- data/app/components/alchemy/ingredients/page_view.rb +15 -0
- data/app/components/alchemy/ingredients/picture_view.rb +108 -0
- data/app/components/alchemy/ingredients/richtext_view.rb +22 -0
- data/app/components/alchemy/ingredients/select_view.rb +6 -0
- data/app/components/alchemy/ingredients/text_view.rb +41 -0
- data/app/components/alchemy/ingredients/video_view.rb +39 -0
- data/app/controllers/alchemy/admin/attachments_controller.rb +3 -3
- data/app/controllers/alchemy/admin/base_controller.rb +7 -7
- data/app/controllers/alchemy/admin/clipboard_controller.rb +2 -2
- data/app/controllers/alchemy/admin/elements_controller.rb +26 -11
- data/app/controllers/alchemy/admin/languages_controller.rb +1 -1
- data/app/controllers/alchemy/admin/nodes_controller.rb +2 -2
- data/app/controllers/alchemy/admin/pages_controller.rb +10 -10
- data/app/controllers/alchemy/admin/pictures_controller.rb +14 -14
- data/app/controllers/alchemy/admin/resources_controller.rb +27 -28
- data/app/controllers/alchemy/admin/styleguide_controller.rb +1 -0
- data/app/controllers/alchemy/admin/tags_controller.rb +11 -11
- data/app/controllers/alchemy/api/base_controller.rb +2 -2
- data/app/controllers/alchemy/api/elements_controller.rb +11 -11
- data/app/controllers/alchemy/api/ingredients_controller.rb +1 -1
- data/app/controllers/alchemy/api/nodes_controller.rb +1 -1
- data/app/controllers/alchemy/api/pages_controller.rb +11 -11
- data/app/controllers/alchemy/attachments_controller.rb +3 -3
- data/app/controllers/alchemy/base_controller.rb +1 -1
- data/app/controllers/alchemy/messages_controller.rb +9 -9
- data/app/controllers/alchemy/pages_controller.rb +8 -19
- data/app/controllers/concerns/alchemy/admin/archive_overlay.rb +1 -0
- data/app/controllers/concerns/alchemy/admin/uploader_responses.rb +5 -7
- data/app/controllers/concerns/alchemy/legacy_page_redirects.rb +5 -5
- data/app/decorators/alchemy/element_editor.rb +4 -4
- data/app/decorators/alchemy/ingredient_editor.rb +6 -6
- data/app/helpers/alchemy/admin/attachments_helper.rb +1 -1
- data/app/helpers/alchemy/admin/base_helper.rb +21 -22
- data/app/helpers/alchemy/admin/elements_helper.rb +1 -1
- data/app/helpers/alchemy/admin/form_helper.rb +1 -1
- data/app/helpers/alchemy/admin/navigation_helper.rb +7 -7
- data/app/helpers/alchemy/admin/pages_helper.rb +2 -2
- data/app/helpers/alchemy/admin/tags_helper.rb +3 -3
- data/app/helpers/alchemy/base_helper.rb +2 -2
- data/app/helpers/alchemy/elements_block_helper.rb +9 -7
- data/app/helpers/alchemy/elements_helper.rb +12 -12
- data/app/helpers/alchemy/pages_helper.rb +11 -11
- data/app/helpers/alchemy/url_helper.rb +1 -1
- data/app/mailers/alchemy/messages_mailer.rb +1 -1
- data/app/models/alchemy/attachment.rb +6 -6
- data/app/models/alchemy/base_record.rb +1 -0
- data/app/models/alchemy/eager_loading.rb +6 -6
- data/app/models/alchemy/element/definitions.rb +1 -1
- data/app/models/alchemy/element/element_ingredients.rb +3 -3
- data/app/models/alchemy/element.rb +2 -2
- data/app/models/alchemy/elements_repository.rb +1 -1
- data/app/models/alchemy/image_cropper_settings.rb +2 -2
- data/app/models/alchemy/ingredient.rb +14 -12
- data/app/models/alchemy/ingredient_validator.rb +1 -1
- data/app/models/alchemy/ingredients/datetime.rb +1 -1
- data/app/models/alchemy/ingredients/file.rb +5 -5
- data/app/models/alchemy/ingredients/headline.rb +4 -4
- data/app/models/alchemy/ingredients/picture.rb +27 -9
- data/app/models/alchemy/ingredients/richtext.rb +15 -12
- data/app/models/alchemy/ingredients/text.rb +6 -6
- data/app/models/alchemy/language/code.rb +1 -1
- data/app/models/alchemy/language.rb +4 -4
- data/app/models/alchemy/legacy_page_url.rb +1 -1
- data/app/models/alchemy/node.rb +2 -2
- data/app/models/alchemy/page/page_elements.rb +14 -14
- data/app/models/alchemy/page/page_naming.rb +4 -4
- data/app/models/alchemy/page/page_natures.rb +1 -1
- data/app/models/alchemy/page/page_scopes.rb +5 -5
- data/app/models/alchemy/page.rb +11 -11
- data/app/models/alchemy/picture/calculations.rb +2 -2
- data/app/models/alchemy/picture/transformations.rb +2 -2
- data/app/models/alchemy/picture/url.rb +4 -4
- data/app/models/alchemy/picture.rb +11 -10
- data/app/models/alchemy/picture_thumb/create.rb +1 -1
- data/app/models/alchemy/picture_thumb.rb +1 -1
- data/app/models/alchemy/picture_variant.rb +2 -3
- data/app/models/alchemy/tag.rb +8 -0
- data/app/models/concerns/alchemy/picture_thumbnails.rb +6 -6
- data/app/serializers/alchemy/base_serializer.rb +1 -1
- data/app/serializers/alchemy/page_tree_serializer.rb +7 -7
- data/app/services/alchemy/duplicate_element.rb +3 -3
- data/app/services/alchemy/tag_validations.rb +1 -1
- data/app/views/alchemy/admin/elements/_element.html.erb +3 -0
- data/app/views/alchemy/admin/pages/edit.html.erb +0 -3
- data/app/views/alchemy/admin/pages/update.js.erb +10 -4
- data/app/views/alchemy/admin/pictures/_infos.html.erb +1 -1
- data/app/views/alchemy/ingredients/_audio_view.html.erb +1 -14
- data/app/views/alchemy/ingredients/_boolean_view.html.erb +1 -1
- data/app/views/alchemy/ingredients/_datetime_view.html.erb +3 -9
- data/app/views/alchemy/ingredients/_file_view.html.erb +3 -16
- data/app/views/alchemy/ingredients/_headline_view.html.erb +4 -10
- data/app/views/alchemy/ingredients/_html_view.html.erb +1 -1
- data/app/views/alchemy/ingredients/_link_view.html.erb +4 -9
- data/app/views/alchemy/ingredients/_node_view.html.erb +1 -1
- data/app/views/alchemy/ingredients/_page_view.html.erb +1 -4
- data/app/views/alchemy/ingredients/_picture_view.html.erb +4 -5
- data/app/views/alchemy/ingredients/_richtext_editor.html.erb +11 -2
- data/app/views/alchemy/ingredients/_richtext_view.html.erb +3 -3
- data/app/views/alchemy/ingredients/_select_view.html.erb +1 -1
- data/app/views/alchemy/ingredients/_text_view.html.erb +3 -19
- data/app/views/alchemy/ingredients/_video_view.html.erb +3 -18
- data/app/views/alchemy/ingredients/shared/_link_tools.html.erb +1 -0
- data/app/views/alchemy/ingredients/shared/_picture_tools.html.erb +1 -0
- data/app/views/layouts/alchemy/admin.html.erb +7 -9
- data/bin/setup +37 -0
- data/bin/start +17 -0
- data/config/initializers/assets.rb +1 -0
- data/config/initializers/dragonfly.rb +1 -0
- data/config/initializers/mime_types.rb +1 -0
- data/config/initializers/mini_profiler.rb +1 -0
- data/config/initializers/simple_form.rb +3 -2
- data/config/locales/alchemy.en.yml +1 -1
- data/config/routes.rb +21 -20
- data/config/spring.rb +1 -0
- data/db/migrate/20230121212637_alchemy_six_point_one.rb +8 -8
- data/db/migrate/20230505132743_add_indexes_to_alchemy_pictures.rb +6 -0
- data/lib/alchemy/admin/locale.rb +3 -3
- data/lib/alchemy/admin/preview_url.rb +2 -2
- data/lib/alchemy/auth_accessors.rb +1 -1
- data/lib/alchemy/config.rb +1 -1
- data/lib/alchemy/controller_actions.rb +4 -4
- data/lib/alchemy/deprecation.rb +1 -0
- data/lib/alchemy/dragonfly/processors/thumbnail.rb +1 -1
- data/lib/alchemy/element_definition.rb +2 -2
- data/lib/alchemy/engine.rb +2 -1
- data/lib/alchemy/filetypes.rb +7 -7
- data/lib/alchemy/forms/builder.rb +4 -4
- data/lib/alchemy/i18n.rb +6 -4
- data/lib/alchemy/install/tasks.rb +2 -1
- data/lib/alchemy/name_conversions.rb +1 -1
- data/lib/alchemy/page_layout.rb +1 -1
- data/lib/alchemy/permissions.rb +5 -4
- data/lib/alchemy/resource.rb +10 -10
- data/lib/alchemy/resources_helper.rb +7 -7
- data/lib/alchemy/routing_constraints.rb +2 -2
- data/lib/alchemy/seeder.rb +12 -5
- data/lib/alchemy/shell.rb +2 -1
- data/lib/alchemy/taggable.rb +3 -2
- data/lib/alchemy/tasks/tidy.rb +1 -0
- data/lib/alchemy/test_support/capybara_helpers.rb +1 -1
- data/lib/alchemy/test_support/config_stubbing.rb +1 -0
- data/lib/alchemy/test_support/factories/element_factory.rb +4 -0
- data/lib/alchemy/test_support/factories/page_factory.rb +2 -2
- data/lib/alchemy/test_support/having_crop_action_examples.rb +9 -9
- data/lib/alchemy/test_support/having_picture_thumbnails_examples.rb +33 -33
- data/lib/alchemy/test_support/integration_helpers.rb +4 -3
- data/lib/alchemy/test_support/shared_contexts.rb +2 -1
- data/lib/alchemy/test_support/shared_dom_ids_examples.rb +9 -9
- data/lib/alchemy/test_support/shared_ingredient_examples.rb +12 -6
- data/lib/alchemy/test_support/shared_uploader_examples.rb +1 -0
- data/lib/alchemy/tinymce.rb +3 -26
- data/lib/alchemy/upgrader.rb +1 -0
- data/lib/alchemy/version.rb +1 -1
- data/lib/alchemy_cms.rb +1 -0
- data/lib/generators/alchemy/base.rb +3 -2
- data/lib/generators/alchemy/elements/elements_generator.rb +2 -1
- data/lib/generators/alchemy/ingredient/ingredient_generator.rb +1 -0
- data/lib/generators/alchemy/install/files/application.html.erb +1 -1
- data/lib/generators/alchemy/install/install_generator.rb +2 -1
- data/lib/generators/alchemy/module/module_generator.rb +1 -0
- data/lib/generators/alchemy/page_layouts/page_layouts_generator.rb +1 -0
- data/lib/generators/alchemy/site_layouts/site_layouts_generator.rb +1 -0
- data/lib/generators/alchemy/views/views_generator.rb +2 -1
- data/lib/tasks/alchemy/thumbnails.rake +5 -5
- data/lib/tasks/alchemy/tidy.rake +1 -0
- data/lib/tasks/alchemy/upgrade.rake +6 -5
- data/package/admin.js +2 -0
- data/package/dist/admin.js +3 -3
- data/package/dist/admin.js.map +4 -4
- data/package/src/datepicker.js +1 -0
- data/package/src/tinymce.js +142 -0
- data/package.json +2 -2
- metadata +39 -7
- data/app/assets/javascripts/alchemy/alchemy.tinymce.js.coffee +0 -93
- data/app/presenters/alchemy/picture_view.rb +0 -88
- data/app/views/alchemy/admin/pages/_tinymce_custom_config.html.erb +0 -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,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,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,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,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
|
@@ -54,7 +54,7 @@ module Alchemy
|
|
54
54
|
else
|
55
55
|
respond_to do |format|
|
56
56
|
format.html { render "500", status: 500 }
|
57
|
-
format.json { render json: {
|
57
|
+
format.json { render json: {message: @notice}, status: 500 }
|
58
58
|
end
|
59
59
|
end
|
60
60
|
end
|
@@ -101,7 +101,7 @@ module Alchemy
|
|
101
101
|
flash[:notice] = Alchemy.t(flash_notice)
|
102
102
|
do_redirect_to redirect_url
|
103
103
|
else
|
104
|
-
render action: (params[:action] == "update" ? "edit" : "new")
|
104
|
+
render action: ((params[:action] == "update") ? "edit" : "new")
|
105
105
|
end
|
106
106
|
end
|
107
107
|
|
@@ -138,11 +138,11 @@ module Alchemy
|
|
138
138
|
#
|
139
139
|
def current_alchemy_site
|
140
140
|
@current_alchemy_site ||= begin
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
141
|
+
site_id = params[:site_id] || session[:alchemy_site_id]
|
142
|
+
site = Site.find_by(id: site_id) || super
|
143
|
+
session[:alchemy_site_id] = site&.id
|
144
|
+
site
|
145
|
+
end
|
146
146
|
end
|
147
147
|
|
148
148
|
def notify_error_tracker(exception)
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module Alchemy
|
4
4
|
module Admin
|
5
5
|
class ClipboardController < Alchemy::Admin::BaseController
|
6
|
-
REMARKABLE_TYPES = %w
|
6
|
+
REMARKABLE_TYPES = %w[elements pages]
|
7
7
|
|
8
8
|
authorize_resource class: :alchemy_admin_clipboard
|
9
9
|
before_action :set_clipboard
|
@@ -20,7 +20,7 @@ module Alchemy
|
|
20
20
|
unless @clipboard.detect { |item| item["id"] == remarkable_params[:remarkable_id] }
|
21
21
|
@clipboard << {
|
22
22
|
"id" => remarkable_params[:remarkable_id],
|
23
|
-
"action" => params[:remove] ? "cut" : "copy"
|
23
|
+
"action" => params[:remove] ? "cut" : "copy"
|
24
24
|
}
|
25
25
|
end
|
26
26
|
respond_to do |format|
|
@@ -29,10 +29,11 @@ module Alchemy
|
|
29
29
|
@page_version = PageVersion.find(params[:element][:page_version_id])
|
30
30
|
@page = @page_version.page
|
31
31
|
Element.transaction do
|
32
|
-
|
33
|
-
|
32
|
+
@paste_from_clipboard = params[:paste_from_clipboard].present?
|
33
|
+
@element = if @paste_from_clipboard
|
34
|
+
paste_element_from_clipboard
|
34
35
|
else
|
35
|
-
|
36
|
+
Element.new(create_element_params)
|
36
37
|
end
|
37
38
|
if @page.definition["insert_elements_at"] == "top"
|
38
39
|
@insert_at_top = true
|
@@ -65,7 +66,7 @@ module Alchemy
|
|
65
66
|
def destroy
|
66
67
|
@richtext_ids = @element.richtext_ingredients_ids
|
67
68
|
@element.destroy
|
68
|
-
@notice = Alchemy.t("Successfully deleted element") % {
|
69
|
+
@notice = Alchemy.t("Successfully deleted element") % {element: @element.display_name}
|
69
70
|
end
|
70
71
|
|
71
72
|
def publish
|
@@ -80,7 +81,7 @@ module Alchemy
|
|
80
81
|
# element over from another nestable element
|
81
82
|
Element.find_by(id: element_id).update_columns(
|
82
83
|
parent_element_id: params[:parent_element_id],
|
83
|
-
position: position
|
84
|
+
position: position
|
84
85
|
)
|
85
86
|
end
|
86
87
|
# Need to manually touch the parent because Rails does not do it
|
@@ -95,24 +96,38 @@ module Alchemy
|
|
95
96
|
@page = @element.page
|
96
97
|
# We do not want to trigger the touch callback or any validations
|
97
98
|
@element.update_columns(folded: !@element.folded)
|
99
|
+
# Fold all nested elements if folded
|
100
|
+
if @element.folded?
|
101
|
+
ids = collapse_nested_elements_ids(@element)
|
102
|
+
Alchemy::Element.where(id: ids).update_all(folded: true)
|
103
|
+
end
|
98
104
|
end
|
99
105
|
|
100
106
|
private
|
101
107
|
|
108
|
+
def collapse_nested_elements_ids(element)
|
109
|
+
ids = []
|
110
|
+
element.all_nested_elements.includes(:all_nested_elements).reject(&:compact?).each do |nested_element|
|
111
|
+
ids.push nested_element.id if nested_element.expanded?
|
112
|
+
ids.concat collapse_nested_elements_ids(nested_element) if nested_element.all_nested_elements.reject(&:compact?).any?
|
113
|
+
end
|
114
|
+
ids
|
115
|
+
end
|
116
|
+
|
102
117
|
def element_includes
|
103
118
|
[
|
104
119
|
{
|
105
|
-
ingredients: :related_object
|
120
|
+
ingredients: :related_object
|
106
121
|
},
|
107
122
|
:tags,
|
108
123
|
{
|
109
124
|
all_nested_elements: [
|
110
125
|
{
|
111
|
-
ingredients: :related_object
|
126
|
+
ingredients: :related_object
|
112
127
|
},
|
113
|
-
:tags
|
114
|
-
]
|
115
|
-
}
|
128
|
+
:tags
|
129
|
+
]
|
130
|
+
}
|
116
131
|
]
|
117
132
|
end
|
118
133
|
|
@@ -138,7 +153,7 @@ module Alchemy
|
|
138
153
|
@source_element,
|
139
154
|
{
|
140
155
|
parent_element_id: create_element_params[:parent_element_id],
|
141
|
-
page_version_id: @page_version.id
|
156
|
+
page_version_id: @page_version.id
|
142
157
|
}
|
143
158
|
)
|
144
159
|
if element_from_clipboard["action"] == "cut"
|
@@ -12,7 +12,7 @@ module Alchemy
|
|
12
12
|
def new
|
13
13
|
@node = Node.new(
|
14
14
|
parent_id: params[:parent_id],
|
15
|
-
language: @current_language
|
15
|
+
language: @current_language
|
16
16
|
)
|
17
17
|
end
|
18
18
|
|
@@ -28,7 +28,7 @@ module Alchemy
|
|
28
28
|
:url,
|
29
29
|
:title,
|
30
30
|
:nofollow,
|
31
|
-
:external
|
31
|
+
:external
|
32
32
|
)
|
33
33
|
end
|
34
34
|
end
|