alchemy_cms 3.3.3 → 3.4.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (133) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +1 -1
  3. data/CHANGELOG.md +42 -4
  4. data/README.md +7 -3
  5. data/alchemy_cms.gemspec +1 -0
  6. data/app/assets/javascripts/alchemy/{alchemy.js → admin.js} +1 -2
  7. data/app/assets/javascripts/alchemy/alchemy.datepicker.js.coffee +28 -25
  8. data/app/assets/javascripts/alchemy/alchemy.dialog.js.coffee +1 -0
  9. data/app/assets/javascripts/alchemy/alchemy.i18n.js.coffee +7 -1
  10. data/app/assets/javascripts/alchemy/alchemy.link_dialog.js.coffee +1 -1
  11. data/app/assets/javascripts/alchemy/alchemy.sitemap.js.coffee +26 -0
  12. data/app/assets/javascripts/alchemy/alchemy.translations.js.coffee +56 -1
  13. data/app/assets/stylesheets/alchemy/_variables.scss +1 -0
  14. data/app/assets/stylesheets/alchemy/admin.scss +2 -1
  15. data/app/assets/stylesheets/alchemy/archive.scss +7 -0
  16. data/app/assets/stylesheets/alchemy/base.scss +0 -42
  17. data/app/assets/stylesheets/alchemy/buttons.scss +2 -1
  18. data/app/assets/stylesheets/alchemy/form_fields.scss +9 -0
  19. data/app/assets/stylesheets/alchemy/forms.scss +36 -10
  20. data/app/assets/stylesheets/alchemy/frame.scss +12 -1
  21. data/app/assets/stylesheets/alchemy/icons.scss +1 -1
  22. data/app/assets/stylesheets/alchemy/jquery-ui.scss +0 -260
  23. data/app/assets/stylesheets/alchemy/jquery.datetimepicker.scss +507 -0
  24. data/app/assets/stylesheets/alchemy/lists.scss +62 -0
  25. data/app/assets/stylesheets/alchemy/selects.scss +9 -2
  26. data/app/assets/stylesheets/alchemy/sitemap.scss +28 -51
  27. data/app/assets/stylesheets/alchemy/toolbar.scss +0 -2
  28. data/app/controllers/alchemy/admin/attachments_controller.rb +4 -6
  29. data/app/controllers/alchemy/admin/base_controller.rb +2 -2
  30. data/app/controllers/alchemy/admin/dashboard_controller.rb +2 -2
  31. data/app/controllers/alchemy/admin/languages_controller.rb +5 -0
  32. data/app/controllers/alchemy/admin/pages_controller.rb +14 -5
  33. data/app/controllers/alchemy/admin/resources_controller.rb +17 -1
  34. data/app/controllers/alchemy/base_controller.rb +1 -0
  35. data/app/controllers/alchemy/messages_controller.rb +1 -1
  36. data/app/controllers/alchemy/pages_controller.rb +16 -26
  37. data/app/controllers/alchemy/pictures_controller.rb +23 -10
  38. data/app/controllers/concerns/alchemy/page_redirects.rb +7 -7
  39. data/app/helpers/alchemy/admin/base_helper.rb +22 -19
  40. data/app/helpers/alchemy/admin/essences_helper.rb +26 -11
  41. data/app/helpers/alchemy/admin/pages_helper.rb +2 -1
  42. data/app/helpers/alchemy/essences_helper.rb +0 -35
  43. data/app/helpers/alchemy/url_helper.rb +1 -1
  44. data/app/mailers/alchemy/base_mailer.rb +18 -0
  45. data/app/mailers/alchemy/{messages.rb → messages_mailer.rb} +1 -1
  46. data/app/models/alchemy/attachment.rb +9 -0
  47. data/app/models/alchemy/cell.rb +1 -1
  48. data/app/models/alchemy/essence_picture.rb +4 -1
  49. data/app/models/alchemy/essence_picture_view.rb +68 -0
  50. data/app/models/alchemy/language.rb +8 -10
  51. data/app/models/alchemy/language/code.rb +4 -1
  52. data/app/models/alchemy/page.rb +69 -26
  53. data/app/models/alchemy/page/page_natures.rb +22 -0
  54. data/app/models/alchemy/page/page_scopes.rb +20 -6
  55. data/app/models/alchemy/picture.rb +37 -4
  56. data/app/models/alchemy/site.rb +8 -0
  57. data/app/serializers/alchemy/page_tree_serializer.rb +1 -1
  58. data/app/views/alchemy/admin/attachments/_archive_overlay.html.erb +9 -6
  59. data/app/views/alchemy/admin/attachments/_file_to_assign.html.erb +11 -9
  60. data/app/views/alchemy/admin/attachments/_filter_bar.html.erb +32 -0
  61. data/app/views/alchemy/admin/attachments/_overlay_file_list.html.erb +14 -2
  62. data/app/views/alchemy/admin/attachments/index.html.erb +10 -9
  63. data/app/views/alchemy/admin/dashboard/_locked_pages.html.erb +20 -9
  64. data/app/views/alchemy/admin/dashboard/_recent_pages.html.erb +11 -1
  65. data/app/views/alchemy/admin/languages/_form.html.erb +1 -0
  66. data/app/views/alchemy/admin/languages/index.html.erb +7 -8
  67. data/app/views/alchemy/admin/layoutpages/_layoutpage.html.erb +1 -1
  68. data/app/views/alchemy/admin/layoutpages/index.html.erb +2 -2
  69. data/app/views/alchemy/admin/pages/_current_page.html.erb +4 -0
  70. data/app/views/alchemy/admin/pages/_form.html.erb +16 -1
  71. data/app/views/alchemy/admin/pages/_locked_page.html.erb +2 -12
  72. data/app/views/alchemy/admin/pages/_page.html.erb +1 -1
  73. data/app/views/alchemy/admin/pages/_page_for_links.html.erb +2 -2
  74. data/app/views/alchemy/admin/pages/_page_status.html.erb +23 -11
  75. data/app/views/alchemy/admin/pages/edit.html.erb +2 -1
  76. data/app/views/alchemy/admin/pages/index.html.erb +2 -2
  77. data/app/views/alchemy/admin/partials/_language_tree_select.html.erb +3 -0
  78. data/app/views/alchemy/admin/partials/_site_select.html.erb +9 -0
  79. data/app/views/alchemy/admin/pictures/_picture.html.erb +6 -1
  80. data/app/views/alchemy/admin/pictures/_picture_to_assign.html.erb +2 -1
  81. data/app/views/alchemy/admin/pictures/show.html.erb +1 -1
  82. data/app/views/alchemy/admin/resources/_filter_bar.html.erb +31 -0
  83. data/app/views/alchemy/admin/resources/_form.html.erb +6 -0
  84. data/app/views/alchemy/admin/resources/_tag_list.html.erb +16 -0
  85. data/app/views/alchemy/admin/resources/index.html.erb +13 -1
  86. data/app/views/alchemy/essences/_essence_file_view.html.erb +2 -5
  87. data/app/views/alchemy/essences/_essence_picture_view.html.erb +5 -3
  88. data/app/views/alchemy/{messages → messages_mailer}/contact_form_mail.de.text.erb +0 -0
  89. data/app/views/alchemy/{messages → messages_mailer}/contact_form_mail.en.text.erb +0 -0
  90. data/app/views/alchemy/{messages → messages_mailer}/contact_form_mail.es.text.erb +0 -0
  91. data/app/views/alchemy/{messages → messages_mailer}/new.html.erb +0 -0
  92. data/app/views/layouts/alchemy/admin.html.erb +3 -8
  93. data/config/alchemy/config.yml +4 -3
  94. data/config/initializers/assets.rb +2 -2
  95. data/config/initializers/dragonfly.rb +3 -0
  96. data/config/initializers/mime_types.rb +1 -0
  97. data/config/locales/alchemy.de.yml +10 -2
  98. data/config/locales/alchemy.en.yml +6 -2
  99. data/config/locales/alchemy.es.yml +6 -3
  100. data/config/locales/alchemy.fr.yml +6 -2
  101. data/config/locales/alchemy.it.yml +937 -0
  102. data/config/locales/alchemy.nl.yml +6 -2
  103. data/config/locales/alchemy.ru.yml +3 -2
  104. data/config/locales/simple_form.it.yml +25 -0
  105. data/db/migrate/20160108174834_add_timebased_publishing_columns_to_pages.rb +32 -0
  106. data/db/migrate/20160422195310_add_image_file_format_to_alchemy_pictures.rb +21 -0
  107. data/db/migrate/20160617224938_change_alchemy_pages_locked_to_locked_at.rb +22 -0
  108. data/lib/alchemy/ability_helper.rb +23 -0
  109. data/lib/alchemy/configuration_methods.rb +2 -2
  110. data/lib/alchemy/controller_actions.rb +4 -33
  111. data/lib/alchemy/engine.rb +1 -0
  112. data/lib/alchemy/resource.rb +30 -13
  113. data/lib/alchemy/resources_helper.rb +17 -0
  114. data/lib/alchemy/test_support/factories/page_factory.rb +7 -2
  115. data/lib/alchemy/upgrader.rb +1 -0
  116. data/lib/alchemy/upgrader/tasks/install_asset_manifests.rb +15 -0
  117. data/lib/alchemy/upgrader/three_point_four.rb +16 -0
  118. data/lib/alchemy/version.rb +1 -1
  119. data/lib/rails/generators/alchemy/elements/elements_generator.rb +8 -7
  120. data/lib/rails/generators/alchemy/essence/essence_generator.rb +2 -6
  121. data/lib/rails/generators/alchemy/install/files/all.css +11 -0
  122. data/lib/rails/generators/alchemy/install/files/all.js +11 -0
  123. data/lib/rails/generators/alchemy/install/files/{alchemy.elements.css.scss → article.scss} +0 -0
  124. data/lib/rails/generators/alchemy/install/install_generator.rb +20 -19
  125. data/lib/rails/generators/alchemy/module/module_generator.rb +3 -5
  126. data/lib/rails/generators/alchemy/page_layouts/page_layouts_generator.rb +7 -6
  127. data/lib/rails/generators/alchemy/site_layouts/site_layouts_generator.rb +7 -6
  128. data/vendor/assets/javascripts/date-formatter.js +161 -0
  129. data/vendor/assets/javascripts/jquery_plugins/jquery.datetimepicker.full.min.js +2 -0
  130. data/vendor/assets/javascripts/tinymce/langs/it.js +219 -0
  131. metadata +48 -13
  132. data/app/assets/javascripts/alchemy/alchemy.custom.js +0 -1
  133. data/app/assets/stylesheets/alchemy/custom.scss +0 -1
