alchemy_cms 8.4.3 → 8.4.4

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.
@@ -1 +1 @@
1
- !function(){"use strict";const e={outline:"","outline-offset":"",cursor:""},t={outline:"2px dashed #f0b437","outline-offset":"4px",cursor:"pointer"},n={outline:"2px dashed #90b9d0","outline-offset":"4px"};let o=[],s=null;function a(e){i(e),s=e,Object.assign(e.style,n),e.scrollIntoView({behavior:"smooth",block:"start"})}function i(t){o.forEach(n=>{n!==t&&Object.assign(n.style,e)}),s=null}function c(e){const t=(n=e.element_id,o.find(e=>e.dataset.alchemyElement===n.toString()));var n;t?a(t):console.warn("Could not focus element with id",e.element_id)}function l(n){n.addEventListener("mouseover",()=>{n!==s&&Object.assign(n.style,t)}),n.addEventListener("mouseout",()=>{n!==s&&Object.assign(n.style,e)})}function d(e){const t=e.target.closest("[data-alchemy-element]");return t&&(a(t),function(e){window.parent.postMessage({message:"Alchemy.focusElementEditor",element_id:e.dataset.alchemyElement},window.location.origin)}(t)),t}function r(e){(function(e){if(e.closest("a"))return!0;const t=e.closest("button, input");return Boolean(t?.form)&&["submit","image"].includes(t.type)})(e.target)&&d(e)&&(e.stopPropagation(),e.preventDefault())}function u(){window.addEventListener("message",e=>{switch(e.data.message){case"Alchemy.blurElements":i();break;case"Alchemy.focusElement":c(e.data);break;default:console.info("Received unknown message!",e.data)}}),window.addEventListener("click",r,{capture:!0}),window.addEventListener("click",d),o=Array.from(document.querySelectorAll("[data-alchemy-element]")),o.forEach(l),window.parent.postMessage({message:"Alchemy.previewReady"},window.location.origin)}"loading"===document.readyState?document.addEventListener("DOMContentLoaded",u,{once:!0}):u()}();
1
+ !function(){"use strict";const e={outline:"","outline-offset":"",cursor:""},t={outline:"2px dashed #f0b437","outline-offset":"4px",cursor:"pointer"},n={outline:"2px dashed #90b9d0","outline-offset":"4px"};let o=[],s=null;function a(e){i(e),s=e,Object.assign(e.style,n),e.scrollIntoView({behavior:"smooth",block:"start"})}function i(t){o.forEach(n=>{n!==t&&Object.assign(n.style,e)}),s=null}function c(e){const t=(n=e.element_id,o.find(e=>e.dataset.alchemyElement===n.toString()));var n;t?a(t):console.warn("Could not focus element with id",e.element_id)}function l(n){n.addEventListener("mouseover",()=>{n!==s&&Object.assign(n.style,t)}),n.addEventListener("mouseout",()=>{n!==s&&Object.assign(n.style,e)})}function d(e){const t=e.target.closest("[data-alchemy-element]");return t&&(a(t),function(e){window.parent.postMessage({message:"Alchemy.focusElementEditor",element_id:e.dataset.alchemyElement},window.location.origin)}(t)),t}function r(e){(function(e){if(e.closest("a"))return!0;const t=e.closest("button, input");return Boolean(t?.form)&&["submit","image"].includes(t.type)})(e.target)&&(d(e),e.stopPropagation(),e.preventDefault())}function u(){window.addEventListener("message",e=>{switch(e.data.message){case"Alchemy.blurElements":i();break;case"Alchemy.focusElement":c(e.data);break;default:console.info("Received unknown message!",e.data)}}),window.addEventListener("click",r,{capture:!0}),window.addEventListener("click",d),window.addEventListener("turbo:before-visit",e=>e.preventDefault()),o=Array.from(document.querySelectorAll("[data-alchemy-element]")),o.forEach(l),window.parent.postMessage({message:"Alchemy.previewReady"},window.location.origin)}"loading"===document.readyState?document.addEventListener("DOMContentLoaded",u,{once:!0}):u()}();
@@ -43,7 +43,7 @@ module Alchemy
43
43
  end
