alchemy_cms 8.3.5 → 8.3.7

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bbbeeb181a57c8aa3b7093ff06d224379e907c822cc904966a60ea9e4fd47a9a
4
- data.tar.gz: 14534974daa1420fa07cd3af38c71ed2a983ef6a0952d4b0457f8805a441dab4
3
+ metadata.gz: c8f639a1d9089eea972ddfa6d299a072337dfe342441fddd6150cd2dfae8e47f
4
+ data.tar.gz: dcc66f199295ff7bffbe07e5e2c28f98e3867675066faa87a6142d745425bf5a
5
5
  SHA512:
6
- metadata.gz: d69c8d862b3301e857ed130f974be1106aef8fca47cf296571596e5121e51c42a5a8e81ae9421b8fe88b5765ddeecbc5ec07a5fe07944a4f1795e11477e0eb27
7
- data.tar.gz: 1aebb4ad39b71937f7168d4659e3f9318579c5e9ba13dde47b16108e8d27ec5e3c5f38302397d7561b6ed5136838f1460ecb54257106839073a817e11581688c
6
+ metadata.gz: 65edc33be6840411cefa89b7a01e99f9d63d556b80c7e3b51f8efe40eac5d75fb6fc9ed7622c9af74161ac80719834e1128dfb7e312e85db5cc0a1254a0f5bd1
7
+ data.tar.gz: 00254aaacad56b9fd1cd0ac085d676bb3907f4c69f73a0590ab6c0e1b43ae0f7ae0b59c83e84b953131e65a1937da917cbccecd14ec6447af3080921bd1a54fa
@@ -10,6 +10,8 @@ module Alchemy
10
10
  # name of an action to evaluate if the user can perform these action on the given object
11
11
  # @param [String, nil] :tooltip
12
12
  # show a tooltip around the button
13
+ # @param [Object, nil] :resource
14
+ # the resource of the row the action belongs to
13
15
  # @param [Lambda] :block
14
16
  # a block to include a button or a link
15
17
  #
@@ -30,15 +32,11 @@ module Alchemy
30
32
  <% end %>
31
33
  ERB
32
34
 
33
- def initialize(name = nil, tooltip = nil, &block)
35
+ def initialize(name = nil, tooltip = nil, resource = nil, &block)
34
36
  @name = name
35
37
  @tooltip = tooltip
36
- @block = block
37
- end
38
-
39
- def with_resource(resource)
40
38
  @resource = resource
41
- self
39
+ @block = block
42
40
  end
43
41
  end
44
42
  end
@@ -7,6 +7,8 @@ module Alchemy
7
7
  #
8
8
  # @param [String, nil] :css_classes
9
9
  # css classes that are show at the table cell
10
+ # @param [Object, nil] :resource
11
+ # the resource of the row the cell belongs to
10
12
  # @param [Lambda] :block
11
13
  # a block to include a button or a link
12
14
  #
@@ -19,14 +21,10 @@ module Alchemy
19
21
  </td>
20
22
  ERB
21
23
 
22
- def initialize(css_classes, &block)
24
+ def initialize(css_classes, resource = nil, &block)
23
25
  @css_classes = css_classes
24
- @block = block
25
- end
26
-
27
- def with_resource(resource)
28
26
  @resource = resource
29
- self
27
+ @block = block
30
28
  end
31
29
  end
32
30
  end
@@ -69,12 +69,12 @@ module Alchemy
69
69
  <% collection.each do |resource| %>
70
70
  <tr class="<%= cycle('even', 'odd') %>">
71
71
  <% cells.each do |cell| %>
72
- <%= render cell.with_resource(resource) %>
72
+ <%= render cell_for(cell, resource) %>
73
73
  <% end %>
74
74
  <% if actions? %>
75
75
  <td class="tools">
76
76
  <% actions.each do |action| %>
77
- <%= render action.with_resource(resource) %>
77
+ <%= render action_for(action, resource) %>
78
78
  <% end %>
79
79
  </td>
80
80
  <% end %>
@@ -134,6 +134,18 @@ module Alchemy
134
134
 
135
135
  private
136
136
 
137
+ # A fresh cell instance is built for every resource because ViewComponent
138
+ # instances are single-use and may not be rendered more than once.
139
+ def cell_for(cell, resource)
140
+ Cell.new(cell.css_classes, resource, &cell.block)
141
+ end
142
+
143
+ # A fresh action instance is built for every resource because ViewComponent
144
+ # instances are single-use and may not be rendered more than once.
145
+ def action_for(action, resource)
146
+ Action.new(action.name, action.tooltip, resource, &action.block)
147
+ end
148
+
137
149
  ##
138
150
  # if no cells are available the resource_helper will be used, to generate the
139
151
  # default attributes of the given resource
@@ -3,6 +3,8 @@ module Alchemy
3
3
  class RichtextView < BaseView
4
4
  attr_reader :plain_text
5
5
 