@@ -68,7 +68,7 @@ module Alchemy
68
68
  @page = @element.page
69
69
  @root_page = @page.get_language_root
70
70
  if @message.valid?
71
- Messages.contact_form_mail(@message, mail_to, mail_from, subject).deliver
71
+ MessagesMailer.contact_form_mail(@message, mail_to, mail_from, subject).deliver
72
72
  redirect_to_success_page
73
73
  else
74
74
  render template: 'alchemy/pages/show'
@@ -26,6 +26,8 @@ module Alchemy
26
26
  if: :run_on_page_layout_callbacks?,
27
27
  only: [:index, :show]
28
28
 
29
+ before_action :set_expiration_headers, only: [:index, :show], if: -> { @page }
30
+
29
31
  rescue_from ActionController::UnknownFormat, with: :page_not_found!
30
32
 
31
33
  # == The index action gets invoked if one requests '/' or '/:locale'
@@ -35,7 +37,7 @@ module Alchemy
35
37
  # Loads the current language root page. The current language is either loaded via :locale
36
38
  # parameter or, if that's missing, the default language is used.
37
39
  #
38
- # If this page is not published then it loads the first published descendant it finds.
40
+ # If this page is not published then it redirects to the first published descendant it finds.
39
41
  #
40
42
  # If no public page can be found it renders a 404 error.
