alchemy_cms 5.1.0 → 5.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +2 -1
  3. data/CHANGELOG.md +51 -0
  4. data/Gemfile +2 -1
  5. data/alchemy_cms.gemspec +2 -2
  6. data/app/assets/stylesheets/alchemy/_variables.scss +2 -0
  7. data/app/assets/stylesheets/alchemy/elements.scss +38 -5
  8. data/app/controllers/alchemy/admin/base_controller.rb +1 -0
  9. data/app/controllers/alchemy/admin/trash_controller.rb +2 -0
  10. data/app/decorators/alchemy/content_editor.rb +64 -0
  11. data/app/decorators/alchemy/element_editor.rb +67 -0
  12. data/app/helpers/alchemy/admin/contents_helper.rb +3 -8
  13. data/app/helpers/alchemy/elements_helper.rb +0 -18
  14. data/app/helpers/alchemy/pages_helper.rb +3 -1
  15. data/app/models/alchemy/attachment.rb +4 -0
  16. data/app/models/alchemy/content.rb +7 -0
  17. data/app/models/alchemy/element.rb +38 -0
  18. data/app/models/alchemy/element/definitions.rb +5 -22
  19. data/app/models/alchemy/page/page_elements.rb +9 -2
  20. data/app/models/alchemy/picture_variant.rb +1 -1
  21. data/app/views/alchemy/admin/elements/_element.html.erb +1 -1
  22. data/app/views/alchemy/admin/elements/_element_footer.html.erb +1 -1
  23. data/app/views/alchemy/admin/elements/_element_header.html.erb +2 -0
  24. data/app/views/alchemy/admin/elements/_element_toolbar.html.erb +1 -1
  25. data/config/brakeman.ignore +305 -17
  26. data/config/locales/alchemy.en.yml +40 -24
  27. data/lib/alchemy/deprecation.rb +1 -1
  28. data/lib/alchemy/element_definition.rb +70 -0
  29. data/lib/alchemy/elements_finder.rb +6 -2
  30. data/lib/alchemy/essence.rb +4 -4
  31. data/lib/alchemy/filetypes.rb +13 -0
  32. data/lib/alchemy/forms/builder.rb +1 -1
  33. data/lib/alchemy/i18n.rb +4 -5
  34. data/lib/alchemy/page_layout.rb +1 -0
  35. data/lib/alchemy/resource.rb +5 -3
  36. data/lib/alchemy/test_support.rb +18 -0
  37. data/lib/alchemy/test_support/essence_shared_examples.rb +0 -3
  38. data/lib/alchemy/test_support/factories.rb +11 -0
  39. data/lib/alchemy/test_support/factories/attachment_factory.rb +0 -2
  40. data/lib/alchemy/test_support/factories/content_factory.rb +0 -6
  41. data/lib/alchemy/test_support/factories/dummy_user_factory.rb +0 -2
  42. data/lib/alchemy/test_support/factories/element_factory.rb +0 -3
  43. data/lib/alchemy/test_support/factories/essence_file_factory.rb +0 -3
  44. data/lib/alchemy/test_support/factories/essence_page_factory.rb +0 -3
  45. data/lib/alchemy/test_support/factories/essence_picture_factory.rb +0 -4
  46. data/lib/alchemy/test_support/factories/essence_text_factory.rb +0 -2
  47. data/lib/alchemy/test_support/factories/language_factory.rb +0 -3
  48. data/lib/alchemy/test_support/factories/node_factory.rb +0 -4
  49. data/lib/alchemy/test_support/factories/page_factory.rb +0 -3
  50. data/lib/alchemy/test_support/factories/picture_factory.rb +0 -2
  51. data/lib/alchemy/test_support/factories/picture_thumb_factory.rb +0 -3
  52. data/lib/alchemy/test_support/factories/site_factory.rb +0 -2
  53. data/lib/alchemy/test_support/integration_helpers.rb +5 -5
  54. data/lib/alchemy/version.rb +1 -1
  55. data/lib/alchemy_cms.rb +1 -0
  56. metadata +8 -6
@@ -130,31 +130,45 @@ en:
130
130
  # == Mime Types translations
131
131
  # These are human readable mime types used for the document-type row in archive files.
