alchemy_cms 3.5.0 → 3.6.0

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.
Files changed (118) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +82 -26
  3. data/.travis.yml +1 -1
  4. data/CHANGELOG.md +23 -1
  5. data/README.md +14 -5
  6. data/Rakefile +0 -1
  7. data/alchemy_cms.gemspec +2 -5
  8. data/app/assets/images/alchemy/alchemy-logo.png +0 -0
  9. data/app/assets/javascripts/alchemy/admin.js +1 -1
  10. data/app/assets/javascripts/alchemy/alchemy.base.js.coffee +4 -8
  11. data/app/assets/javascripts/alchemy/alchemy.buttons.js.coffee +2 -2
  12. data/app/assets/javascripts/alchemy/alchemy.datepicker.js.coffee +18 -27
  13. data/app/assets/javascripts/alchemy/alchemy.dialog.js.coffee +1 -1
  14. data/app/assets/javascripts/alchemy/alchemy.element_editors.js.coffee +3 -8
  15. data/app/assets/javascripts/alchemy/alchemy.elements_window.js.coffee +1 -1
  16. data/app/assets/javascripts/alchemy/alchemy.gui.js.coffee +0 -1
  17. data/app/assets/javascripts/alchemy/alchemy.page_sorter.js +22 -46
  18. data/app/assets/javascripts/alchemy/alchemy.preview_window.js.coffee +1 -1
  19. data/app/assets/javascripts/alchemy/alchemy.sitemap.js.coffee +2 -2
  20. data/app/assets/javascripts/alchemy/alchemy.spinner.js +32 -0
  21. data/app/assets/javascripts/alchemy/alchemy.tinymce.js.coffee +1 -1
  22. data/app/assets/javascripts/alchemy/templates/index.js +1 -0
  23. data/app/assets/javascripts/alchemy/templates/spinner.hbs +7 -0
  24. data/app/assets/stylesheets/alchemy/_extends.scss +1 -0
  25. data/app/assets/stylesheets/alchemy/admin.scss +1 -0
  26. data/app/assets/stylesheets/alchemy/base.scss +1 -7
  27. data/app/assets/stylesheets/alchemy/buttons.scss +1 -5
  28. data/app/assets/stylesheets/alchemy/dialogs.scss +0 -4
  29. data/app/assets/stylesheets/alchemy/elements.scss +2 -6
  30. data/app/assets/stylesheets/alchemy/frame.scss +0 -5
  31. data/app/assets/stylesheets/alchemy/image_library.scss +0 -13
  32. data/app/assets/stylesheets/alchemy/sitemap.scss +5 -34
  33. data/app/assets/stylesheets/alchemy/spinner.scss +52 -0
  34. data/app/controllers/alchemy/admin/attachments_controller.rb +12 -11
  35. data/app/controllers/alchemy/admin/base_controller.rb +3 -7
  36. data/app/controllers/alchemy/admin/essence_pictures_controller.rb +2 -1
  37. data/app/controllers/alchemy/admin/languages_controller.rb +3 -8
  38. data/app/controllers/alchemy/admin/pictures_controller.rb +11 -7
  39. data/app/controllers/alchemy/admin/resources_controller.rb +1 -1
  40. data/app/controllers/alchemy/attachments_controller.rb +2 -0
  41. data/app/controllers/alchemy/base_controller.rb +4 -5
  42. data/app/controllers/concerns/alchemy/admin/uploader_responses.rb +1 -1
  43. data/app/helpers/alchemy/admin/base_helper.rb +17 -8
  44. data/app/helpers/alchemy/admin/tags_helper.rb +31 -18
  45. data/app/helpers/alchemy/base_helper.rb +1 -1
  46. data/app/helpers/alchemy/pages_helper.rb +4 -7
  47. data/app/models/alchemy/attachment.rb +4 -0
  48. data/app/models/alchemy/cell.rb +1 -1
  49. data/app/models/alchemy/element.rb +6 -12
  50. data/app/models/alchemy/element/definitions.rb +2 -2
  51. data/app/models/alchemy/element/element_contents.rb +1 -1
  52. data/app/models/alchemy/essence_picture_view.rb +14 -2
  53. data/app/models/alchemy/language.rb +4 -4
  54. data/app/models/alchemy/page.rb +25 -28
  55. data/app/models/alchemy/page/page_elements.rb +1 -1
  56. data/app/models/alchemy/page/page_natures.rb +1 -1
  57. data/app/models/alchemy/picture.rb +5 -1
  58. data/app/models/alchemy/site.rb +27 -12
  59. data/app/views/alchemy/admin/attachments/_tag_list.html.erb +14 -13
  60. data/app/views/alchemy/admin/dashboard/info.html.erb +1 -1
  61. data/app/views/alchemy/admin/layoutpages/edit.html.erb +1 -1
  62. data/app/views/alchemy/admin/pages/_create_language_form.html.erb +1 -1
  63. data/app/views/alchemy/admin/pages/_form.html.erb +1 -1
  64. data/app/views/alchemy/admin/pages/_new_page_form.html.erb +1 -1
  65. data/app/views/alchemy/admin/pages/_page.html.erb +1 -1
  66. data/app/views/alchemy/admin/pages/_sitemap.html.erb +2 -2
  67. data/app/views/alchemy/admin/pages/configure_external.html.erb +1 -1
  68. data/app/views/alchemy/admin/pages/index.html.erb +5 -7
  69. data/app/views/alchemy/admin/pages/sort.html.erb +19 -0
  70. data/app/views/alchemy/admin/pages/update.js.erb +1 -1
  71. data/app/views/alchemy/admin/pictures/_archive.html.erb +19 -19
  72. data/app/views/alchemy/admin/pictures/_tag_list.html.erb +3 -4
  73. data/app/views/alchemy/admin/resources/_tag_list.html.erb +3 -4
  74. data/app/views/alchemy/admin/resources/edit.html.erb +1 -1
  75. data/app/views/alchemy/admin/resources/new.html.erb +1 -1
  76. data/app/views/alchemy/pages/_meta_data.html.erb +1 -1
  77. data/app/views/alchemy/pages/show.rss.builder +0 -2
  78. data/app/views/alchemy/welcome.html.erb +1 -1
  79. data/app/views/layouts/alchemy/admin.html.erb +1 -1
  80. data/config/locales/alchemy.de.yml +4 -4
  81. data/config/locales/alchemy.en.yml +4 -4
  82. data/config/locales/alchemy.es.yml +3 -3
  83. data/config/locales/alchemy.fr.yml +4 -4
  84. data/config/locales/alchemy.it.yml +3 -3
  85. data/config/locales/alchemy.nl.yml +4 -4
  86. data/config/locales/alchemy.ru.yml +3 -3
  87. data/lib/alchemy/auth_accessors.rb +6 -6
  88. data/lib/alchemy/cache_digests/template_tracker.rb +5 -5
  89. data/lib/alchemy/controller_actions.rb +1 -6
  90. data/lib/alchemy/engine.rb +0 -53
  91. data/lib/alchemy/errors.rb +12 -3
  92. data/lib/alchemy/i18n.rb +1 -1
  93. data/lib/alchemy/logger.rb +1 -1
  94. data/lib/alchemy/page_layout.rb +5 -5
  95. data/lib/alchemy/seeder.rb +16 -49
  96. data/lib/alchemy/tasks/helpers.rb +1 -1
  97. data/lib/alchemy/test_support/config_stubbing.rb +28 -0
  98. data/lib/alchemy/test_support/essence_shared_examples.rb +6 -6
  99. data/lib/alchemy/test_support/factories/language_factory.rb +1 -1
  100. data/lib/alchemy/test_support/factories/page_factory.rb +7 -0
  101. data/lib/alchemy/test_support/factories/site_factory.rb +6 -0
  102. data/lib/alchemy/test_support/shared_contexts.rb +14 -0
  103. data/lib/alchemy/test_support/shared_uploader_examples.rb +10 -0
  104. data/lib/alchemy/touching.rb +1 -1
  105. data/lib/alchemy/version.rb +1 -1
  106. data/lib/alchemy_cms.rb +56 -1
  107. data/lib/{alchemy/kaminari → kaminari}/scoped_pagination_url_helper.rb +0 -0
  108. data/lib/rails/generators/alchemy/base.rb +1 -1
  109. data/lib/rails/generators/alchemy/elements/elements_generator.rb +2 -1
  110. data/lib/rails/generators/alchemy/page_layouts/page_layouts_generator.rb +2 -1
  111. data/lib/rails/generators/alchemy/site_layouts/site_layouts_generator.rb +2 -1
  112. data/lib/tasks/alchemy/tidy.rake +91 -89
  113. data/lib/tasks/alchemy/upgrade.rake +15 -15
  114. metadata +29 -14
  115. data/app/assets/javascripts/alchemy/alchemy.spinner.js.coffee +0 -49
  116. data/app/views/alchemy/admin/pages/sort.js.erb +0 -4
  117. data/vendor/assets/javascripts/handlebars.js +0 -4608
  118. data/vendor/assets/javascripts/spin.min.js +0 -1