41
43
  #
@@ -45,8 +47,7 @@ module Alchemy
45
47
  raise "Remove deprecated `redirect_index` configuration!" if Alchemy.version == "4.0.0.rc1"
46
48
  redirect_permanently_to page_redirect_url
47
49
  else
48
- authorize! :index, @page
49
- render_page if render_fresh_page?
50
+ show
50
51
  end
51
52
  end
52
53
 
@@ -84,13 +85,13 @@ module Alchemy
84
85
  #
85
86
  # Loads the current public language root page.
86
87
  #
87
- # If the root page is not public it loads the first published child.
88
+ # If the root page is not public it redirects to the first published child.
88
89
  # This can be configured via +redirect_to_public_child+ [default: true]
89
90
  #
90
91
  # If no index page and no admin users are present we show the "Welcome to Alchemy" page.
91
92
  #
92
93
  def load_index_page
93
- @page ||= public_root_page || first_public_child
94
+ @page ||= Language.current_root_page
94
95
  render template: 'alchemy/welcome', layout: false if signup_required?
95
96
  end
96
97
 
@@ -110,26 +111,6 @@ module Alchemy
110
111
  )
111
112
  end
112
113
 
113
- # Returns the current language root page, if it's published.
114
- #
115
- # Otherwise it returns nil.
116
- #
117
- def public_root_page
118
- @root_page ||= Language.current_root_page
119
- @root_page if @root_page && @root_page.public?
120
- end
121
-
122
- # Returns the first public child of the current language root page.
123
- #
124
- # If +redirect_to_public_child+ is configured to +false+ it returns +nil+.
125
- #
126
- def first_public_child
127
- if Alchemy::Config.get(:redirect_to_public_child)
128
- return unless @root_page
129
- @root_page.descendants.published.first
130
- end
131
- end
132
-
133
114
  # Redirects to given url with 301 status