6
+ delegate :sanitizer_settings, to: :ingredient
7
+
6
8
  # @param ingredient [Alchemy::Ingredient]
7
9
  # @param plain_text [Boolean] (false) Whether to show as plain text or with markup
8
10
  def initialize(ingredient, plain_text: nil, html_options: {})
@@ -13,6 +15,8 @@ module Alchemy
13
15
  def call
14
16
  if plain_text
15
17
  ingredient.stripped_body
18
+ elsif sanitizer_settings.present?
19
+ sanitize(value.to_s, **sanitizer_settings)
16
20
  else
17
21
  value.to_s.html_safe
18
22
  end.html_safe
@@ -10,9 +10,11 @@ module Alchemy
10
10
  include Alchemy::Admin::PreviewTime
11
11
 
12
12
  before_action :load_element, only: [:update, :destroy, :collapse, :expand, :publish]
13
- authorize_resource class: Alchemy::Element
13
+ authorize_resource class: Alchemy::Element, except: [:index]
14
14
 
15
15
  def index
16
+ authorize! :edit_content, @page
17
+
16
18
  preloaded = Alchemy::ElementPreloader
17
19
  .new(page_version: @page_version)
18
20
  .call
@@ -6,7 +6,7 @@ module Alchemy
6
6
  before_action :authorize_access, only: [:move, :toggle_folded]
7
7
 
8
8
  def index
9
- @nodes = Node.all
9
+ @nodes = Node.accessible_by(current_ability, :index)
10
10
  @nodes = @nodes.includes(:parent)
11
11
  @nodes = @nodes.where(language_id: params[:language_id]) if params[:language_id]
12
12
  @nodes = @nodes.ransack(params[:filter]).result.order(:lft)
@@ -36,6 +36,10 @@ module Alchemy
36
36
  settings[:tinymce] || {}
37
37
  end
38
38
 
39
+ def sanitizer_settings
40
+ settings[:sanitizer] || {}
41
+ end
42
+
39
43
  private
40
44
 
41
45
  def strip_content
@@ -48,10 +52,6 @@ module Alchemy
48
52
  sanitizer_settings
49
53
  )
50
54
  end
51
-
52
- def sanitizer_settings
53
- settings[:sanitizer] || {}
54
- end
55
55
  end
56
56
  end
57
57
  end
@@ -20,6 +20,27 @@ module Alchemy
20
20
  foreign_key: :related_object_id,
21
21
  inverse_of: :related_object
22
22
 
23
+ # All nodes of the current site whose language is published.
24
+ scope :from_current_public_site, -> {
25
+ joins(:language)
26
+ .where(Alchemy::Language.table_name => {site_id: Alchemy::Current.site})
27
+ .merge(Alchemy::Language.published)
28
+ }
29
+
30
+ # Nodes an anonymous visitor may see: external links or nodes attached to a
31
+ # published, unrestricted page, scoped to the current site.
32
+ scope :available_to_guests, -> {
33
+ from_current_public_site.where(page: nil)
34
+ .or(from_current_public_site.where(page: Alchemy::Page.published.not_restricted))
35
+ }
36
+
37
+ # Like #available_to_guests but including nodes attached to restricted pages,
38
+ # which logged in members are allowed to see.
39
+ scope :available_to_members, -> {
40
+ from_current_public_site.where(page: nil)
41
+ .or(from_current_public_site.where(page: Alchemy::Page.published))
42
+ }
43
+
23
44
  before_validation :translate_root_menu_name, if: -> { root? }
24
45
  before_validation :set_menu_type_from_root, unless: -> { root? }
25
46
 
@@ -44,6 +44,11 @@ module Alchemy
44
44
  can :read, Alchemy::Page, Alchemy::Page.published.not_restricted.from_current_site do |p|
45
45
  p.public? && !p.restricted? && p.site == Alchemy::Current.site
46
46
  end
47
+
48
+ can :read, Alchemy::Node, Alchemy::Node.available_to_guests do |node|
49
+ node.language.public? && node.language.site_id == Alchemy::Current.site&.id &&
50
+ (node.page.nil? || (node.page.public? && !node.page.restricted?))
51
+ end
47
52
  end
48
53
  end
49
54
 
@@ -67,6 +72,11 @@ module Alchemy
67
72
  can :read, Alchemy::Page, Alchemy::Page.published.from_current_site do |p|
68
73
  p.public? && p.site == Alchemy::Current.site
69
74
  end
75
+
76
+ can :read, Alchemy::Node, Alchemy::Node.available_to_members do |node|
77
+ node.language.public? && node.language.site_id == Alchemy::Current.site&.id &&
78
+ (node.page.nil? || node.page.public?)
79
+ end
70
80
  end
71
81
  end
72
82
 
@@ -11,6 +11,17 @@ module Alchemy
11
11
  base.after_create_commit if: :svg? do
12
12
  SanitizeSvgJob.perform_later(self, file_accessor: :image_file)