44
44
 
45
45
  def call
46
- return if picture.blank?
46
+ return if picture.blank? || src.nil?
47
47
 
48
48
  output = caption ? img_tag + caption : img_tag
49
49
 
@@ -72,7 +72,9 @@ module Alchemy
72
72
  end
73
73
 
74
74
  def src
75
- ingredient.picture_url(picture_options)
75
+ return @_src if defined?(@_src)
76
+
77
+ @_src = ingredient.picture_url(picture_options)
76
78
  end
77
79
 
78
80
  def img_tag
@@ -101,7 +101,8 @@ function navigates(target) {
101
101
  // Client side routers navigate from click handlers on the link itself, so
102
102
  // navigating clicks have to be taken before the event reaches the page.
103
103
  function onNavigatingClick(event) {
104
- if (navigates(event.target) && selectClickedElement(event)) {
104
+ if (navigates(event.target)) {
105
+ selectClickedElement(event)
105
106
  event.stopPropagation()
106
107
  event.preventDefault()
107
108
  }
@@ -123,6 +124,10 @@ function init() {
123
124
 
124
125
  window.addEventListener("click", onNavigatingClick, { capture: true })
125
126
  window.addEventListener("click", selectClickedElement)
127
+ // Turbo.visit can be called from any click handler, not only from links.
128
+ window.addEventListener("turbo:before-visit", (event) =>
129
+ event.preventDefault()
130
+ )
126
131
 
127
132
  elements = Array.from(document.querySelectorAll("[data-alchemy-element]"))
128
133
  elements.forEach(observeElement)
@@ -11,6 +11,7 @@ module Alchemy
11
11
 
12
12
  all_element_ids = get_all_element_ids(elements, element_ids)
13
13
  Element.where(id: all_element_ids).touch_all
14
+ PageVersion.scheduled.where(id: elements.select(:page_version_id)).touch_all
14
15
 
15
16
  page_ids = elements.joins(page_version: :page).distinct.pluck("alchemy_pages.id")
16
17
  Page.where(id: page_ids).touch_all
@@ -453,14 +453,17 @@ module Alchemy
453
453
  # Returns true if the draft version has changes not yet published.
454
454
  #
455
455
  # Compares the updated_at timestamp of the draft_version against the
456
- # updated_at timestamp of the public_version. If there's no public_version,
457
- # the page has never been published and always has unpublished changes.
456
+ # page's published_at timestamp. The public_version's updated_at is not
457
+ # used, because it gets touched to invalidate caches. It only serves as
458
+ # fallback for public versions created without publishing (via +public_on=+).
459
+ # If there's no public_version, the page has never been published and
460
+ # always has unpublished changes.
458
461
  #
459
462
  # @return [Boolean]
460
463
  def has_unpublished_changes?
461
464
  return true unless public_version
462
465
 
463
- draft_version.updated_at > public_version.updated_at
466
+ draft_version.updated_at > (published_at || public_version.updated_at)
464
467
  end
465
468
 
466
469
  # Sets the public_on date on the published version
@@ -158,6 +158,12 @@ module Alchemy
158
158
  picture.image_file&.variable?
159
159
  end
160
160
 
161
+ # @param [String, Symbol] format file extension
162
+ # @return [TrueClass, FalseClass]
163
+ def can_convert_to?(format)
164
+ Marcel::MimeType.for(extension: format.to_s).in?(::ActiveStorage.variable_content_types)
165
+ end
166
+
161
167
  # @param [Alchemy::Picture]
162
168
  # @return [String]
163
169
  def image_file_basename(picture)
@@ -164,6 +164,12 @@ module Alchemy
164
164
  image_file_extension(picture).in?(CONVERTIBLE_FILE_FORMATS)
165
165
  end
166
166
 
167
+ # @param [String, Symbol] format file extension
168
+ # @return [TrueClass, FalseClass]
169
+ def can_convert_to?(format)
170
+ format.to_s.in?(CONVERTIBLE_FILE_FORMATS)
171
+ end
172
+
167
173
  # @param [Alchemy::Picture]
168
174
  # @return [String]
169
175
  def image_file_basename(picture)
@@ -15,6 +15,7 @@ module Alchemy
15
15
  :file_mime_type,
16
16
  :file_name,
17
17
  :file_size,
18
+ :can_convert_to?,
18
19
  :has_convertible_format?,
19
20
  :image_file_basename,
20
21
  :image_file_extension,
@@ -33,11 +33,13 @@ module Alchemy
33
33
  # @option options crop [Boolean]
34
34
  # If set to true the picture will be cropped to fit the size value.
35
35
  #
36
- # @return [String]
36
+ # @return [String, nil] +nil+ if no picture is assigned or the url could not be generated
37
37
  def picture_url(options = {})
38
38
  return if picture.nil?
39
39
 
40
- picture.url(picture_url_options(options)) || "missing-image.png"
40
+ url = picture.url(picture_url_options(options))
41
+ Logger.warn("Could not generate url for picture #{picture.id}") if url.nil?
42
+ url
41
43
  end
42
44
 
43
45
  # Picture rendering options
@@ -47,7 +49,7 @@ module Alchemy
47
49
  #
48
50
  # The render format is taken from the +format+ option, the +format+ setting
49
51
  # of the ingredient definition or the +default_render_format+ of the picture.
50
- # A format is only applied if the picture can be converted.
52
+ # A format is only applied if the picture can be converted into it.
51
53
  #
52
54
  # @param options [Hash] Options that override the defaults (i.e. +size+ or +format+ of a srcset entry)
53
55
  #
@@ -157,7 +159,8 @@ module Alchemy
157
159
  private
158
160
 
159
161
  def picture_render_format(format)
160
- if format.present? && picture.has_convertible_format?
162
+ if format.present? && picture.has_convertible_format? &&
163
+ Alchemy.storage_adapter.can_convert_to?(format)
161
164
  format.to_s
162
165
  else
163
166
  picture.default_render_format
@@ -138,7 +138,7 @@ form {
138
138
  }
139
139
  }
140
140
 
141
- input:invalid:focus {
141
+ input:user-invalid:focus {
142
142
  @extend %field-with-error;
143
143
  }
144
144
 
@@ -103,8 +103,9 @@ RSpec.shared_examples_for "having picture thumbnails" do
103
103
  expect(picture).to receive(:url) { nil }
104
104
  end
105
105
 
106
- it "returns missing image url" do
107
- is_expected.to eq "missing-image.png"
106
+ it "returns nil and logs a warning" do
107
+ expect(Alchemy::Logger).to receive(:warn).with(/Could not generate url for picture/)
108
+ is_expected.to be_nil
108
109
  end
109
110
  end
110
111
  end
@@ -204,6 +205,17 @@ RSpec.shared_examples_for "having picture thumbnails" do
204
205
  expect(picture_url_options[:format]).to eq "png"
205
206
  end
206
207
  end
208
+
209
+ context "with format the picture can not be converted to" do
210
+ before do
211
+ allow(record).to receive(:settings) { {format: "svg"} }
212
+ allow(picture).to receive(:default_render_format) { "webp" }
213
+ end
214
+
215
+ it "includes the pictures default render format" do
216
+ expect(picture_url_options[:format]).to eq "webp"
217
+ end
218
+ end
207
219
  end
208
220
 
209
221
  context "with picture not having a convertible format" do
@@ -3,7 +3,7 @@
3
3
  module Alchemy
4
4
  extend self
5
5
 
6
- VERSION = "8.4.3"
6
+ VERSION = "8.4.4"
7
7
 
8
8
  def version
9
9
  VERSION
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: alchemy_cms
3
3
  version: !ruby/object:Gem::Version
4
- version: 8.4.3
4
+ version: 8.4.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Thomas von Deyen