134
115
  def redirect_permanently_to(url)
135
116
  redirect_to url, status: :moved_permanently
@@ -170,6 +151,14 @@ module Alchemy
170
151
  end
171
152
  end
172
153
 
154
+ def set_expiration_headers
155
+ if @page.cache_page?
156
+ expires_in @page.expiration_time, public: !@page.restricted
157
+ else
158
+ expires_now
159
+ end
160
+ end
161
+
173
162
  def set_root_page
174
163
  @root_page ||= Language.current_root_page
175
164
  end
@@ -200,7 +189,8 @@ module Alchemy
200
189
  def render_fresh_page?
201
190
  !@page.cache_page? || stale?(etag: page_etag,
202
191
  last_modified: @page.published_at,
203
- public: !@page.restricted)
192
+ public: !@page.restricted,
193
+ template: 'pages/show')
204
194
  end
205
195
 
206
196
  def page_not_found!
@@ -1,6 +1,6 @@
1
1
  module Alchemy
2
2
  class PicturesController < Alchemy::BaseController
3
- ALLOWED_IMAGE_TYPES = %w(png jpeg gif)
3
+ ALLOWED_IMAGE_TYPES = %w(png jpeg gif svg)
4
4
 
5
5
  caches_page :show, :thumbnail, :zoom
6
6
 
@@ -25,7 +25,7 @@ module Alchemy
25
25
  @size = params[:size]
26
26
  end
27
27
 
28
- respond_to { |format| send_image(processed_image, format) }
28
+ respond_to { |format| send_image(processed_image, format, flatten: true) }
29
29
  end
30
30
 
31
31
  def zoom
@@ -45,20 +45,29 @@ module Alchemy
45
45
  false
46
46
  end
47
47
 
48
- def send_image(image, format)
48
+ def send_image(image, format, opts = {})
49
49
  request.session_options[:skip] = true
50
50
  ALLOWED_IMAGE_TYPES.each do |type|
51
+ # Flatten animated gifs, only if converting to a different format.
52
+ # Can be overwritten via +options[:flatten]+.
53
+ options = {
54
+ flatten: type != "gif" && image.ext == 'gif'
55
+ }.merge(opts)
56
+
51
57
  format.send(type) do
52
- options = []
58
+ encoding_options = []
53
59
  if type == 'jpeg'
54
60
  quality = params[:quality] || Config.get(:output_image_jpg_quality)
55
- options << "-quality #{quality}"
61
+ encoding_options << "-quality #{quality}"
62
+ end
63
+ if options[:flatten]
64
+ encoding_options << "-flatten"
56
65
  end
57
- # Flatten animated gifs, only if converting to a different format.
58
- if type != "gif" && image.ext == 'gif'
59
- options << "-flatten"
66
+ if @picture.has_convertible_format?
67
+ render text: image.encode(type, encoding_options.join(' ')).data
68
+ else
69
+ render text: image.data
60
70
  end
61
- render text: image.encode(type, options.join(' ')).data
62
71
  end
63
72
  end
64
73
  end
@@ -70,7 +79,7 @@ module Alchemy
70
79
  if @image.nil?