@@ -44,11 +44,7 @@ module Alchemy
44
44
  else
45
45
  render_errors_or_redirect(
46
46
  @attachment,
47
- admin_attachments_path(
48
- per_page: params[:per_page],
49
- page: params[:page],
50
- q: params[:q]
51
- ),
47
+ admin_attachments_path(search_params),
52
48
  Alchemy.t("File successfully updated")
53
49
  )
54
50
  end
@@ -57,11 +53,7 @@ module Alchemy
57
53
  def destroy
58
54
  name = @attachment.name
59
55
  @attachment.destroy
60
- @url = admin_attachments_url(
61
- per_page: params[:per_page],
62
- page: params[:page],
63
- q: params[:q]
64
- )
56
+ @url = admin_attachments_url(search_params)
65
57
  flash[:notice] = Alchemy.t('File deleted successfully', name: name)
66
58
  end
67
59
 
@@ -75,9 +67,18 @@ module Alchemy
75
67
 
76
68
  private
77
69
 
70
+ def search_params
71
+ params.except(:attachment, :id).permit(
72
+ :file_type,
73
+ :page,
74
+ {q: resource_handler.search_field_name},
75
+ :tagged_with
76
+ )
77
+ end
78
+
78
79
  def handle_uploader_response(status:)
79
80
  if @attachment.valid?
