alchemy_cms 6.0.2 → 6.0.5

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.

Potentially problematic release.


This version of alchemy_cms might be problematic. Click here for more details.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7302c6c464ed0c57f1b2113a1d7334eb4234e80ee75a68f43579895207c8c406
4
- data.tar.gz: 26bd58edc3af02af5c418f082584cbfa897a16f26a670db08f147072df827a71
3
+ metadata.gz: c5eb21e30df422e1b214dd34d5d6166d8aaf195d393590efdf1e6a16a1247910
4
+ data.tar.gz: aa579d8246cfc7952a2f2491c86a78e272733eb7c21d993d4302201ee2b1a3f3
5
5
  SHA512:
6
- metadata.gz: edc87ff799f4940275d70cfc24f89c4af6dd7ef4f5e77eb3982d6a85a221cf158afa05874f7c996f7c2326fef6b4131a5b15b167daea7ae6b4c39a060ecbd28d
7
- data.tar.gz: b2a2d119a9c10c098b579a03548f9c26d49e61104be1d3058cd1d8e7329f432078258ee5f7f5e67f62d78bdf661588a72232f28e12e48ee9cb33568ae2625bfc
6
+ metadata.gz: 4b92efbc92492ccbc49ff95249672d5083499898d6ce70644b68a44fdc49720a716986c138211c308c1e3cf3a7b614db6d04a9463399c81c8167b31622aa1f5c
7
+ data.tar.gz: 4d297aea832eeea8b51183b234bcad0cacc168283e07f1d83ca180e1a59fb02601756869e74c3348f10b14308a62e660d6dd2b0a04251da51b4c574435e4518a
@@ -20,6 +20,7 @@ jobs:
20
20
  database:
21
21
  - mysql
22
22
  - postgresql
23
+ - mariadb
23
24
  exclude:
24
25
  - rails: "6.0"
25
26
  ruby: "3.1"
@@ -27,12 +28,18 @@ jobs:
27
28
  - rails: "6.0"
28
29
  ruby: "3.1"
29
30
  database: postgresql
31
+ - rails: "6.0"
32
+ ruby: "3.1"
33
+ database: mariadb
30
34
  - rails: "7.0"
31
35
  ruby: "2.6"
32
36
  database: mysql
33
37
  - rails: "7.0"
34
38
  ruby: "2.6"
35
39
  database: postgresql
40
+ - rails: "7.0"
41
+ ruby: "2.6"
42
+ database: mariadb
36
43
  env:
37
44
  DB: ${{ matrix.database }}
38
45
  DB_USER: alchemy_user
@@ -58,6 +65,15 @@ jobs:
58
65
  MYSQL_DATABASE: alchemy_cms_dummy_test
59
66
  MYSQL_ROOT_PASSWORD: password
60
67
  options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=5
68
+ mariadb:
69
+ image: mariadb:latest
70
+ ports: ["3307:3306"]
71
+ env:
72
+ MARIADB_USER: alchemy_user
73
+ MARIADB_PASSWORD: password
74
+ MARIADB_DATABASE: alchemy_cms_dummy_test
75
+ MARIADB_ROOT_PASSWORD: password
76
+ options: --health-cmd="mariadb-admin ping" --health-interval=10s --health-timeout=5s --health-retries=5
61
77
  steps:
62
78
  - uses: actions/checkout@v2.3.4
63
79
  - name: Set up Ruby
@@ -81,7 +97,7 @@ jobs:
81
97
  sudo apt install -qq --fix-missing libpq-dev -o dir::cache::archives="/home/runner/apt/cache"
82
98
  sudo chown -R runner /home/runner/apt/cache
83
99
  - name: Install MySQL headers
84
- if: matrix.database == 'mysql'
100
+ if: matrix.database == 'mysql' || matrix.database == 'mariadb'
85
101
  run: |
86
102
  mkdir -p /home/runner/apt/cache
87
103
  sudo apt update -qq