71
80
  raise MissingImageFileError, "Missing image file for #{@picture.inspect}"
72
81
  end
73
- if @size.present?
82
+ if resizable?
74
83
  if params[:crop_size].present? && params[:crop_from].present? || params[:crop].present?
75
84
  @picture.crop(@size, params[:crop_from], params[:crop_size], @upsample)
76
85
  else
@@ -80,5 +89,9 @@ module Alchemy
80
89
  @image
81
90
  end
82
91
  end
92
+
93
+ def resizable?
94
+ @size.present? && @picture.has_convertible_format?
95
+ end
83
96
  end
84
97
  end
@@ -36,10 +36,14 @@ module Alchemy
36
36
  end
37
37
 
38
38
  def public_child_redirect_url
39
- return unless redirect_to_public_child?
39
+ return if @page.public?
40
40
 
41
- @page = @page.self_and_descendants.published.not_restricted.first
42
- @page ? page_redirect_url : page_not_found!
41
+ if configuration(:redirect_to_public_child)
42
+ @page = @page.descendants.published.not_restricted.first
43
+ @page ? page_redirect_url : page_not_found!
44
+ else
45
+ page_not_found!
46
+ end
43
47
  end
44
48
 
45
49
  def controller_and_action_url
@@ -65,9 +69,5 @@ module Alchemy
65
69
  def locale_prefix_missing?
66
70
  multi_language? && params[:locale].blank? && !default_locale?
67
71
  end
68
-
69
- def redirect_to_public_child?
70
- configuration(:redirect_to_public_child) && !@page.public?
71
- end
72
72
  end
73
73
  end
@@ -332,38 +332,41 @@ module Alchemy
332
332
 
333
333
  # Renders a textfield ready to display a datepicker
334
334
  #
335
- # Uses a HTML5 <tt><input type="date"></tt> field.
335
+ # Uses a HTML5 +<input type="date">+ field.
336
+ # A Javascript observer converts this into a fancy Datepicker.
337
+ # If you pass +'datetime'+ as +:type+ the datepicker will also have a Time select.
336
338
  #
337
- # === Example
339
+ # The date value gets localized via +I18n.l+. The format on Time and Date is +datepicker+
340
+ # or +datetimepicker+, if you pass another +type+.
341
+ #
342
+ # === Date Example
338
343
  #
339
344
  # <%= alchemy_datepicker(@person, :birthday) %>
340
345
  #
346
+ # === Datetime Example
347
+ #
348
+ # <%= alchemy_datepicker(@page, :public_on, type: 'datetime') %>
349
+ #
341
350
  # @param [ActiveModel::Base] object
342
351
  # An instance of a model
343
352
  # @param [String or Symbol] method
344
353
  # The attribute method to be called for the date value
345
354
  #
346
- # @option html_options [String] :type ('date')
355
+ # @option html_options [String] :type (date)
347
356
  # The type of text field
348
- # @option html_options [String] :class ('thin_border date')
357
+ # @option html_options [String] :class (type)
349
358
  # CSS classes of the input field
350
- # @option html_options [String] :value (object.send(method.to_sym).nil? ? nil : l(object.send(method.to_sym), :format => :datepicker))
351
- # The value the input displays
359
+ # @option html_options [String] :value (value of method on object)
360
+ # The value the input displays. If you pass a String its parsed with +Time.parse+
352
361
  #
353
362
  def alchemy_datepicker(object, method, html_options = {})
354
- value = nil
355
- if object.send(method.to_sym).present?
356
- value = l(object.send(method.to_sym), format: :datepicker)
357
- elsif html_options[:value].present?
358
- date = html_options.delete(:value)
359
- date = Time.parse(date) if date.is_a?(String)
360
- value = l(date, format: :datepicker)
361
- end
362
- text_field object.class.name.underscore.to_sym, method.to_sym, html_options.merge({
363
- type: 'date',
364
- class: 'date',
365
- value: value
366
- })
363
+ type = html_options.delete(:type) || 'date'
364
+ date = html_options.delete(:value) || object.send(method.to_sym).presence
365
+ date = Time.zone.parse(date) if date.is_a?(String)
366
+ value = date ? l(date, format: "#{type}picker".to_sym) : nil
367
+
368
+ text_field object.class.name.demodulize.underscore.to_sym,
369
+ method.to_sym, {type: type, class: type, value: value}.merge(html_options)
367
370
  end