80
- render succesful_uploader_response(file: @attachment, status: status)
81
+ render successful_uploader_response(file: @attachment, status: status)
81
82
  else
82
83
  render failed_uploader_response(file: @attachment)
83
84
  end
@@ -138,17 +138,13 @@ module Alchemy
138
138
  #
139
139
  def options_from_params
140
140
  case params[:options]
141
- when ''
141
+ when '', nil
142
142
  {}
143
143
  when String
144
144
  JSON.parse(params[:options])
145
- when Hash
146
- params[:options]
147
- when Array
148
- params[:options]
149
145
  else
150
- {}
151
- end.symbolize_keys
146
+ params[:options].permit!.to_h
147
+ end.deep_symbolize_keys
152
148
  end
153
149
 
154
150
  # This method decides if we want to raise an exception or not.
@@ -1,6 +1,7 @@
1
1
  module Alchemy
2
2
  module Admin
3
3
  class EssencePicturesController < Alchemy::Admin::BaseController
4
+ FLOAT_REGEX = /\A\d+(\.\d+)?\z/
4
5
  authorize_resource class: Alchemy::EssencePicture
5
6
 
6
7
  before_action :load_essence_picture, only: [:edit, :crop, :update]
@@ -92,7 +93,7 @@ module Alchemy
92
93
  # aspect ratio, don't specify a size or only width or height.
93
94
  #
94
95
  def ratio_from_size_or_params
95
- if @min_size.value?(0) && @options[:fixed_ratio]
96
+ if @min_size.value?(0) && @options[:fixed_ratio].to_s =~ FLOAT_REGEX
96
97
  @options[:fixed_ratio].to_f
97
98
  elsif !@min_size[:width].zero? && !@min_size[:height].zero?
98
99
  @min_size[:width].to_f / @min_size[:height].to_f