data/CHANGELOG.md CHANGED
@@ -1,3 +1,20 @@
1
+ ## 6.0.5 (2022-05-11)
2
+
3
+ - Extract element ingredient migrator [#2337](https://github.com/AlchemyCMS/alchemy_cms/pull/2337) ([tvdeyen](https://github.com/tvdeyen))
4
+
5
+ ## 6.0.4 (2022-05-06)
6
+
7
+ - Add support for Rails' recycable cache keys [#2334](https://github.com/AlchemyCMS/alchemy_cms/pull/2334) ([tvdeyen](https://github.com/tvdeyen))
8
+ - Make Alchemy::Content::Factory reloadable [#2333](https://github.com/AlchemyCMS/alchemy_cms/pull/2333) ([tvdeyen](https://github.com/tvdeyen))
9
+ - Wrap the result of rendering into an ActiveSupport::SafeBuffer [#2332](https://github.com/AlchemyCMS/alchemy_cms/pull/2332) ([mamhoff](https://github.com/mamhoff))
10
+ - Override Alchemy::Page.ransackable_scopes [#2328](https://github.com/AlchemyCMS/alchemy_cms/pull/2328) ([mamhoff](https://github.com/mamhoff))
11
+ - Default Link Format matcher: Allow tel: protocol [#2327](https://github.com/AlchemyCMS/alchemy_cms/pull/2327) ([mamhoff](https://github.com/mamhoff))
12
+
13
+ ## 6.0.3 (2022-05-02)
14
+
15
+ - Add Support for MariaDB [#2326](https://github.com/AlchemyCMS/alchemy_cms/pull/2326) ([mamhoff](https://github.com/mamhoff))
16
+ - Fix Alchemy::Content::Factory module definition [#2325](https://github.com/AlchemyCMS/alchemy_cms/pull/2325) ([tvdeyen](https://github.com/tvdeyen))
17
+
1
18
  ## 6.0.2 (2022-04-27)
2
19
 
3
20
  - Remove JSON decode from ingredient data store [#2323](https://github.com/AlchemyCMS/alchemy_cms/pull/2323) ([tvdeyen](https://github.com/tvdeyen))
data/Gemfile CHANGED
@@ -17,7 +17,9 @@ end
17
17
  if ENV["DB"].nil? || ENV["DB"] == "sqlite"
18
18
  gem "sqlite3", "~> 1.4.1"
19
19
  end
20
- gem "mysql2", "~> 0.5.1" if ENV["DB"] == "mysql"
20
+ if ENV["DB"] == "mysql" || ENV["DB"] == "mariadb"
21
+ gem "mysql2", "~> 0.5.1"
22
+ end
21
23
  gem "pg", "~> 1.0" if ENV["DB"] == "postgresql"
22
24
 
23
25
  group :development, :test do
@@ -87,11 +87,13 @@ module Alchemy
87
87
  elements = finder.elements(page_version: page_version)
88
88
 
89
89
  default_rendering = ->(element, i) { render_element(element, options, i + 1) }
90
- if block_given?
91
- elements.map.with_index(&blk)
92
- else
93
- elements.map.with_index(&default_rendering)
94
- end.join(options[:separator]).html_safe
90
+ capture do
91
+ if block_given?
92
+ elements.map.with_index(&blk)
93
+ else
94
+ elements.map.with_index(&default_rendering)
95
+ end.join(options[:separator]).html_safe
96
+ end
95
97
  end
96
98
 
97
99
  # This helper renders a {Alchemy::Element} view partial.
@@ -3,139 +3,141 @@
3
3
  module Alchemy
4
4
  # Holds everything concerning the building and creating of contents and the related essence object.
5
5
  #
6
- module Content::Factory
7
- extend ActiveSupport::Concern
6
+ class Content < BaseRecord
7
+ module Factory
8
+ extend ActiveSupport::Concern
9
+
10
+ module ClassMethods
11
+ SKIPPED_ATTRIBUTES_ON_COPY = %w(position created_at updated_at creator_id updater_id element_id id)
12
+
13
+ # Builds a new content as descriped in the elements.yml file.
14
+ #
15
+ # @param [Hash]
16
+ # The content definition used for finding the content in +elements.yml+ file
17
+ #
18
+ def new(attributes = {})
19
+ element = attributes[:element] || Element.find_by(id: attributes[:element_id])
20
+ return super if attributes.empty? || element.nil?
21
+
22
+ definition = element.content_definition_for(attributes[:name])
23
+ if definition.blank? && attributes[:essence_type].nil?
24
+ raise ContentDefinitionError, "No definition found in elements.yml for #{attributes.inspect} and #{element.inspect}"
25
+ end
26
+
27
+ super(
28
+ name: attributes[:name],
29
+ essence_type: attributes[:essence_type] || normalize_essence_type(definition[:type]),
30
+ element: element,
31
+ ).tap(&:build_essence)
32
+ end
8
33
 
9
- module ClassMethods
10
- SKIPPED_ATTRIBUTES_ON_COPY = %w(position created_at updated_at creator_id updater_id element_id id)
34
+ # Creates a new content from elements definition in the +elements.yml+ file.
35
+ #
36
+ # 1. It builds the content
37
+ # 2. It creates the essence record (content object gets saved)
38
+ #
39
+ # @return [Alchemy::Content]
40
+ #
41
+ def create(attributes = {})
42
+ new(attributes).tap do |content|
43
+ content.essence.save && content.save
44
+ end
45
+ end
11
46
 
12
- # Builds a new content as descriped in the elements.yml file.
13
- #
14
- # @param [Hash]
15
- # The content definition used for finding the content in +elements.yml+ file
16
- #
17
- def new(attributes = {})
18
- element = attributes[:element] || Element.find_by(id: attributes[:element_id])
19
- return super if attributes.empty? || element.nil?
47
+ # Creates a copy of source and also copies the associated essence.
48
+ #
49
+ # You can pass a differences hash to update the attributes of the copy.
50
+ #
51
+ # === Example
52
+ #
53
+ # @copy = Alchemy::Content.copy(@content, {element_id: 3})
54
+ # @copy.element_id # => 3
55
+ #
56
+ def copy(source, differences = {})
57
+ Content.new(
58
+ source.attributes.with_indifferent_access.
59
+ except(*SKIPPED_ATTRIBUTES_ON_COPY).
60
+ merge(differences.with_indifferent_access)
61
+ ).tap do |new_content|
62
+ new_content.build_essence(
63
+ source.essence.attributes.
64
+ except(*SKIPPED_ATTRIBUTES_ON_COPY)
65
+ )
66
+ new_content.save
67
+ end
68
+ end
20
69
 
21
- definition = element.content_definition_for(attributes[:name])
22
- if definition.blank? && attributes[:essence_type].nil?
23
- raise ContentDefinitionError, "No definition found in elements.yml for #{attributes.inspect} and #{element.inspect}"
70
+ # Returns all content definitions from elements.yml
71
+ #
72
+ def definitions
73
+ definitions = Element.definitions.flat_map { |e| e["contents"] }
74
+ definitions.compact!
75
+ definitions
24
76
  end
25
77
 
26
- super(
27
- name: attributes[:name],
28
- essence_type: attributes[:essence_type] || normalize_essence_type(definition[:type]),
29
- element: element
30
- ).tap(&:build_essence)
31
- end
78
+ # Returns a normalized Essence type
79
+ #
80
+ # Adds Alchemy module name in front of given essence type
81
+ # unless there is a Class with the specified name that is an essence.
82
+ #
83
+ # @param [String]
84
+ # the essence type to normalize
85
+ #
86
+ def normalize_essence_type(essence_type)
87
+ essence_type = essence_type.classify
88
+ return essence_type if is_an_essence?(essence_type)
89
+
90
+ "Alchemy::#{essence_type}"
91
+ end
32
92
 
33
- # Creates a new content from elements definition in the +elements.yml+ file.
34
- #
35
- # 1. It builds the content
36
- # 2. It creates the essence record (content object gets saved)
37
- #
38
- # @return [Alchemy::Content]
39
- #
40
- def create(attributes = {})
41
- new(attributes).tap do |content|
42
- content.essence.save && content.save
93
+ private
94
+
95
+ def is_an_essence?(essence_type)
96
+ klass = Module.const_get(essence_type)
97
+ klass.is_a?(Class) && klass.new.acts_as_essence?
98
+ rescue NameError
99
+ false
43
100
  end
44
101
  end
45
102
 
46
- # Creates a copy of source and also copies the associated essence.
47
- #
48
- # You can pass a differences hash to update the attributes of the copy.
49
- #
50
- # === Example
51
- #
52
- # @copy = Alchemy::Content.copy(@content, {element_id: 3})
53
- # @copy.element_id # => 3
103
+ # Instance Methods
104
+
105
+ # Returns the definition hash from +elements.yml+ file.
54
106
  #
55
- def copy(source, differences = {})
56
- Content.new(
57
- source.attributes.with_indifferent_access.
58
- except(*SKIPPED_ATTRIBUTES_ON_COPY).
59
- merge(differences.with_indifferent_access)
60
- ).tap do |new_content|
61
- new_content.build_essence(
62
- source.essence.attributes.
63
- except(*SKIPPED_ATTRIBUTES_ON_COPY)
64
- )
65
- new_content.save
107
+ def definition
108
+ if element.blank?
109
+ log_warning "Content with id #{id} is missing its Element."
110
+ return {}
66
111
  end
112
+ element.content_definition_for(name) || {}
67
113
  end
68
114
 
69
- # Returns all content definitions from elements.yml
115
+ # Build essence from definition.
70
116
  #
71
- def definitions
72
- definitions = Element.definitions.flat_map { |e| e["contents"] }
73
- definitions.compact!
74
- definitions
117
+ # If an optional type is passed, this type of essence gets created.
118
+ #
119
+ def build_essence(attributes = {})
120
+ self.essence = essence_class.new(
121
+ { content: self, ingredient: default_value }.merge(attributes)
122
+ )
75
123
  end
76
124
 
77
- # Returns a normalized Essence type
78
- #
79
- # Adds Alchemy module name in front of given essence type
80
- # unless there is a Class with the specified name that is an essence.
125
+ # Creates essence from definition.
81
126
  #
82
- # @param [String]
83
- # the essence type to normalize
127
+ # If an optional type is passed, this type of essence gets created.
84
128
  #
85
- def normalize_essence_type(essence_type)
86
- essence_type = essence_type.classify
87
- return essence_type if is_an_essence?(essence_type)
88
-
89
- "Alchemy::#{essence_type}"
129
+ def create_essence!(attrs = {})
130
+ build_essence(attrs).save!
131
+ save!
90
132
  end
91
133
 
92
134
  private
93
135
 
94
- def is_an_essence?(essence_type)
95
- klass = Module.const_get(essence_type)
96
- klass.is_a?(Class) && klass.new.acts_as_essence?
97
- rescue NameError
98
- false
99
- end
100
- end
101
-
102
- # Instance Methods
103
-
104
- # Returns the definition hash from +elements.yml+ file.
105
- #
106
- def definition
107
- if element.blank?
108
- log_warning "Content with id #{id} is missing its Element."
109
- return {}
136
+ # Returns a class constant from definition's type field or the essence_type column
137
+ #
138
+ def essence_class
139
+ (essence_type || Content.normalize_essence_type(definition["type"])).constantize
110
140
  end
111
- element.content_definition_for(name) || {}
112
- end
113
-
114
- # Build essence from definition.
115
- #
116
- # If an optional type is passed, this type of essence gets created.
117
- #
118
- def build_essence(attributes = {})
119
- self.essence = essence_class.new(
120
- { content: self, ingredient: default_value }.merge(attributes)
121
- )
122
- end
123
-
124
- # Creates essence from definition.
125
- #
126
- # If an optional type is passed, this type of essence gets created.
127
- #
128
- def create_essence!(attrs = {})
129
- build_essence(attrs).save!
130
- save!
131
- end
132
-
133
- private
134
-
135
- # Returns a class constant from definition's type field or the essence_type column
136
- #
137
- def essence_class
138
- (essence_type || Content.normalize_essence_type(definition["type"])).constantize
139
141
  end
140
142
  end
141
143
  end
@@ -16,13 +16,15 @@
16
16
  # updater_id :integer
17
17
  #
18
18
 
19
+ require_dependency "alchemy/content/factory"
20
+
19
21
  module Alchemy
20
22
  class Content < BaseRecord
21
23
  include Alchemy::Logger
22
24
  include Alchemy::Hints
23
25
 
24
26
  # Concerns
25
- include Alchemy::Content::Factory
27
+ include Factory
26
28
 
27
29
  belongs_to :essence, polymorphic: true, dependent: :destroy, inverse_of: :content
28
30
  belongs_to :element, touch: true, inverse_of: :contents
@@ -8,6 +8,8 @@ module Alchemy
8
8
 
9
9
  self.table_name = "alchemy_ingredients"
10
10
 
11
+ attribute :data, :json
12
+
11
13
  belongs_to :element, touch: true, class_name: "Alchemy::Element", inverse_of: :ingredients
12
14
  belongs_to :related_object, polymorphic: true, optional: true
13
15
 
@@ -103,17 +103,17 @@ module Alchemy
103
103
  page_layout.parameterize.underscore
104
104
  end
105
105
 
106
- # Returns the key that's taken for cache path.
106
+ # Returns the version that's taken for Rails' recycable cache key.
107
107
  #
108
108
  # Uses the +published_at+ value that's updated when the user publishes the page.
109
109
  #
110
- # If the page is the current preview it uses the updated_at value as cache key.
110
+ # If the page is the current preview it uses the +updated_at+ value as cache key.
111
111
  #
112
- def cache_key
112
+ def cache_version
113
113
  if Page.current_preview == id
114
- "alchemy/pages/#{id}-#{updated_at}"
114
+ updated_at.to_s
115
115
  else
116
- "alchemy/pages/#{id}-#{published_at}"
116
+ published_at.to_s
117
117
  end
118
118
  end
119
119
 
@@ -122,6 +122,10 @@ module Alchemy
122
122
  )
123
123
  SQL
124
124
  end
125
+
126
+ def ransackable_scopes(_auth_object)
127
+ [:published, :from_current_site, :searchables, :layoutpages]
128
+ end
125
129
  end
126
130
  end
127
131
  end
@@ -200,7 +200,7 @@ link_target_options: [blank]
200
200
  format_matchers:
201
201
  email: !ruby/regexp '/\A[^@\s]+@([^@\s]+\.)+[^@\s]+\z/'
202
202
  url: !ruby/regexp '/\A[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}(:[0-9]{1,5})?(\/.*)?\z/ix'
203
- link_url: !ruby/regexp '/^(mailto:|\/|[a-z]+:\/\/)/'
203
+ link_url: !ruby/regexp '/^(tel:|mailto:|\/|[a-z]+:\/\/)/'
204
204
 
205
205
  # The layout used for rendering the +alchemy/admin/pages#show+ action.
206
206
  admin_page_preview_layout: application
@@ -24,31 +24,8 @@ module Alchemy::Upgrader::Tasks
24
24
  if elements.any?
25
25
  puts "-- Creating ingredients for #{elements.count} #{element_definition[:name]}(s)"
26
26
  elements.each do |element|
27
- Alchemy::Element.transaction do
28
- element_definition[:ingredients].each do |ingredient_definition|
29
- content = element.content_by_name(ingredient_definition[:role])
30
- next unless content
31
-
32
- essence = content.essence
33
- ingredient = element.ingredients.build(
34
- role: ingredient_definition[:role],
35
- type: Alchemy::Ingredient.normalize_type(ingredient_definition[:type]),
36
- )
37
- belongs_to_associations = essence.class.reflect_on_all_associations(:belongs_to)
38
- if belongs_to_associations.any?
39
- ingredient.related_object = essence.public_send(belongs_to_associations.first.name)
40
- else
41
- ingredient.value = content.ingredient
42
- end
43
- data = ingredient.class.stored_attributes.fetch(:data, []).each_with_object({}) do |attr, d|
44
- d[attr] = essence.public_send(attr)
45
- end
46
- ingredient.data = data
47
- print "."
48
- ingredient.save!
49
- content.destroy!
50
- end
51
- end
27
+ MigrateElementIngredients.call(element)
28
+ print "."
52
29
  end
53
30
  puts "\n"
54
31
  else
@@ -58,5 +35,38 @@ module Alchemy::Upgrader::Tasks
58
35
  end
59
36
  end
60
37
  end
38
+
39
+ class MigrateElementIngredients
40
+ def self.call(element)
41
+ Alchemy::Element.transaction do
42
+ element.definition[:ingredients].each do |ingredient_definition|
43
+ ingredient = element.ingredients.build(
44
+ role: ingredient_definition[:role],
45
+ type: Alchemy::Ingredient.normalize_type(ingredient_definition[:type]),
46
+ )
47
+
48
+ content = element.content_by_name(ingredient_definition[:role])
49
+ if content
50
+ essence = content.essence
51
+ if essence
52
+ belongs_to_associations = essence.class.reflect_on_all_associations(:belongs_to)
53
+ if belongs_to_associations.any?
54
+ ingredient.related_object = essence.public_send(belongs_to_associations.first.name)
55
+ else
56
+ ingredient.value = content.ingredient
57
+ end
58
+ data = ingredient.class.stored_attributes.fetch(:data, []).each_with_object({}) do |attr, d|
59
+ d[attr] = essence.public_send(attr)
60
+ end
61
+ ingredient.data = data
62
+ end
63
+ content.destroy!
64
+ end
65
+
66
+ ingredient.save!
67
+ end
68
+ end
69
+ end
70
+ end
61
71
  end
62
72
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Alchemy
4
- VERSION = "6.0.2"
4
+ VERSION = "6.0.5"
5
5
 
6
6
  def self.version
7
7
  VERSION
data/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alchemy_cms/admin",
3
- "version": "6.0.2",
3
+ "version": "6.0.5",
4
4
  "description": "AlchemyCMS",
5
5
  "browser": "package/admin.js",
6
6
  "files": [
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: 6.0.2
4
+ version: 6.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Thomas von Deyen
@@ -13,7 +13,7 @@ authors:
13
13
  autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
- date: 2022-04-27 00:00:00.000000000 Z
16
+ date: 2022-05-11 00:00:00.000000000 Z
17
17
  dependencies:
18
18
  - !ruby/object:Gem::Dependency
19
19
  name: actionmailer