368
371
 
369
372
  # Merges the params-hash with the given hash
@@ -57,25 +57,40 @@ module Alchemy
57
57
  end
58
58
 
59
59
  def essence_picture_thumbnail(content, options)
60
- return if content.ingredient.blank?
61
- crop = !(content.essence.crop_size.blank? && content.essence.crop_from.blank?) ||
62
- (content.settings_value(:crop, options) == true || content.settings_value(:crop, options) == "true")
60
+ ingredient = content.ingredient
61
+ essence = content.essence
62
+ return if ingredient.blank?
63
+
64
+ crop = !(essence.crop_size.blank? && essence.crop_from.blank?) ||
65
+ (
66
+ content.settings_value(:crop, options) == true ||
67
+ content.settings_value(:crop, options) == "true"
68
+ )
69
+
70
+ size = if essence.render_size.blank?
71
+ content.settings_value(:size, options)
72
+ else
73
+ essence.render_size
74
+ end
75
+
63
76
  image_options = {
64
- size: content.essence.thumbnail_size(content.essence.render_size.blank? ? content.settings_value(:size, options) : content.essence.render_size, crop),
65
- crop_from: content.essence.crop_from.blank? ? nil : content.essence.crop_from,
66
- crop_size: content.essence.crop_size.blank? ? nil : content.essence.crop_size,
77
+ size: essence.thumbnail_size(size, crop),
78
+ crop_from: essence.crop_from.blank? ? nil : essence.crop_from,
79
+ crop_size: essence.crop_size.blank? ? nil : essence.crop_size,
67
80
  crop: crop ? 'crop' : nil,
68
81
  upsample: content.settings_value(:upsample, options)
69
82
  }
83
+
70
84
  image_tag(
71
85
  alchemy.thumbnail_path({
72
- id: content.ingredient.id,
73
- name: content.ingredient.urlname,
74
- sh: content.ingredient.security_token(image_options)
86
+ id: ingredient.id,
87
+ name: ingredient.urlname,
88
+ sh: ingredient.security_token(image_options),
89
+ format: ingredient.image_file_format
75
90
  }.merge(image_options)),
76
- alt: content.ingredient.name,
91
+ alt: ingredient.name,
77
92
  class: 'img_paddingtop',
78
- title: Alchemy.t(:image_name) + ": #{content.ingredient.name}"
93
+ title: Alchemy.t(:image_name) + ": #{ingredient.name}"
79
94
  )
80
95
  end
81
96
 
@@ -22,7 +22,8 @@ module Alchemy
22
22
  def combined_page_status(page)
23
23
  page.status.map do |state, _value|
24
24
  next if state == :locked
25
- val = content_tag(:span, '', class: page.send(state) ? "page_status #{state}" : "page_status not_#{state}")
25
+ css_class = page.send("#{state}?") ? "page_status #{state}" : "page_status not_#{state}"
26
+ val = content_tag(:span, '', class: css_class)
26
27
  val + page.status_title(state)
27
28
  end.delete_if(&:blank?).join("<br>").html_safe
28
29
  end
@@ -86,40 +86,5 @@ module Alchemy
86
86
  def render_essence_view(content, options = {}, html_options = {})
87
87
  render_essence(content, :view, {for_view: options}, html_options)
88
88
  end
89
-
90
- # Renders a essence picture
91
- #
92
- def render_essence_picture_view(content, options, html_options)
93
- options = {
94
- show_caption: true,
95
- disable_link: false
96
- }.update(content.settings).update(options)
97
- return if content.ingredient.blank?
98
- if content.essence.caption.present? && options[:show_caption]
99
- caption = content_tag(:figcaption, content.essence.caption, id: "#{dom_id(content.ingredient)}_caption", class: "image_caption")
100
- end
101
- img_tag = image_tag(
102
- content.essence.picture_url(options), {
103
- alt: (content.essence.alt_tag.blank? ? nil : content.essence.alt_tag),
104
- title: (content.essence.title.blank? ? nil : content.essence.title),
105
- class: (caption || content.essence.css_class.blank? ? nil : content.essence.css_class)
106
- }.merge(caption ? {} : html_options)
107
- )
108
- output = caption ? img_tag + caption : img_tag
109
- if content.essence.link.present? && !options[:disable_link]
110
- output = link_to(url_for(content.essence.link), {
111
- title: content.essence.link_title.blank? ? nil : content.essence.link_title,
112
- target: (content.essence.link_target == "blank" ? "_blank" : nil),
113
- 'data-link-target' => content.essence.link_target.blank? ? nil : content.essence.link_target
114
- }) do
115
- output
116
- end
117
- end
118
- if caption
119
- content_tag(:figure, output, {class: content.essence.css_class.blank? ? nil : content.essence.css_class}.merge(html_options))
120
- else
121
- output
122
- end
123
- end
124
89
  end