@@ -7,14 +7,9 @@ module Alchemy
7
7
  end
8
8
 
9
9
  def new
10
- @language = Language.new
11
- @language.page_layout = configured_page_layout || @language.page_layout
12
- end
13
-
14
- private
15
-
16
- def configured_page_layout
17
- Config.get(:default_language).try('[]', 'page_layout')
10
+ @language = Language.new(
11
+ page_layout: Config.get(:default_language)['page_layout']
12
+ )
18
13
  end
19
14
  end
20
15
  end
@@ -31,7 +31,7 @@ module Alchemy
31
31
  @picture = Picture.new(picture_params)
32
32
  @picture.name = @picture.humanized_name
33
33
  if @picture.save
34
- render succesful_uploader_response(file: @picture)
34
+ render successful_uploader_response(file: @picture)
35
35
  else
36
36
  render failed_uploader_response(file: @picture)
37
37
  end
@@ -136,12 +136,16 @@ module Alchemy
136
136
  end
137
137
 
138
138
  def redirect_to_index
139
- do_redirect_to admin_pictures_path(
140
- filter: params[:filter].presence,
141
- page: params[:page].presence,
142
- q: params[:q].presence,
143
- size: params[:size].presence,
144
- tagged_with: params[:tagged_with].presence
139
+ do_redirect_to admin_pictures_path(search_params)
140
+ end
141
+
142
+ def search_params
143
+ params.except(:id, :picture_ids).permit(
144
+ :filter,
145
+ :page,
146
+ {q: resource_handler.search_field_name},
147
+ :size,
148
+ :tagged_with
145
149
  )
146
150
  end
147
151
 
@@ -97,7 +97,7 @@ module Alchemy
97
97
  when :destroy
98
98
  verb = "removed"
99
99
  end
100
- flash[:notice] = Alchemy.t("#{resource_handler.resource_name.classify} successfully #{verb}", default: Alchemy.t("Succesfully #{verb}"))
100
+ flash[:notice] = Alchemy.t("#{resource_handler.resource_name.classify} successfully #{verb}", default: Alchemy.t("Successfully #{verb}"))
101
101
  end
102
102
 
103
103
  def is_alchemy_module?
@@ -5,6 +5,7 @@ module Alchemy
5
5
 
6
6
  # sends file inline. i.e. for viewing pdfs/movies in browser
7
7
  def show
8
+ response.headers['Content-Length'] = @attachment.file.size.to_s
8
9
  send_file(
9
10
  @attachment.file.path,
10
11
  {
@@ -17,6 +18,7 @@ module Alchemy
17
18
 
18
19
  # sends file as attachment. aka download
19
20
  def download
21
+ response.headers['Content-Length'] = @attachment.file.size.to_s
20
22
  send_file(
21
23
  @attachment.file.path, {
22
24
  filename: @attachment.file_name,
@@ -51,11 +51,10 @@ module Alchemy
51
51
 
52
52
  def permission_denied(exception = nil)
53
53
  if exception
54
- Rails.logger.debug <<-WARN
55
-
56
- /!\\ Failed to permit #{exception.action} on #{exception.subject.inspect} for:
57
- #{current_alchemy_user.inspect}
58
- WARN
54
+ Rails.logger.debug <<-WARN.strip_heredoc
55
+ /!\\ Failed to permit #{exception.action} on #{exception.subject.inspect} for:
56
+ #{current_alchemy_user.inspect}
57
+ WARN
59
58
  end
60
59
  if current_alchemy_user
61
60
  handle_redirect_for_user
@@ -3,7 +3,7 @@ module Alchemy
3
3
  module UploaderResponses
4
4
  extend ActiveSupport::Concern
5
5
 
6
- def succesful_uploader_response(file:, status: :created)
6
+ def successful_uploader_response(file:, status: :created)
7
7
  message = Alchemy.t(:upload_success,
8
8
  scope: [:uploader, file.class.model_name.i18n_key],
9
9
  name: file.name
@@ -312,9 +312,9 @@ module Alchemy
312
312
  }
313
313
  options = defaults.merge(options)
314
314
  content_for(:toolbar) do
315
- content = <<-CONTENT
316
- #{options[:buttons].map { |button_options| toolbar_button(button_options) }.join}
317
- #{render('alchemy/admin/partials/search_form', url: options[:search_url]) if options[:search]}
315
+ content = <<-CONTENT.strip_heredoc
316
+ #{options[:buttons].map { |button_options| toolbar_button(button_options) }.join}
317
+ #{render('alchemy/admin/partials/search_form', url: options[:search_url]) if options[:search]}
318
318
  CONTENT
319
319
  content.html_safe
320
320
  end
@@ -332,13 +332,18 @@ module Alchemy
332
332
 
333
333
  # Renders a textfield ready to display a datepicker
334
334
  #
335
- # Uses a HTML5 +<input type="date">+ field.
336
335
  # A Javascript observer converts this into a fancy Datepicker.
337
336
  # If you pass +'datetime'+ as +:type+ the datepicker will also have a Time select.
337
+ # If you pass +'time'+ as +:type+ the datepicker will only have a Time select.
338
338
  #
339
- # The date value gets localized via +I18n.l+. The format on Time and Date is +datepicker+
339
+ # The date value gets localized via +I18n.l+. The format on Time and Date is +datepicker+, +timepicker+
340
340
  # or +datetimepicker+, if you pass another +type+.
341
341
  #
342
+ # This helper always renders "text" as input type because:
343
+ # HTML5 supports input types like 'date' but Browsers are using the users OS settings
344
+ # to validate the input format. Since Alchemy is localized in the backend the date formats
345
+ # should be aligned with the users locale setting in the backend but not the OS settings.
346
+ #
342
347
  # === Date Example
343
348
  #
344
349
  # <%= alchemy_datepicker(@person, :birthday) %>
@@ -347,13 +352,17 @@ module Alchemy
347
352
  #
348
353
  # <%= alchemy_datepicker(@page, :public_on, type: 'datetime') %>
349
354
  #
355
+ # === Time Example
356
+ #
357
+ # <%= alchemy_datepicker(@meeting, :starts_at, type: 'time') %>
358
+ #
350
359
  # @param [ActiveModel::Base] object
351
360
  # An instance of a model
352
361
  # @param [String or Symbol] method
353
362
  # The attribute method to be called for the date value
354
363
  #
355
- # @option html_options [String] :type (date)
356
- # The type of text field
364
+ # @option html_options [String] :data-datepicker-type (type)
365
+ # The value of the data attribute for the type
357
366
  # @option html_options [String] :class (type)
358
367
  # CSS classes of the input field
359
368
  # @option html_options [String] :value (value of method on object)
@@ -366,7 +375,7 @@ module Alchemy
366
375
  value = date ? l(date, format: "#{type}picker".to_sym) : nil
367
376
 
368
377
  text_field object.class.name.demodulize.underscore.to_sym,
369
- method.to_sym, {type: type, class: type, value: value}.merge(html_options)
378
+ method.to_sym, {type: "text", class: type, "data-datepicker-type" => type, value: value}.merge(html_options)
370
379
  end
371
380
 
372
381
  # Merges the params-hash with the given hash
@@ -9,16 +9,16 @@ module Alchemy
9
9
  # @return [String]
10
10
  # A HTML string containing <tt><li></tt> tags
11
11
  #
12
- def render_tag_list(class_name, params)
12
+ def render_tag_list(class_name)
13
13
  raise ArgumentError, 'Please provide a String as class_name' if class_name.nil?
14
14
  li_s = []
15
15
  class_name.constantize.tag_counts.sort { |x, y| x.name.downcase <=> y.name.downcase }.each do |tag|
16
16
  tags = filtered_by_tag?(tag) ? tag_filter(remove: tag) : tag_filter(add: tag)
17
- li_s << content_tag('li', name: tag.name, class: tag_list_tag_active?(tag, params) ? 'active' : nil) do
17
+ li_s << content_tag('li', name: tag.name, class: tag_list_tag_active?(tag) ? 'active' : nil) do
18
18
  link_to(
19
19
  "#{tag.name} (#{tag.count})",
20
20
  url_for(
21
- params.delete_if { |k, _v| k == "page" }.merge(
21
+ tag_list_params.reject { |k, _v| k == "page" }.merge(
22
22
  action: 'index',
23
23
  tagged_with: tags
24
24
  )
@@ -39,14 +39,14 @@ module Alchemy
39
39
  # url params
40
40
  # @return [Boolean]
41
41
  #
42
- def tag_list_tag_active?(tag, params)
43
- params[:tagged_with].to_s.split(',').include?(tag.name)
42
+ def tag_list_tag_active?(tag)
43
+ tag_list_params[:tagged_with].to_s.split(',').include?(tag.name)
44
44
  end
45
45
 
46
46
  # Checks if the tagged_with param contains the given tag
47
47
  def filtered_by_tag?(tag)
48
- if params[:tagged_with].present?
49
- tags = params[:tagged_with].split(',')
48
+ if tag_list_params[:tagged_with].present?
49
+ tags = tag_list_params[:tagged_with].split(',')
50
50
  tags.include?(tag.name)
51
51
  else
52
52
  false
@@ -55,8 +55,8 @@ module Alchemy
55
55
 
56
56
  # Adds the given tag to the tag filter.
57
57
  def add_to_tag_filter(tag)
58
- if params[:tagged_with].present?
59
- tags = params[:tagged_with].split(',')
58
+ if tag_list_params[:tagged_with].present?
59
+ tags = tag_list_params[:tagged_with].split(',')
60
60
  tags << tag.name
61
61
  else
62
62
  [tag.name]
@@ -65,8 +65,8 @@ module Alchemy
65
65
 
66
66
  # Removes the given tag from the tag filter.
67
67
  def remove_from_tag_filter(tag)
68
- if params[:tagged_with].present?
69
- tags = params[:tagged_with].split(',')
68
+ if tag_list_params[:tagged_with].present?
69
+ tags = tag_list_params[:tagged_with].split(',')
70
70
  tags.delete_if { |t| t == tag.name }
71
71
  else
72
72
  []
@@ -84,17 +84,30 @@ module Alchemy
84
84
  # ** :remove (ActsAsTaggableOn::Tag) - The tag that should be removed from the tag-filter
85
85
  #
86
86
  def tag_filter(options = {})
87
- case
88
- when options[:add]
89
- taglist = add_to_tag_filter(options[:add]) if options[:add]
90
- when options[:remove]
91
- taglist = remove_from_tag_filter(options[:remove]) if options[:remove]
92
- else
93
- return params[:tagged_with]
87
+ if options[:add]
88
+ taglist = add_to_tag_filter(options[:add])
89
+ elsif options[:remove]
90
+ taglist = remove_from_tag_filter(options[:remove])
91
+ else
92
+ return tag_list_params[:tagged_with]
94
93
  end
95
94
  return nil if taglist.blank?
96
95
  taglist.uniq.join(',')
97
96
  end
97
+
98
+ def tag_list_params
99
+ params.permit(
100
+ :controller,
101
+ :content_id,
102
+ :element_id,
103
+ :options,
104
+ :swap,
105
+ :use_route,
106
+ :tagged_with,
107
+ :filter,
108
+ q: params.fetch(:q, {}).keys
109
+ )
110
+ end
98
111
  end
99
112
  end
100
113
  end
@@ -14,7 +14,7 @@ module Alchemy
14
14
  # Logs a message in the Rails logger (warn level)
15
15
  # and optionally displays an error message to the user.
16
16
  def warning(message, text = nil)
17
- Logger.warn(message, caller.first)
17
+ Logger.warn(message, caller(0..0))
18
18
  unless text.nil?
19
19
  warning = content_tag('p', class: 'content_editor_error') do
20
20
  render_icon('warning') + text
@@ -166,7 +166,7 @@ module Alchemy
166
166
  pages = page.children.accessible_by(current_ability, :see)
167
167
  pages = pages.restricted if options.delete(:restricted_only)
168
168
  if depth = options[:deepness]
169
- pages = pages.where("#{Page.table_name}.depth <= #{depth}")
169
+ pages = pages.where('depth <= ?', depth)
170
170
  end
171
171
  if options[:reverse]
172
172
  pages.reverse!
@@ -246,15 +246,12 @@ module Alchemy
246
246
  end
247
247
 
248
248
  if options.delete(:reverse)
249
- pages.to_a.reverse!
249
+ pages = pages.reorder('lft DESC')
250
250
  end
251
251
 
252
252
  if options[:without].present?
253
- if options[:without].class == Array
254
- pages = pages.to_a - options[:without]
255
- else
256
- pages.to_a.delete(options[:without])
257
- end
253
+ without = options.delete(:without)
254
+ pages = pages.where.not(id: without.try(:collect, &:id) || without.id)
258
255
  end
259
256
 
260
257
  render 'alchemy/breadcrumb/wrapper', pages: pages, options: options
@@ -36,6 +36,10 @@ module Alchemy
36
36
 
37
37
  # We need to define this method here to have it available in the validations below.
38
38
  class << self
39
+ def searchable_alchemy_resource_attributes
40
+ %w(name file_name)
41
+ end
42
+
39
43
  def allowed_filetypes
40
44
  Config.get(:uploader).fetch('allowed_filetypes', {}).fetch('alchemy/attachments', [])
41
45
  end
@@ -56,7 +56,7 @@ module Alchemy
56
56
  private
57
57
 
58
58
  def read_yml_file
59
- ::YAML.load(ERB.new(File.read(yml_file_path)).result) || []
59
+ ::YAML.safe_load(ERB.new(File.read(yml_file_path)).result, [], [], true) || []
60
60
  end
61
61
 
62
62
  def yml_file_path
@@ -211,7 +211,8 @@ module Alchemy
211
211
  # Pass an element name to get next of this kind.
212
212
  #
213
213
  def next(name = nil)
214
- previous_or_next('>', name)
214
+ elements = page.elements.published.where('position > ?', position)
215
+ select_element(elements, name, :asc)
215
216
  end
216
217
 
217
218
  # Returns previous public element from same page.
@@ -219,7 +220,8 @@ module Alchemy
219
220
  # Pass an element name to get previous of this kind.
220
221
  #
221
222
  def prev(name = nil)
222
- previous_or_next('<', name)
223
+ elements = page.elements.published.where('position < ?', position)
224
+ select_element(elements, name, :desc)
223
225
  end
224
226
 
225
227
  # Stores the page into +touchable_pages+ (Pages that have to be touched after updating the element).
@@ -313,17 +315,9 @@ module Alchemy
313
315
 
314
316
  private
315
317
 
316
- # Returns previous or next public element from same page.
317
- #
318
- # @param [String]
319
- # Pass '>' or '<' to find next or previous public element.
320
- # @param [String]
321
- # Pass an element name to get previous of this kind.
322
- #
323
- def previous_or_next(dir, name = nil)
324
- elements = page.elements.published.where("#{self.class.table_name}.position #{dir} #{position}")
318
+ def select_element(elements, name, order)
325
319
  elements = elements.named(name) if name.present?
326
- elements.reorder("position #{dir == '>' ? 'ASC' : 'DESC'}").limit(1).first
320
+ elements.reorder(position: order).limit(1).first
327
321
  end
328
322
 
329
323
  # Returns all cells from given page this element could be placed in.
@@ -26,7 +26,7 @@ module Alchemy
26
26
  #
27
27
  def read_definitions_file
28
28
  if ::File.exist?(definitions_file_path)
29
- ::YAML.load(ERB.new(File.read(definitions_file_path)).result) || []
29
+ ::YAML.safe_load(ERB.new(File.read(definitions_file_path)).result, [Regexp, Date], [], true) || []
30
30
  else
31
31
  raise LoadError, "Could not find elements.yml file! Please run `rails generate alchemy:scaffold`"
32
32
  end
@@ -46,7 +46,7 @@ module Alchemy
46
46
  definition
47
47
  else
48
48
  log_warning "Could not find element definition for #{name}. Please check your elements.yml file!"
49
- return {}
49
+ {}
50
50
  end
51
51
  end
52
52
  end