132
132
  mime_types:
133
- audio/mp4: 'MP4-Audio'
134
- application/msword: 'Word-Document'
135
- application/rtf: 'RTF-Document'
136
- audio/mpeg: 'MP3-Audio'
137
- text/plain: 'Text-Document'
138
- video/mp4: 'MP4-Video'
139
- video/mpeg: 'MPEG-Video'
140
- application/pdf: 'PDF-Document'
141
- application/x-flash-video: 'Flash-Video'
142
- video/x-flv: 'Flash-Video'
143
- application/x-shockwave-flash: 'Flash-Movie'
144
- application/zip: 'ZIP-Archive'
145
- application/x-rar: 'RAR-Archive'
133
+ application/msexcel: Excel Spreadsheet
134
+ application/mspowerpoint: PowerPoint Presentation
135
+ application/msword: Word Document
136
+ application/pdf: PDF Document
137
+ application/rtf: RTF Document
138
+ application/vcard: vCard
146
139
  application/vnd:
147
- ms-excel: 'Excel-Document'
148
- video/quicktime: 'Quicktime-Video'
149
- image/x-psd: 'Photoshop-File'
150
- image/gif: 'GIF-Image'
151
- image/png: 'PNG-Image'
152
- image/jpeg: 'JPG-Image'
153
- video/x-msvideo: 'AVI-Video'
154
- video/x-ms-wmv: 'Windows Media Video'
155
- image/tiff: 'TIFF-Image'
156
- 'text/x-vcard': 'vCard'
157
- application/vcard: 'vCard'
140
+ ms-excel: Excel Spreadsheet
141
+ ms-powerpoint: PowerPoint Presentation
142
+ ms-word: Word Document
143
+ openxmlformats-officedocument:
144
+ presentationml:
145
+ presentation: PowerPoint 2007 Presentation
146
+ spreadsheetml:
147
+ sheet: Excel 2007 Spreadsheet
148
+ wordprocessingml:
149
+ document: Word 2007 Document
150
+ application/x-flash-video: Flash Video
151
+ application/x-rar: RAR Archive
152
+ application/x-shockwave-flash: Flash Movie
153
+ application/zip: ZIP Archive
154
+ audio/mp4: MPEG-4 Audio
155
+ audio/mpeg: MP3 Audio
156
+ audio/wav: WAV Audio
157
+ audio/x-wav: WAV Audio
158
+ image/gif: GIF Image
159
+ image/jpeg: JPG Image
160
+ image/png: PNG Image
161
+ image/tiff: TIFF Image
162
+ image/x-psd: Photoshop File
163
+ image/svg+xml: SVG Image
164
+ text/plain: Plain Text Document
165
+ text/x-vcard: vCard
166
+ video/mp4: MPEG-4 Video
167
+ video/mpeg: MPEG Video
168
+ video/quicktime: Quicktime Video
169
+ video/x-flv: Flash Video
170
+ video/x-ms-wmv: Windows Media Video
171
+ video/x-msvideo: AVI Video
158
172
 
159
173
  link_target_options:
160
174
  default: Same Window
@@ -295,7 +309,9 @@ en:
295
309
  "Visit page": "Visit page"
296
310
  "Warning!": "Warning!"
297
311
  content_definition_missing: "Warning: Content is missing its definition. Please check the elements.yml"
312
+ content_deprecated: "WARNING! This content is deprecated and will be removed soon. Please do not use it anymore."
298
313
  element_definition_missing: "WARNING! Missing element definition. Please check your elements.yml file."
314
+ element_deprecated: "WARNING! This element is deprecated and will be removed soon. Please do not use it anymore."
299
315
  page_definition_missing: "WARNING! Missing page layout definition. Please check your page_layouts.yml file."
300
316
  "Welcome to Alchemy": "Welcome to Alchemy"
301
317
  "Who else is online": "Who else is online"
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module Alchemy
3
- Deprecation = ActiveSupport::Deprecation.new("5.1", "Alchemy")
3
+ Deprecation = ActiveSupport::Deprecation.new("6.0", "Alchemy")
4
4
  end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Alchemy