13
13
  end
14
+
15
+ # The image file's dirty state is cleared by the time
16
+ # +after_update_commit+ runs, so capture it here to know whether the
17
+ # blob was actually replaced (rather than only its metadata updated).
18
+ base.before_update do
19
+ @image_file_replaced = image_file.changed?
20
+ end
21
+
22
+ base.after_update_commit if: -> { @image_file_replaced && svg? } do
23
+ SanitizeSvgJob.perform_later(self, file_accessor: :image_file)
24
+ end
14
25
  end
15
26
  end
16
27
 
@@ -20,6 +31,17 @@ module Alchemy
20
31
  base.after_create_commit if: :svg? do
21
32
  SanitizeSvgJob.perform_later(self, file_accessor: :file)
22
33
  end
34
+
35
+ # The file's dirty state is cleared by the time
36
+ # +after_update_commit+ runs, so capture it here to know whether the
37
+ # blob was actually replaced (rather than only its metadata updated).
38
+ base.before_update do
39
+ @file_replaced = file.changed?
40
+ end
41
+
42
+ base.after_update_commit if: -> { @file_replaced && svg? } do
43
+ SanitizeSvgJob.perform_later(self, file_accessor: :file)
44
+ end
23
45
  end
24
46
  end
25
47
 
@@ -17,7 +17,7 @@
17
17
  box-shadow: 0 0 0 1px #fff;
18
18
  box-sizing: border-box;
19
19
  border-bottom-right-radius: var(--border-radius);
20
- padding: 12px 16px 12px;
20
+ padding: 12px 16px 12px 12px;
21
21
  gap: 12px;
22
22
  justify-content: space-between;
23
23
  align-items: center;
@@ -26,6 +26,7 @@
26
26
  font-family: "Open Sans", "Lucida Grande", "Lucida Sans Unicode",
27
27
  "Lucida Sans", Verdana, Tahoma, sans-serif;
28
28
  font-size: 13px;
29
+ line-height: normal;
29
30
  }
30
31
 
31
32
  .menubar * {
@@ -107,7 +108,7 @@
107
108
  <% end %>
108
109
  <% end %>
109
110
  <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30 30">
110
- <path fill="#fff" d="M15.7 7.9L9.9 2.2 2.1 4.3 0 12.1l5.7 5.8 7.8-2.1 2.2-7.9zm-5.3 10.3l-1.2 4.4 3.2 3.2 4.4-1.2 1.2-4.4-3.2-3.2-4.4 1.2zM23.5 7.3L17.2 9l-1.7 6.2 4.5 4.6 6.2-1.7 1.7-6.2-4.4-4.6z"/>
111
+ <path fill="#fff" d="M15.7 7.9L9.9 2.2 2.1 4.3 0 12.1l5.7 5.8 7.8-2.1 2.2-7.9zm-5.3 10.3l-1.2 4.4 3.2 3.2 4.4-1.2 1.2-4.4-3.2-3.2-4.4 1.2zM23.5 7.3L17.2 9l-1.7 6.2 4.5 4.6 6.2-1.7 1.7-6.2-4.4-4.6z" />
111
112
  </svg>
112
113
  </div>
113
114
  </template>
@@ -178,13 +178,12 @@ module Alchemy
178
178
  end
179
179
 
180
180
  if app.config.active_storage
181
- # Rails sends svg images as attachment instead of inline.
182
- # We want to display svgs and not download them.
183
- unless app.config.active_storage.content_types_allowed_inline.include? svg
184
- app.config.active_storage.content_types_allowed_inline += [svg]
185
- end
186
- # Rails renders SVG as binary for security reasons.
187
- # We sanitize SVGs on upload and therefore can serve them as image.
181
+ # Serve SVGs with their real image/svg+xml content type so they render
182
+ # in <img> tags. We deliberately do NOT add svg to
183
+ # +content_types_allowed_inline+: an SVG opened as a top-level document
184
+ # executes its embedded scripts, so it must be served with an
185
+ # attachment (download) disposition rather than inline. <img> ignores
186
+ # Content-Disposition, so image display is unaffected.
188
187
  if app.config.active_storage.content_types_to_serve_as_binary.include? svg
189
188
  app.config.active_storage.content_types_to_serve_as_binary.delete(svg)
190
189
  end
@@ -111,7 +111,6 @@ module Alchemy
111
111
  def create_page(draft, attributes = {})
112
112
  children = draft.delete("children") || []
113
113
  page = Alchemy::Page.new(draft.merge(attributes))
114
- page.versions.build
115
114
  page.save!
116
115
  log "Created page: #{page.name}"
117
116
  children.each do |child|
@@ -3,7 +3,7 @@
3
3
  module Alchemy
4
4
  extend self
5
5
 
6
- VERSION = "8.3.5"
6
+ VERSION = "8.3.7"
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.3.5
4
+ version: 8.3.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Thomas von Deyen