alchemy_cms 6.0.2 → 6.0.5

Sign up to get free protection for your applications and to get access to all the features.
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