4
+ class ElementDefinition
5
+ class << self
6
+ # Returns the definitions from elements.yml file.
7
+ #
8
+ # Place a +elements.yml+ file inside your apps +config/alchemy+ folder to define
9
+ # your own set of elements
10
+ #
11
+ def all
12
+ @definitions ||= read_definitions_file.map(&:with_indifferent_access)
13
+ end
14
+
15
+ # Add additional page definitions to collection.
16
+ #
17
+ # Useful for extending the elements from an Alchemy module.
18
+ #
19
+ # === Usage Example
20
+ #
21
+ # Call +Alchemy::ElementDefinition.add(your_definition)+ in your engine.rb file.
22
+ #
23
+ # @param [Array || Hash]
24
+ # You can pass a single element definition as Hash, or a collection of elements as Array.
25
+ #
26
+ def add(element)
27
+ all
28
+ if element.is_a?(Array)
29
+ @definitions += element
30
+ elsif element.is_a?(Hash)
31
+ @definitions << element
32
+ else
33
+ raise TypeError
34
+ end
35
+ end
36
+
37
+ # Returns one element definition by given name.
38
+ #
39
+ def get(name)
40
+ return {} if name.blank?
41
+
42
+ all.detect { |a| a["name"] == name }
43
+ end
44
+
45
+ private
46
+
47
+ # Reads the element definitions from +config/alchemy/elements.yml+.
48
+ #
49
+ def read_definitions_file
50
+ if File.exist?(definitions_file_path)
51
+ YAML.safe_load(
52
+ ERB.new(File.read(definitions_file_path)).result,
53
+ YAML_WHITELIST_CLASSES,
54
+ [],
55
+ true
56
+ ) || []
57
+ else
58
+ raise LoadError,
59
+ "Could not find elements.yml file! Please run `rails generate alchemy:install`"
60
+ end
61
+ end
62
+
63
+ # Returns the elements.yml file path
64
+ #
65
+ def definitions_file_path
66
+ Rails.root.join "config/alchemy/elements.yml"
67
+ end
68
+ end
69
+ end
70
+ end
@@ -26,8 +26,6 @@ module Alchemy
26
26
  # Randomize the output of elements
27
27
  # @option options [Boolean] :reverse (false)
28
28
  # Reverse the load order
29
- # @option options [Hash] :fallback
30
- # Define elements that are loaded from another page if no element was found on given page.
31
29
  def initialize(options = {})
32
30
  @options = options
33
31
  end
@@ -83,6 +81,9 @@ module Alchemy
83
81
  when Alchemy::Page
84
82
  page_or_layout
85
83
  when String