125
90
  end
@@ -43,7 +43,7 @@ module Alchemy
43
43
  url_params = {
44
44
  id: picture.id,
45
45
  name: picture.urlname,
46
- format: configuration(:image_output_format),
46
+ format: picture.default_render_format,
47
47
  sh: picture.security_token(optional_params)
48
48
  }
49
49
  url_params.update(optional_params.update(crop: optional_params[:crop] ? 'crop' : nil))
@@ -0,0 +1,18 @@
1
+ module Alchemy
2
+ begin
3
+ base_class = Object.const_get('::ApplicationMailer')
4
+ rescue NameError
5
+ base_class = ActionMailer::Base
6
+ end
7
+
8
+ # The +BaseMailer+ is the class all Alchemy mailers inherit from.
9
+ #
10
+ # Itself inherits from +ApplicationMailer+ when it is defined, or
11
+ # as a fallback from +ActionMailer::Base+.
12
+ #
13
+ # +ApplicationMailer+ is the Rails standard for registering helpers and
14
+ # setting the default layout. It is only generated though, when you
15
+ # +rails generate+ a mailer.
16
+ #
17
+ class BaseMailer < base_class; end
18
+ end
@@ -1,5 +1,5 @@
1
1
  module Alchemy
2
- class Messages < ActionMailer::Base
2
+ class MessagesMailer < BaseMailer
3
3
  def contact_form_mail(message, mail_to, mail_from, subject)
4
4
  @message = message
5
5
  mail(
@@ -39,6 +39,13 @@ module Alchemy
39
39
  def allowed_filetypes
40
40
  Config.get(:uploader).fetch('allowed_filetypes', {}).fetch('alchemy/attachments', [])
41
41
  end
42
+
43
+ def file_types_for_select
44
+ file_types = Alchemy::Attachment.pluck(:file_mime_type).uniq.map do |type|
45
+ [Alchemy.t(type, scope: 'mime_types'), type]
46
+ end
47
+ file_types.sort_by(&:first)
48
+ end
42
49
  end
43
50
 
44
51
  validates_presence_of :file
@@ -56,6 +63,8 @@ module Alchemy
56
63
 
57
64
  after_update :touch_contents
58
65
 
66
+ scope :with_file_type, ->(file_type) { where(file_mime_type: file_type) }
67
+
59
68
  # Instance methods
60
69
 
61
70
  def to_jq_upload
@@ -28,7 +28,7 @@ module Alchemy
28
28
  belongs_to :page
29
29
  validates_uniqueness_of :name, scope: 'page_id'
30
30
  validates_format_of :name, with: /\A[a-z0-9_-]+\z/
31
- has_many :elements, -> { where(parent_element_id: nil).order(:position) }, dependent: :destroy
31
+ has_many :elements, -> { order(:position) }, dependent: :destroy
32
32
 
33
33
  class << self
34
34
  def definitions
@@ -126,11 +126,13 @@ module Alchemy
126
126
  #
127
127
  def picture_params(options = {})
128
128
  return {} if picture.nil?
129
+
129
130
  params = {
130
131
  id: picture.id,
131
132
  name: picture.urlname,
132
- format: Config.get(:image_output_format)
133
+ format: picture.default_render_format
133
134
  }.merge(options)
135
+
134
136
  if options[:crop] && crop_from.present? && crop_size.present?
135
137
  params = {
136
138
  crop: true,
@@ -138,6 +140,7 @@ module Alchemy
138
140
  crop_size: crop_size
139
141
  }.merge(params)
140
142
  end
143
+
141
144
  params = clean_picture_params(params)
142
145
  params.merge(sh: picture.security_token(params))
143
146
  end