alchemy_cms 6.0.0.b2 → 6.0.0.pre.b6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +29 -0
  3. data/README.md +14 -2
  4. data/Rakefile +37 -23
  5. data/app/assets/stylesheets/alchemy/_extends.scss +15 -2
  6. data/app/assets/stylesheets/alchemy/archive.scss +2 -1
  7. data/app/assets/stylesheets/alchemy/fonts.scss +0 -0
  8. data/app/assets/stylesheets/tinymce/skins/alchemy/fonts/tinymce-small.svg +0 -0
  9. data/app/assets/stylesheets/tinymce/skins/alchemy/fonts/tinymce-small.ttf +0 -0
  10. data/app/assets/stylesheets/tinymce/skins/alchemy/fonts/tinymce-small.woff +0 -0
  11. data/app/assets/stylesheets/tinymce/skins/alchemy/fonts/tinymce.svg +0 -0
  12. data/app/assets/stylesheets/tinymce/skins/alchemy/fonts/tinymce.ttf +0 -0
  13. data/app/assets/stylesheets/tinymce/skins/alchemy/fonts/tinymce.woff +0 -0
  14. data/app/assets/stylesheets/tinymce/skins/alchemy/img/anchor.gif +0 -0
  15. data/app/assets/stylesheets/tinymce/skins/alchemy/img/loader.gif +0 -0
  16. data/app/assets/stylesheets/tinymce/skins/alchemy/img/object.gif +0 -0
  17. data/app/assets/stylesheets/tinymce/skins/alchemy/img/trans.gif +0 -0
  18. data/app/assets/stylesheets/tinymce/skins/alchemy/skin.min.css.scss +0 -0
  19. data/app/controllers/alchemy/admin/elements_controller.rb +9 -4
  20. data/app/decorators/alchemy/element_editor.rb +7 -4
  21. data/app/decorators/alchemy/ingredient_editor.rb +5 -1
  22. data/app/helpers/alchemy/elements_block_helper.rb +14 -6
  23. data/app/models/alchemy/element/element_essences.rb +14 -3
  24. data/app/models/alchemy/element/element_ingredients.rb +11 -3
  25. data/app/models/alchemy/element/presenters.rb +9 -2
  26. data/app/models/alchemy/element.rb +0 -14
  27. data/app/models/alchemy/ingredient.rb +16 -62
  28. data/app/models/alchemy/page/page_natures.rb +1 -10
  29. data/app/models/alchemy/page.rb +3 -3
  30. data/app/models/alchemy/page_version.rb +1 -1
  31. data/app/views/alchemy/admin/elements/create.js.erb +1 -1
  32. data/app/views/alchemy/admin/elements/destroy.js.erb +1 -3
  33. data/app/views/alchemy/admin/elements/fold.js.erb +2 -2
  34. data/app/views/alchemy/admin/elements/update.js.erb +1 -1
  35. data/app/views/alchemy/admin/pages/edit.html.erb +1 -1
  36. data/app/views/alchemy/ingredients/_boolean_editor.html.erb +1 -1
  37. data/app/views/alchemy/ingredients/_file_editor.html.erb +3 -1
  38. data/app/views/alchemy/ingredients/_headline_editor.html.erb +1 -1
  39. data/app/views/alchemy/ingredients/_html_editor.html.erb +1 -1
  40. data/app/views/alchemy/ingredients/_link_editor.html.erb +8 -8
  41. data/app/views/alchemy/ingredients/_node_editor.html.erb +1 -0
  42. data/app/views/alchemy/ingredients/_page_editor.html.erb +1 -0
  43. data/app/views/alchemy/ingredients/_picture_editor.html.erb +7 -6
  44. data/app/views/alchemy/ingredients/_select_editor.html.erb +1 -0
  45. data/app/views/alchemy/ingredients/_text_editor.html.erb +5 -4
  46. data/lib/alchemy/test_support/shared_ingredient_examples.rb +0 -1
  47. data/lib/alchemy/tinymce.rb +4 -0
  48. data/lib/alchemy/upgrader/tasks/ingredients_migrator.rb +4 -1
  49. data/lib/alchemy/version.rb +1 -1
  50. data/package.json +1 -1
  51. metadata +4 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 50e6a89657c457ab3061bdc6d2cff1eba7ab25856249071e1bbe8c7684e18055
4
- data.tar.gz: 282dd5e51249513e14309c4d98a09822b2fd7d038f8494f3313e00bc1026f8df
3
+ metadata.gz: a2356162437442579cc431fade3fc3486f5262d22e6a6212950634e5ac90f595
4
+ data.tar.gz: 4a933ae8c9652adbe6fef16cf2e420c31160b4352fb2fba3a15f536f67f1dbc0
5
5
  SHA512:
6
- metadata.gz: 54fc70c8b8ba0305d79d3e6309d4dab7a0a050a3aad1d06014fd3f38f9af34f81970a68358a7c3c222d2d740896dccb905920e6a67ee43bfb226d925916b67e6
7
- data.tar.gz: 5d5c5ea2794e1f3c2e84a6b99f4eeb07fe284e98dca598c93361ed5f977b3b4b0e966372f2bba1c32f6b5bf21f8f94ec6cb68bd7b375f6bfb899b295bc724e11
6
+ metadata.gz: 807c2793f1805245bf12512582f2bfd06acdc777a811b070877447792d20b5cfae46b7e04276d90aed455f97fca91f88385df51317a294b9569b7e845017dfbe
7
+ data.tar.gz: 04ec712fe19f499cf0174ee0d044a6c086abec3c063cfa5df32380033de40f4373c9e0978f626a74ac83a67b190ef4154566da152b39e02dba47b15bc6d9e94f
data/CHANGELOG.md CHANGED
@@ -1,3 +1,32 @@
1
+ ## 6.0.0-b6 (2021-09-02)
2
+
3
+ - Fix element with ingredients preview text [#2187](https://github.com/AlchemyCMS/alchemy_cms/pull/2187) ([tvdeyen](https://github.com/tvdeyen))
4
+ - Do not validate element during toggle fold and create [#2186](https://github.com/AlchemyCMS/alchemy_cms/pull/2186) ([tvdeyen](https://github.com/tvdeyen))
5
+ ## 6.0.0-b5 (2021-08-27)
6
+
7
+ - Remove spec that tests default data store value [#2184](https://github.com/AlchemyCMS/alchemy_cms/pull/2184) ([tvdeyen](https://github.com/tvdeyen))
8
+ - Remove data store accessor from ingredient base class [#2183](https://github.com/AlchemyCMS/alchemy_cms/pull/2183) ([tvdeyen](https://github.com/tvdeyen))
9
+
10
+ ## 6.0.0-b4 (2021-08-27)
11
+
12
+ - Load custom Tinymce config for ingredients [#2182](https://github.com/AlchemyCMS/alchemy_cms/pull/2182) ([tvdeyen](https://github.com/tvdeyen))
13
+ - Fix ingredient editor selector in element update callback [#2181](https://github.com/AlchemyCMS/alchemy_cms/pull/2181) ([tvdeyen](https://github.com/tvdeyen))
14
+ - Ingredient by role block level helper [#2180](https://github.com/AlchemyCMS/alchemy_cms/pull/2180) ([tvdeyen](https://github.com/tvdeyen))
15
+ - Fixes caching [#2179](https://github.com/AlchemyCMS/alchemy_cms/pull/2179) ([tvdeyen](https://github.com/tvdeyen))
16
+ - make images non-executable [#2176](https://github.com/AlchemyCMS/alchemy_cms/pull/2176) ([mensfeld](https://github.com/mensfeld))
17
+ - Release task [#2173](https://github.com/AlchemyCMS/alchemy_cms/pull/2173) ([tvdeyen](https://github.com/tvdeyen))
18
+
19
+ ## 6.0.0.b3 (2021-08-12)
20
+
21
+ ### Fixes
22
+
23
+ - Simplify ingredient creation [#2171](https://github.com/AlchemyCMS/alchemy_cms/pull/2171) ([tvdeyen](https://github.com/tvdeyen))
24
+ - Return ingredients value if element asked for ingredient [#2170](https://github.com/AlchemyCMS/alchemy_cms/pull/2170) ([tvdeyen](https://github.com/tvdeyen))
25
+ - Fix ingredient form field DOM ids [#2167](https://github.com/AlchemyCMS/alchemy_cms/pull/2167) ([tvdeyen](https://github.com/tvdeyen))
26
+ - Ensure resource table ends before the filter/tag sidebar [#2166](https://github.com/AlchemyCMS/alchemy_cms/pull/2166) ([robinboening](https://github.com/robinboening))
27
+ - Return fully namespaced ingredient constant [#2164](https://github.com/AlchemyCMS/alchemy_cms/pull/2164) ([tvdeyen](https://github.com/tvdeyen))
28
+ - (Re)-init Tinymce for elements with ingredients [#2163](https://github.com/AlchemyCMS/alchemy_cms/pull/2163) ([tvdeyen](https://github.com/tvdeyen))
29
+
1
30
  ## 6.0.0.b2 (2021-08-05)
2
31
 
3
32
  ### Features
data/README.md CHANGED
@@ -339,6 +339,10 @@ $ bin/rails s
339
339
 
340
340
  ## 📦 Releasing
341
341
 
342
+ ### Bump version
343
+
344
+ Bump the version number in both `lib/alchemy/version.rb` and `./package.json`. Make sure both are exactly the same and follow [SemVer format](https://semver.org/#semantic-versioning-specification-semver).
345
+
342
346
  ### Update the changelog
343
347
 
344
348
  ```bash
@@ -346,12 +350,20 @@ $ export GITHUB_ACCESS_TOKEN=...
346
350
  $ PREVIOUS_VERSION=4.1.0 bundle exec rake alchemy:changelog:update
347
351
  ```
348
352
 
349
- ### Release a new version
353
+ ### Commit version bump
350
354
 
351
355
  ```bash
352
- $ bundle exec rake release
356
+ $ git commit -am "Bump version to vX.Y.Z"
353
357
  ```
354
358
 
359
+ ### Release a new version
360
+
361
+ This task will publish both the ruby gem and the npm package.
362
+ It also tags the latest commit.
363
+
364
+ ```bash
365
+ $ bundle exec rake alchemy:release
366
+ ```
355
367
 
356
368
  ## ❓Getting Help
357
369
 
data/Rakefile CHANGED
@@ -1,37 +1,37 @@
1
1
  begin
2
- require 'bundler/setup'
2
+ require "bundler/setup"
3
3
  rescue LoadError
4
- puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
4
+ puts "You must `gem install bundler` and `bundle install` to run rake tasks"
5
5
  end
6
6
 
7
7
  begin
8
- require 'rdoc/task'
8
+ require "rdoc/task"
9
9
  rescue LoadError
10
- require 'rdoc/rdoc'
11
- require 'rake/rdoctask'
10
+ require "rdoc/rdoc"
11
+ require "rake/rdoctask"
12
12
  RDoc::Task = Rake::RDocTask
13
13
  end
14
14
 
15
- desc 'Generate documentation for Alchemy CMS.'
15
+ desc "Generate documentation for Alchemy CMS."
16
16
  RDoc::Task.new(:rdoc) do |rdoc|
17
- rdoc.rdoc_dir = 'rdoc'
18
- rdoc.title = 'Alchemy CMS'
19
- rdoc.options << '--line-numbers' << '--inline-source'
20
- rdoc.rdoc_files.include('README.md')
21
- rdoc.rdoc_files.include('config/alchemy/*.yml')
22
- rdoc.rdoc_files.include('lib/**/*.rb')
23
- rdoc.rdoc_files.include('app/**/*.rb')
17
+ rdoc.rdoc_dir = "rdoc"
18
+ rdoc.title = "Alchemy CMS"
19
+ rdoc.options << "--line-numbers" << "--inline-source"
20
+ rdoc.rdoc_files.include("README.md")
21
+ rdoc.rdoc_files.include("config/alchemy/*.yml")
22
+ rdoc.rdoc_files.include("lib/**/*.rb")
23
+ rdoc.rdoc_files.include("app/**/*.rb")
24
24
  end
25
25
 
26
- APP_RAKEFILE = File.expand_path('spec/dummy/Rakefile', __dir__)
27
- load 'rails/tasks/engine.rake'
26
+ APP_RAKEFILE = File.expand_path("spec/dummy/Rakefile", __dir__)
27
+ load "rails/tasks/engine.rake"
28
28
 
29
- require 'rspec/core'
30
- require 'rspec/core/rake_task'
29
+ require "rspec/core"
30
+ require "rspec/core/rake_task"
31
31
 
32
32
  RSpec::Core::RakeTask.new(:spec)
33
33
 
34
- task default: ['alchemy:spec:prepare', :spec]
34
+ task default: ["alchemy:spec:prepare", :spec]
35
35
 
36
36
  Bundler::GemHelper.install_tasks
37
37
 
@@ -56,11 +56,11 @@ namespace :alchemy do
56
56
  namespace :changelog do
57
57
  desc "Update CHANGELOG from GitHub (Set GITHUB_ACCESS_TOKEN and PREVIOUS_VERSION to a version you want to write changelog changes for)"
58
58
  task :update do
59
- original_file = './CHANGELOG.md'
60
- new_file = original_file + '.new'
61
- backup = original_file + '.old'
62
- changes = `git rev-list v#{ENV['PREVIOUS_VERSION']}..HEAD | bundle exec github_fast_changelog AlchemyCMS/alchemy_cms`
63
- File.open(new_file, 'w') do |fo|
59
+ original_file = "./CHANGELOG.md"
60
+ new_file = original_file + ".new"
61
+ backup = original_file + ".old"
62
+ changes = `git rev-list v#{ENV["PREVIOUS_VERSION"]}..HEAD | bundle exec github_fast_changelog AlchemyCMS/alchemy_cms`
63
+ File.open(new_file, "w") do |fo|
64
64
  fo.puts changes
65
65
  File.foreach(original_file) do |li|
66
66
  fo.puts li
@@ -72,4 +72,18 @@ namespace :alchemy do
72
72
  File.delete(backup)
73
73
  end
74
74
  end
75
+
76
+ desc "Release a new Ruby gem and npm package in one command"
77
+ task :release do
78
+ require "json"
79
+ require_relative "lib/alchemy/version"
80
+ package = File.read("package.json")
81
+ unless JSON.parse(package)["version"] == Alchemy.version
82
+ abort "Ruby gem and npm package versions are out of sync! Please fix."
83
+ end
84
+ # Release the Ruby gem with bundler
85
+ Rake::Task["release"].invoke
86
+ # Publish npm package via CLI
87
+ system "npm publish"
88
+ end
75
89
  end
@@ -31,15 +31,28 @@
31
31
  line-height: $form-field-line-height;
32
32
  transition: $transition-duration;
33
33
 
34
- &:focus {
34
+ &:focus:not(.readonly) {
35
35
  @include default-focus-style($box-shadow: 0 0 0 1px $focus-color);
36
36
  }
37
37
 
38
- &[disabled], .disabled {
38
+ &[disabled],
39
+ &.disabled,
40
+ &[readonly],
41
+ &.readonly {
39
42
  color: $form-field-disabled-text-color;
40
43
  background-color: $form-field-disabled-bg-color;
44
+ cursor: default;
45
+ }
46
+
47
+ &[disabled],
48
+ &.disabled {
41
49
  cursor: not-allowed;
42
50
  }
51
+
52
+ &[readonly],
53
+ &.readonly {
54
+ pointer-events: none;
55
+ }
43
56
  }
44
57
 
45
58
  %gradiated-toolbar {
@@ -20,7 +20,8 @@
20
20
  padding-bottom: 60px;
21
21
 
22
22
  &.with_tag_filter {
23
- padding-right: 242px;
23
+ width: calc(100% - 235px);
24
+ overflow-x: auto;
24
25
  }
25
26
  }
26
27
 
File without changes
@@ -32,14 +32,14 @@ module Alchemy
32
32
  if @paste_from_clipboard = params[:paste_from_clipboard].present?
33
33
  @element = paste_element_from_clipboard
34
34
  else
35
- @element = Element.create(create_element_params)
35
+ @element = Element.new(create_element_params)
36
36
  end
37
37
  if @page.definition["insert_elements_at"] == "top"
38
38
  @insert_at_top = true
39
- @element.move_to_top
39
+ @element.position = 1
40
40
  end
41
41
  end
42
- if @element.valid?
42
+ if @element.save
43
43
  render :create
44
44
  else
45
45
  @element.page_version = @page_version
@@ -65,6 +65,7 @@ module Alchemy
65
65
  end
66
66
 
67
67
  def destroy
68
+ @richtext_ids = @element.richtext_contents_ids + @element.richtext_ingredients_ids
68
69
  @element.destroy
69
70
  @notice = Alchemy.t("Successfully deleted element") % { element: @element.display_name }
70
71
  end
@@ -90,10 +91,14 @@ module Alchemy
90
91
  end
91
92
  end
92
93
 
94
+ # Toggle fodls the element and persists the state in the db
95
+ #
96
+ # Ingredient validations might make the element invalid.
97
+ # In this case we are just toggling a UI state and do not care about the validations.
93
98
  def fold
94
99
  @page = @element.page
95
100
  @element.folded = !@element.folded
96
- @element.save
101
+ @element.save(validate: false)
97
102
  end
98
103
 
99
104
  private
@@ -26,7 +26,7 @@ module Alchemy
26
26
  # @return Array<Alchemy::IngredientEditor>
27
27
  def ingredients
28
28
  element.definition.fetch(:ingredients, []).map do |ingredient|
29
- Alchemy::IngredientEditor.new(find_or_create_ingredient(ingredient[:role]))
29
+ Alchemy::IngredientEditor.new(find_or_create_ingredient(ingredient))
30
30
  end
31
31
  end
32
32
 
@@ -121,9 +121,12 @@ module Alchemy
121
121
  Alchemy::Content.create(element: element, name: name)
122
122
  end
123
123
 
124
- def find_or_create_ingredient(role)
125
- element.ingredients.find { |i| i.role == role } ||
126
- Ingredient.create(element: element, role: role)
124
+ def find_or_create_ingredient(definition)
125
+ element.ingredients.detect { |i| i.role == definition[:role] } ||
126
+ element.ingredients.create!(
127
+ role: definition[:role],
128
+ type: Alchemy::Ingredient.normalize_type(definition[:type]),
129
+ )
127
130
  end
128
131
  end
129
132
  end
@@ -64,8 +64,12 @@ module Alchemy
64
64
  "element[ingredients_attributes][#{form_field_counter}][#{column}]"
65
65
  end
66
66
 
67
+ # Returns a unique string to be passed to a form field id.
68
+ #
69
+ # @param column [String] A Ingredient column_name. Default is 'value'
70
+ #
67
71
  def form_field_id(column = "value")
68
- "element_ingredients_attributes_#{form_field_counter}_#{column}"
72
+ "element_#{element.id}_ingredient_#{id}_#{column}"
69
73
  end
70
74
 
71
75
  # Fixes Rails partial renderer calling to_model on the object
@@ -51,21 +51,23 @@ module Alchemy
51
51
  # If the element uses +ingredients+ it returns the +value+ of the ingredient record.
52
52
  #
53
53
  def ingredient(name)
54
- element.ingredient(name).presence || element.ingredient_by_role(name)&.value
54
+ element.ingredient(name)
55
55
  end
56
56
 
57
- deprecate ingredient: :value, deprecator: Alchemy::Deprecation
58
-
59
57
  # Returns the value of one of the element's ingredients.
60
58
  #
61
59
  def value(name)
62
- element.ingredient_by_role(name)&.value
60
+ element.value_for(name)
63
61
  end
64
62
 
65
- # Returns true if the given content or ingredient has been filled by the user.
63
+ # Returns true if the given content or ingredient has a value.
66
64
  #
67
65
  def has?(name)
68
- element.has_ingredient?(name) || element.has_value_for?(name)
66
+ if element.ingredient_definitions.any?
67
+ element.has_value_for?(name)
68
+ else
69
+ element.has_ingredient?(name)
70
+ end
69
71
  end
70
72
 
71
73
  # Return's the given content's essence.
@@ -75,6 +77,12 @@ module Alchemy
75
77
  end
76
78
 
77
79
  deprecate essence: "Use `ingredient_by_role` instead", deprecator: Alchemy::Deprecation
80
+
81
+ # Return's the ingredient record by given role.
82
+ #
83
+ def ingredient_by_role(role)
84
+ element.ingredient_by_role(role)
85
+ end
78
86
  end
79
87
 
80
88
  # Block-level helper for element views. Constructs a DOM element wrapping
@@ -5,10 +5,20 @@ module Alchemy
5
5
  module ElementEssences
6
6
  # Returns the contents essence value (aka. ingredient) for passed content name.
7
7
  def ingredient(name)
8
- content = content_by_name(name)
9
- return nil if content.blank?
8
+ ing = ingredient_by_role(name)
9
+ if ing
10
+ Alchemy::Deprecation.warn <<~WARN
11
+ Using `element.ingredient` to get the value of an ingredient is deprecated and will change in Alchemy 6.1
12
+ If you want to read the value of an elements ingredient please use `element.value_for(:ingredient_role)` instead.
13
+ The next version of Alchemy will return a `Alchemy::Ingredient` record instead.
14
+ WARN
15
+ ing.value
16
+ else
17
+ content = content_by_name(name)
18
+ return nil if content.blank?
10
19
 
11
- content.ingredient
20
+ content.ingredient
21
+ end
12
22
  end
13
23
 
14
24
  # True if the element has a content for given name,
@@ -16,6 +26,7 @@ module Alchemy
16
26
  def has_ingredient?(name)
17
27
  ingredient(name).present?
18
28
  end
29
+ deprecate has_ingredient?: :has_value_for?, deprecator: Alchemy::Deprecation
19
30
 
20
31
  # Returns all essence errors in the format of:
21
32
  #
@@ -22,6 +22,11 @@ module Alchemy
22
22
  validates_associated :ingredients, on: :update
23
23
  end
24
24
 
25
+ # The value of an ingredient of the element by role
26
+ def value_for(role)
27
+ ingredient_by_role(role)&.value
28
+ end
29
+
25
30
  # Find first ingredient from element by given role.
26
31
  def ingredient_by_role(role)
27
32
  ingredients.detect { |ingredient| ingredient.role == role.to_s }
@@ -87,7 +92,7 @@ module Alchemy
87
92
  # True if the element has a ingredient for given name
88
93
  # that has a non blank value.
89
94
  def has_value_for?(role)
90
- ingredient_by_role(role)&.value.present?
95
+ value_for(role).present?
91
96
  end
92
97
 
93
98
  # Ingredient validation error messages
@@ -167,8 +172,11 @@ module Alchemy
167
172
 
168
173
  # Builds ingredients for this element as described in the +elements.yml+
169
174
  def build_ingredients
170
- self.ingredients = ingredient_definitions.map do |attributes|
171
- Ingredient.build(role: attributes[:role], element: self)
175
+ ingredient_definitions.each do |attributes|
176
+ ingredients.build(
177
+ role: attributes[:role],
178
+ type: Alchemy::Ingredient.normalize_type(attributes[:type]),
179
+ )
172
180
  end
173
181
  end
174
182
  end
@@ -99,12 +99,12 @@ module Alchemy
99
99
  # The ingredient that's used for element's preview text.
100
100
  #
101
101
  # It tries to find one of element's ingredients that is defined +as_element_title+.
102
- # Takes element's first ingredient if no ingredient is defined +as_element_title+.
102
+ # Takes element's first defined ingredient if no ingredient is defined +as_element_title+.
103
103
  #
104
104
  # @return (Alchemy::Ingredient)
105
105
  #
106
106
  def preview_ingredient
107
- @_preview_ingredient ||= ingredients.detect(&:preview_ingredient?) || ingredients.first
107
+ @_preview_ingredient ||= ingredients.detect(&:preview_ingredient?) || first_ingredient_by_definition
108
108
  end
109
109
 
110
110
  private
@@ -122,6 +122,13 @@ module Alchemy
122
122
  def preview_text_from_preview_ingredient(maxlength)
123
123
  preview_ingredient&.preview_text(maxlength)
124
124
  end
125
+
126
+ def first_ingredient_by_definition
127
+ return if ingredient_definitions.empty?
128
+
129
+ role = ingredient_definitions.first["role"]
130
+ ingredients.detect { |ingredient| ingredient.role == role }
131
+ end
125
132
  end
126
133
  end
127
134
  end
@@ -267,20 +267,6 @@ module Alchemy
267
267
  "alchemy/elements/#{name}"
268
268
  end
269
269
 
270
- # Returns the key that's taken for cache path.
271
- #
272
- # Uses the page's +published_at+ value that's updated when the user publishes the page.
273
- #
274
- # If the page is the current preview it uses the element's updated_at value as cache key.
275
- #
276
- def cache_key
277
- if Page.current_preview == page
278
- "alchemy/elements/#{id}-#{updated_at}"
279
- else
280
- "alchemy/elements/#{id}-#{page.published_at}"
281
- end
282
- end
283
-
284
270
  # A collection of element names that can be nested inside this element.
285
271
  def nestable_elements
286
272
  definition.fetch("nestable_elements", [])
@@ -6,12 +6,13 @@ module Alchemy
6
6
 
7
7
  include Hints
8
8
 
9
- self.abstract_class = true
10
9
  self.table_name = "alchemy_ingredients"
11
10
 
12
11
  belongs_to :element, touch: true, class_name: "Alchemy::Element", inverse_of: :ingredients
13
12
  belongs_to :related_object, polymorphic: true, optional: true
14
13
 
14
+ before_validation(on: :create) { self.value ||= default_value }
15
+
15
16
  validates :type, presence: true
16
17
  validates :role, presence: true
17
18
 
@@ -33,32 +34,6 @@ module Alchemy
33
34
  scope :videos, -> { where(type: "Alchemy::Ingredients::Video") }
34
35
 
35
36
  class << self
36
- # Builds concrete ingredient class as described in the +elements.yml+
37
- def build(attributes = {})
38
- element = attributes[:element]
39
- raise ArgumentError, "No element given. Please pass element in attributes." if element.nil?
40
- raise ArgumentError, "No role given. Please pass role in attributes." if attributes[:role].nil?
41
-
42
- definition = element.ingredient_definition_for(attributes[:role])
43
- if definition.nil?
44
- raise DefinitionError,
45
- "No definition found for #{attributes[:role]}. Please define #{attributes[:role]} on #{element[:name]}."
46
- end
47
-
48
- ingredient_class = Ingredient.ingredient_class_by_type(definition[:type])
49
- ingredient_class.new(
50
- type: Ingredient.normalize_type(definition[:type]),
51
- value: default_value(definition),
52
- role: definition[:role],
53
- element: element,
54
- )
55
- end
56
-
57
- # Creates concrete ingredient class as described in the +elements.yml+
58
- def create(attributes = {})
59
- build(attributes).tap(&:save)
60
- end
61
-
62
37
  # Defines getter and setter method aliases for related object
63
38
  #
64
39
  # @param [String|Symbol] The name of the alias
@@ -78,20 +53,6 @@ module Alchemy
78
53
  end
79
54
  end
80
55
 
81
- # Returns an ingredient class by type
82
- #
83
- # Raises ArgumentError if there is no such class in the
84
- # +Alchemy::Ingredients+ module namespace.
85
- #
86
- # If you add custom ingredient class,
87
- # put them in the +Alchemy::Ingredients+ module namespace
88
- #
89
- # @param [String] The ingredient class name to constantize
90
- # @return [Class]
91
- def ingredient_class_by_type(ingredient_type)
92
- Alchemy::Ingredients.const_get(ingredient_type.to_s.classify.demodulize)
93
- end
94
-
95
56
  # Modulize ingredient type
96
57
  #
97
58
  # Makes sure the passed ingredient type is in the +Alchemy::Ingredients+
@@ -112,22 +73,6 @@ module Alchemy
112
73
  default: Alchemy.t("ingredient_roles.#{role}", default: role.humanize),
113
74
  )
114
75
  end
115
-
116
- private
117
-
118
- # Returns the default value from ingredient definition
119
- #
120
- # If the value is a symbol it gets passed through i18n
121
- # inside the +alchemy.default_ingredient_texts+ scope
122
- def default_value(definition)
123
- default = definition[:default]
124
- case default
125
- when Symbol
126
- Alchemy.t(default, scope: :default_ingredient_texts)
127
- else
128
- default
129
- end
130
- end
131
76
  end
132
77
 
133
78
  # Compatibility method for access from element
@@ -173,11 +118,6 @@ module Alchemy
173
118
  value.to_s[0..maxlength - 1]
174
119
  end
175
120
 
176
- # Cross DB adapter data accessor that works
177
- def data
178
- @_data ||= (self[:data] || {}).with_indifferent_access
179
- end
180
-
181
121
  # The path to the view partial of the ingredient
182
122
  # @return [String]
183
123
  def to_partial_path
@@ -220,5 +160,19 @@ module Alchemy
220
160
  def hint_translation_attribute
221
161
  role
222
162
  end
163
+
164
+ # Returns the default value from ingredient definition
165
+ #
166
+ # If the value is a symbol it gets passed through i18n
167
+ # inside the +alchemy.default_ingredient_texts+ scope
168
+ def default_value
169
+ default = definition[:default]
170
+ case default
171
+ when Symbol
172
+ Alchemy.t(default, scope: :default_ingredient_texts)
173
+ else
174
+ default
175
+ end
176
+ end
223
177
  end
224
178
  end
@@ -110,22 +110,13 @@ module Alchemy
110
110
  # If the page is the current preview it uses the updated_at value as cache key.
111
111
  #
112
112
  def cache_key
113
- if Page.current_preview == self
113
+ if Page.current_preview == id
114
114
  "alchemy/pages/#{id}-#{updated_at}"
115
115
  else
116
116
  "alchemy/pages/#{id}-#{published_at}"
117
117
  end
118
118
  end
119
119
 
120
- # We use the published_at value for the cache_key.
121
- #
122
- # If no published_at value is set yet, i.e. because it was never published,
123
- # we return the updated_at value.
124
- #
125
- def published_at
126
- read_attribute(:published_at) || updated_at
127
- end
128
-
129
120
  # Returns true if the page cache control headers should be set.
130
121
  #
131
122
  # == Disable Alchemy's page caching globally
@@ -198,13 +198,13 @@ module Alchemy
198
198
  %w[name urlname title]
199
199
  end
200
200
 
201
- # Used to store the current page previewed in the edit page template.
201
+ # Used to store the current page id previewed in the edit page template.
202
202
  #
203
203
  def current_preview=(page)
204
- RequestStore.store[:alchemy_current_preview] = page
204
+ RequestStore.store[:alchemy_current_preview] = page&.id
205
205
  end
206
206
 
207
- # Returns the current page previewed in the edit page template.
207
+ # Returns the current page id previewed in the edit page template.
208
208
  #
209
209
  def current_preview
210
210
  RequestStore.store[:alchemy_current_preview]
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Alchemy
4
4
  class PageVersion < BaseRecord
5
- belongs_to :page, class_name: "Alchemy::Page", inverse_of: :versions
5
+ belongs_to :page, class_name: "Alchemy::Page", inverse_of: :versions, touch: true
6
6
 
7
7
  has_many :elements, -> { order(:position) },
8
8
  class_name: "Alchemy::Element",
@@ -34,7 +34,7 @@
34
34
 
35
35
  Alchemy.growl('<%= Alchemy.t(:successfully_added_element) %>');
36
36
  Alchemy.closeCurrentDialog();
37
- Alchemy.Tinymce.init(<%= @element.richtext_contents_ids.to_json %>);
37
+ Alchemy.Tinymce.init(<%= (@element.richtext_contents_ids + @element.richtext_ingredients_ids).to_json %>);
38
38
  Alchemy.PreviewWindow.refresh(function() {
39
39
  Alchemy.ElementEditors.focusElementPreview(<%= @element.id %>);
40
40
  });
@@ -3,9 +3,7 @@ $('#element_<%= @element.id %>').hide(200, function() {
3
3
  Alchemy.growl('<%= j @notice %>');
4
4
  $('#element_area .sortable-elements').sortable('refresh');
5
5
  Alchemy.PreviewWindow.refresh();
6
- <% @element.richtext_contents_ids.each do |id| %>
7
- tinymce.get('tinymce_<%= id %>').remove();
8
- <% end %>
6
+ Alchemy.Tinymce.remove(<%= @richtext_ids.to_json %>);
9
7
  <% if @element.fixed? %>
10
8
  Alchemy.FixedElements.removeTab(<%= @element.id %>);
11
9
  <% end %>
@@ -14,12 +14,12 @@
14
14
 
15
15
  <% if @element.folded? -%>
16
16
 
17
- Alchemy.Tinymce.remove(<%= @element.richtext_contents_ids.to_json %>);
17
+ Alchemy.Tinymce.remove(<%= (@element.richtext_contents_ids + @element.richtext_ingredients_ids).to_json %>);
18
18
 
19
19
  <% else -%>
20
20
 
21
21
  $el.trigger('FocusElementEditor.Alchemy');
22
- Alchemy.Tinymce.init(<%= @element.richtext_contents_ids.to_json %>);
22
+ Alchemy.Tinymce.init(<%= (@element.richtext_contents_ids + @element.richtext_ingredients_ids).to_json %>);
23
23
  Alchemy.GUI.initElement($el);
24
24
  Alchemy.SortableElements(
25
25
  <%= @page.id %>,
@@ -1,7 +1,7 @@
1
1
  (function() {
2
2
  var $el = $('#element_<%= @element.id %>');
3
3
  var $errors = $('#element_<%= @element.id %>_errors');
4
- $('> .element-content .content_editor, > .element-content .ingredient_editor', $el).removeClass('validation_failed');
4
+ $('> .element-content .content_editor, > .element-content .ingredient-editor', $el).removeClass('validation_failed');
5
5
 
6
6
  <%- if @element_validated -%>
7
7
 
@@ -138,7 +138,7 @@
138
138
  <% end %>
139
139
 
140
140
  <% content_for :javascripts do %>
141
- <% if Alchemy::Tinymce.custom_config_contents(@page).present? %>
141
+ <% if Alchemy::Tinymce.custom_configs_present?(@page) %>
142
142
  <%= render 'tinymce_custom_config' %>
143
143
  <% end %>
144
144
 
@@ -3,7 +3,7 @@
3
3
  data: boolean_editor.data_attributes do %>
4
4
  <%= element_form.fields_for(:ingredients, boolean_editor.ingredient) do |f| %>
5
5
  <%= f.label :value, style: "display: inline-block" do %>
6
- <%= f.check_box :value %>
6
+ <%= f.check_box :value, id: nil %>
7
7
  <%= render_ingredient_role(boolean_editor) %>
8
8
  <% end %>
9
9
  <%= render_hint_for(boolean_editor) %>
@@ -44,7 +44,9 @@
44
44
  },
45
45
  title: Alchemy.t(:edit_file_properties) %>
46
46
  </div>
47
- <%= f.hidden_field :attachment_id, value: file_editor.attachment&.id %>
47
+ <%= f.hidden_field :attachment_id,
48
+ value: file_editor.attachment&.id,
49
+ id: file_editor.form_field_id(:attachment_id) %>
48
50
  </div>
49
51
  <% end %>
50
52
  <% end %>
@@ -3,7 +3,7 @@
3
3
  data: headline_editor.data_attributes do %>
4
4
  <%= element_form.fields_for(:ingredients, headline_editor.ingredient) do |f| %>
5
5
  <%= ingredient_label(headline_editor) %>
6
- <%= f.text_field :value %>
6
+ <%= f.text_field :value, id: nil %>
7
7
 
8
8
  <div class="input-row">
9
9
  <% if headline_editor.level_options.length > 1 %>
@@ -3,6 +3,6 @@
3
3
  data: html_editor.data_attributes do %>
4
4
  <%= element_form.fields_for(:ingredients, html_editor.ingredient) do |f| %>
5
5
  <%= ingredient_label(html_editor) %>
6
- <%= f.text_area :value %>
6
+ <%= f.text_area :value, id: nil %>
7
7
  <% end %>
8
8
  <% end %>
@@ -4,15 +4,15 @@
4
4
  <%= element_form.fields_for(:ingredients, link_editor.ingredient) do |f| %>
5
5
  <%= ingredient_label(link_editor) %>
6
6
  <%= f.text_field :value,
7
- class: "thin_border text_with_icon disabled",
8
- name: nil,
9
- id: nil,
10
- disabled: true
7
+ class: "thin_border text_with_icon readonly",
8
+ id: link_editor.form_field_id,
9
+ "data-link-value": true,
10
+ readonly: true,
11
+ tabindex: -1
11
12
  %>
12
- <%= f.hidden_field :value, "data-link-value": true %>
13
- <%= f.hidden_field :link_title, "data-link-title": true %>
14
- <%= f.hidden_field :link_class_name, "data-link-class": true %>
15
- <%= f.hidden_field :link_target, "data-link-target": true %>
13
+ <%= f.hidden_field :link_title, "data-link-title": true, id: nil %>
14
+ <%= f.hidden_field :link_class_name, "data-link-class": true, id: nil %>
15
+ <%= f.hidden_field :link_target, "data-link-target": true, id: nil %>
16
16
  <% end %>
17
17
  <%= render "alchemy/ingredients/shared/link_tools", ingredient_editor: link_editor %>
18
18
  <% end %>
@@ -5,6 +5,7 @@
5
5
  <%= ingredient_label(node_editor, :node_id) %>
6
6
  <%= f.text_field :node_id,
7
7
  value: node_editor.node&.id,
8
+ id: node_editor.form_field_id(:node_id),
8
9
  class: 'alchemy_selectbox full_width' %>
9
10
  <% end %>
10
11
  <% end %>
@@ -5,6 +5,7 @@
5
5
  <%= ingredient_label(page_editor, :page_id) %>
6
6
  <%= f.text_field :page_id,
7
7
  value: page_editor.page&.id,
8
+ id: page_editor.form_field_id(:page_id),
8
9
  class: 'alchemy_selectbox full_width' %>
9
10
  <% end %>
10
11
  <% end %>
@@ -44,16 +44,17 @@
44
44
  </div>
45
45
  <% end %>
46
46
  <%= f.hidden_field :picture_id, value: picture_editor.picture&.id,
47
+ id: picture_editor.form_field_id(:picture_id),
47
48
  data: {
48
49
  picture_id: true,
49
50
  image_file_width: picture_editor.image_file_width,
50
51
  image_file_height: picture_editor.image_file_height
51
52
  } %>
52
- <%= f.hidden_field :link, data: { link_value: true } %>
53
- <%= f.hidden_field :link_title, data: { link_title: true } %>
54
- <%= f.hidden_field :link_class_name, data: { link_class: true } %>
55
- <%= f.hidden_field :link_target, data: { link_target: true } %>
56
- <%= f.hidden_field :crop_from, data: { crop_from: true } %>
57
- <%= f.hidden_field :crop_size, data: { crop_size: true } %>
53
+ <%= f.hidden_field :link, data: { link_value: true }, id: nil %>
54
+ <%= f.hidden_field :link_title, data: { link_title: true }, id: nil %>
55
+ <%= f.hidden_field :link_class_name, data: { link_class: true }, id: nil %>
56
+ <%= f.hidden_field :link_target, data: { link_target: true }, id: nil %>
57
+ <%= f.hidden_field :crop_from, data: { crop_from: true }, id: nil %>
58
+ <%= f.hidden_field :crop_size, data: { crop_size: true }, id: nil %>
58
59
  <% end %>
59
60
  <% end %>
@@ -22,6 +22,7 @@
22
22
  options_tags = options_for_select(select_values, select_editor.value)
23
23
  end %>
24
24
  <%= f.select :value, options_tags, {}, {
25
+ id: nil,
25
26
  class: ["alchemy_selectbox", "ingredient-editor-select"]
26
27
  } %>
27
28
  <% end %>
@@ -7,12 +7,13 @@
7
7
  <%= ingredient_label(text_editor) %>
8
8
  <%= f.text_field :value,
9
9
  class: text_editor.settings[:linkable] ? "text_with_icon" : "",
10
+ id: nil,
10
11
  type: text_editor.settings[:input_type] || "text" %>
11
12
  <% if text_editor.settings[:linkable] %>
12
- <%= f.hidden_field :link, "data-link-value": true %>
13
- <%= f.hidden_field :link_title, "data-link-title": true %>
14
- <%= f.hidden_field :link_class_name, "data-link-class": true%>
15
- <%= f.hidden_field :link_target, "data-link-target": true %>
13
+ <%= f.hidden_field :link, "data-link-value": true, id: nil %>
14
+ <%= f.hidden_field :link_title, "data-link-title": true, id: nil %>
15
+ <%= f.hidden_field :link_class_name, "data-link-class": true, id: nil %>
16
+ <%= f.hidden_field :link_target, "data-link-target": true, id: nil %>
16
17
  <%= render "alchemy/ingredients/shared/link_tools", ingredient_editor: text_editor %>
17
18
  <% end %>
18
19
  <% end %>
@@ -16,7 +16,6 @@ RSpec.shared_examples_for "an alchemy ingredient" do
16
16
  it { is_expected.to belong_to(:related_object).optional }
17
17
  it { is_expected.to validate_presence_of(:role) }
18
18
  it { is_expected.to validate_presence_of(:type) }
19
- it { expect(subject.data).to eq({}) }
20
19
 
21
20
  describe "#settings" do
22
21
  subject { ingredient.settings }
@@ -34,6 +34,10 @@ module Alchemy
34
34
  @@init
35
35
  end
36
36
 
37
+ def custom_configs_present?(page)
38
+ custom_config_contents(page).any? || custom_config_ingredients(page).any?
39
+ end
40
+
37
41
  def custom_config_contents(page)
38
42
  content_definitions_from_elements(page.descendent_element_definitions)
39
43
  end
@@ -30,7 +30,10 @@ module Alchemy::Upgrader::Tasks
30
30
  next unless content
31
31
 
32
32
  essence = content.essence
33
- ingredient = Alchemy::Ingredient.build(role: ingredient_definition[:role], element: element)
33
+ ingredient = element.ingredients.build(
34
+ role: ingredient_definition[:role],
35
+ type: Alchemy::Ingredient.normalize_type(ingredient_definition[:type]),
36
+ )
34
37
  belongs_to_associations = essence.class.reflect_on_all_associations(:belongs_to)
35
38
  if belongs_to_associations.any?
36
39
  ingredient.related_object = essence.public_send(belongs_to_associations.first.name)
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Alchemy
4
- VERSION = "6.0.0.b2"
4
+ VERSION = "6.0.0-b6"
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.0-b2",
3
+ "version": "6.0.0-b6",
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.0.b2
4
+ version: 6.0.0.pre.b6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Thomas von Deyen
@@ -10,10 +10,10 @@ authors:
10
10
  - Hendrik Mans
11
11
  - Carsten Fregin
12
12
  - Martin Meyerhoff
13
- autorequire:
13
+ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
- date: 2021-08-05 00:00:00.000000000 Z
16
+ date: 2021-09-02 00:00:00.000000000 Z
17
17
  dependencies:
18
18
  - !ruby/object:Gem::Dependency
19
19
  name: actionmailer
@@ -1517,8 +1517,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
1517
1517
  requirements:
1518
1518
  - ImageMagick (libmagick), v6.6 or greater.
1519
1519
  rubygems_version: 3.1.6
1520
- signing_key:
1520
+ signing_key:
1521
1521
  specification_version: 4
1522
1522
  summary: A powerful, userfriendly and flexible CMS for Rails
1523
1523
  test_files: []
1524
- ...