84
+ Alchemy::Deprecation.warn "Passing a String as `from_page` option to " \
85
+ "`render_elements` is deprecated and will be removed with Alchemy 6.0. " \
86
+ "Please load the page beforehand and pass it as an object instead."
86
87
  Alchemy::Page.find_by(
87
88
  language: Alchemy::Language.current,
88
89
  page_layout: page_or_layout,
@@ -92,6 +93,9 @@ module Alchemy
92
93
  end
93
94
 
94
95
  def fallback_required?(elements)
96
+ if options[:fallback]
97
+ Alchemy::Deprecation.warn "Passing `fallback` options to `render_elements` is deprecated an will be removed with Alchemy 6.0."
98
+ end
95
99
  options[:fallback] && elements
96
100
  .where(Alchemy::Element.table_name => {name: options[:fallback][:for]})
97
101
  .none?
@@ -83,7 +83,7 @@ module Alchemy #:nodoc:
83
83
 
84
84
  if configuration[:belongs_to]
85
85
  class_eval <<-RUBY, __FILE__, __LINE__ + 1
86
- belongs_to :ingredient_association, #{configuration[:belongs_to]}
86
+ belongs_to :ingredient_association, **#{configuration[:belongs_to]}
87
87
 
88
88
  alias_method :#{configuration[:ingredient_column]}, :ingredient_association
89
89
  alias_method :#{configuration[:ingredient_column]}=, :ingredient_association=
@@ -108,9 +108,9 @@ module Alchemy #:nodoc:
108
108
  # Register the current class as has_many association on +Alchemy::Page+ and +Alchemy::Element+ models
109
109
  def register_as_essence_association!
110
110
  klass_name = model_name.to_s
111
- arguments = [:has_many, klass_name.demodulize.tableize.to_sym, through: :contents,
112
- source: :essence, source_type: klass_name]
113
- %w(Page Element).each { |k| "Alchemy::#{k}".constantize.send(*arguments) }
111
+ arguments = [:has_many, klass_name.demodulize.tableize.to_sym]
112
+ kwargs = { through: :contents, source: :essence, source_type: klass_name }
113
+ %w(Page Element).each { |k| "Alchemy::#{k}".constantize.send(*arguments, **kwargs) }
114
114
  end
115
115
  end
116
116
 
@@ -40,7 +40,20 @@ module Alchemy
40
40
  EXCEL_FILE_TYPES = [
41
41
  "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
42
42
  "application/vnd.ms-excel",
43
+ "application/msexcel",
43
44
  "text/csv",
44
45
  ]
46
+
47
+ WORD_FILE_TYPES = [
48
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
49
+ "application/vnd.ms-word",
50
+ "application/msword",
51
+ ]
52
+
53
+ POWERPOINT_FILE_TYPES = [
54
+ "application/vnd.openxmlformats-officedocument.presentationml.presentation",
55
+ "application/vnd.ms-powerpoint",
56
+ "application/mspowerpoint",
57
+ ]
45
58
  end
46
59
  end
@@ -11,7 +11,7 @@ module Alchemy
11
11
  if object.respond_to?(:attribute_fixed?) && object.attribute_fixed?(attribute_name)
12
12
  options[:disabled] = true
13
13
  options[:input_html] = options.fetch(:input_html, {}).merge(
14
- "data-alchemy-tooltip" => Alchemy.t(:attribute_fixed, attribute_name),
14
+ "data-alchemy-tooltip" => Alchemy.t(:attribute_fixed, attribute: attribute_name),
15
15
  )
16
16
  end
17
17
 
data/lib/alchemy/i18n.rb CHANGED
@@ -12,8 +12,8 @@ module Alchemy
12
12
  #
13
13
  # Alchemy.t(:hello)
14
14
  #
15
- def t(msg, *args)
16
- Alchemy::I18n.translate(msg, *args)
15
+ def t(msg, **kwargs)
16
+ Alchemy::I18n.translate(msg, **kwargs)
17
17
  end
18
18
  end
19
19
 
@@ -47,11 +47,10 @@ module Alchemy
47
47
  # world:
48
48
  # hello: Hallo
49
49
  #
50
- def translate(msg, *args)
51
- options = args.extract_options!
50
+ def translate(msg, **options)
52
51
  humanize_default_string!(msg, options)
53
52
  scope = alchemy_scoped_scope(options)
54
- ::I18n.t(msg, options.merge(scope: scope))
53
+ ::I18n.t(msg, **options.merge(scope: scope))
55
54
  end
56
55
 
57
56
  def available_locales
@@ -74,6 +74,7 @@ module Alchemy
74
74
  end
75
75
  mapped_layouts_for_select(layouts)
76
76
  end
77
+ deprecate :layouts_with_own_for_select, deprecator: Alchemy::Deprecation
77
78
 
78
79
  # Returns all layouts that can be used for creating a new page.
79
80
  #
@@ -132,7 +132,9 @@ module Alchemy
132
132
  end
133
133
 
134
134
  def namespaced_resource_name
135
- @_namespaced_resource_name ||= namespaced_resources_name.singularize
135
+ @_namespaced_resource_name ||= begin
136
+ namespaced_resources_name.to_s.singularize
137
+ end.to_sym # Rails >= 6.0.3.7 needs symbols in polymorphic routes
136
138
  end
137
139
 
138
140
  def namespaced_resources_name
@@ -140,13 +142,13 @@ module Alchemy
140
142
  resource_name_array = resource_array.dup
141
143
  resource_name_array.delete(engine_name) if in_engine?
142
144
  resource_name_array.join("_")
143
- end
145
+ end.to_sym # Rails >= 6.0.3.7 needs symbols in polymorphic routes
144
146
  end
145
147
 
146
148
  def namespace_for_scope
147
149
  namespace_array = namespace_diff
148
150
  namespace_array.delete(engine_name) if in_engine?
149
- namespace_array
151
+ namespace_array.map(&:to_sym) # Rails >= 6.0.3.7 needs symbols in polymorphic routes
150
152
  end
151
153
 
152
154
  # Returns an array of underscored association names
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Alchemy
4
+ module TestSupport
5
+ class << self
6
+ def factory_paths
7
+ Dir[
8
+ ::Alchemy::Engine.root.join("lib", "alchemy", "test_support", "factories", "*_factory.rb")
9
+ ].map { |path| path.sub(/.rb\z/, "") }
10
+ end
11
+ deprecate factory_paths: :factories_path, deprecator: Alchemy::Deprecation
12
+
13
+ def factories_path
14
+ ::Alchemy::Engine.root.join("lib", "alchemy", "test_support", "factories")
15
+ end
16
+ end
17
+ end
18
+ end
@@ -1,9 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "shoulda-matchers"
4
- require "alchemy/test_support/factories/page_factory"
5
- require "alchemy/test_support/factories/element_factory"
6
- require "alchemy/test_support/factories/content_factory"
7
4
 
8
5
  RSpec.shared_examples_for "an essence" do
9
6
  let(:element) { Alchemy::Element.new }
@@ -1,5 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ Alchemy::Deprecation.warn <<~MSG
4
+ Please require factories using FactoryBots preferred approach:
5
+
6
+ # spec/rails_helper.rb
7
+
8
+ require 'alchemy/test_support'
9
+
10
+ FactoryBot.definition_file_paths.append(Alchemy::TestSupport.factories_path)
11
+ FactoryBot.find_definitions
12
+ MSG
13
+
3
14
  Dir["#{File.dirname(__FILE__)}/factories/*.rb"].sort.each do |file|
4
15
  require file
5
16
  end
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "factory_bot"
4
-
5
3
  FactoryBot.define do
6
4
  factory :alchemy_attachment, class: "Alchemy::Attachment" do
7
5
  file do
@@ -1,11 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "factory_bot"
4
- require "alchemy/test_support/factories/element_factory"
5
- require "alchemy/test_support/factories/essence_file_factory"
6
- require "alchemy/test_support/factories/essence_picture_factory"
7
- require "alchemy/test_support/factories/essence_text_factory"
8
-
9
3
  FactoryBot.define do
10
4
  factory :alchemy_content, class: "Alchemy::Content" do
11
5
  name { "text" }
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "factory_bot"
4
-
5
3
  FactoryBot.define do
6
4
  factory :alchemy_dummy_user, class: "DummyUser" do
7
5
  sequence(:email) { |n| "john.#{n}@doe.com" }
@@ -1,8 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "factory_bot"
4
- require "alchemy/test_support/factories/page_factory"
5
-
6
3
  FactoryBot.define do
7
4
  factory :alchemy_element, class: "Alchemy::Element" do
8
5
  name { "article" }
@@ -1,8 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "factory_bot"
4
- require "alchemy/test_support/factories/attachment_factory"
5
-
6
3
  FactoryBot.define do
7
4
  factory :alchemy_essence_file, class: "Alchemy::EssenceFile" do
8
5
  attachment factory: :alchemy_attachment
@@ -1,8 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "factory_bot"
4
- require "alchemy/test_support/factories/page_factory"
5
-
6
3
  FactoryBot.define do
7
4
  factory :alchemy_essence_page, class: "Alchemy::EssencePage" do
8
5
  page factory: :alchemy_page
@@ -1,9 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "factory_bot"
4
- require "alchemy/test_support/factories/content_factory"
5
- require "alchemy/test_support/factories/picture_factory"
6
-
7
3
  FactoryBot.define do
8
4
  factory :alchemy_essence_picture, class: "Alchemy::EssencePicture" do
9
5
  picture factory: :alchemy_picture
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "factory_bot"
4
-
5
3
  FactoryBot.define do
6
4
  factory :alchemy_essence_text, class: "Alchemy::EssenceText" do
7
5
  body { "This is a headline" }
@@ -1,8 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "factory_bot"
4
- require "alchemy/test_support/factories/site_factory"
5
-
6
3
  FactoryBot.define do
7
4
  factory :alchemy_language, class: "Alchemy::Language" do
8
5
  name { "Your Language" }