alchemy_cms 3.0.4 → 3.1.0.beta1

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 (210) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +4 -6
  3. data/Gemfile +4 -7
  4. data/README.md +207 -115
  5. data/alchemy_cms.gemspec +10 -9
  6. data/app/assets/javascripts/alchemy/alchemy.dragndrop.js.coffee +2 -2
  7. data/app/assets/javascripts/alchemy/alchemy.image_cropper.js.coffee +2 -2
  8. data/app/assets/javascripts/alchemy/alchemy.js +6 -7
  9. data/app/assets/javascripts/alchemy/alchemy.translations.js.coffee +1 -43
  10. data/app/assets/javascripts/alchemy/alchemy.uploader.js.coffee +1 -1
  11. data/app/assets/stylesheets/alchemy/_mixins.scss +2 -1
  12. data/app/assets/stylesheets/alchemy/buttons.scss +0 -5
  13. data/app/assets/stylesheets/alchemy/dialogs.scss +1 -0
  14. data/app/assets/stylesheets/alchemy/frame.scss +9 -12
  15. data/app/assets/stylesheets/tinymce/skins/alchemy/content.min.css.scss +11 -2
  16. data/app/assets/stylesheets/tinymce/skins/alchemy/skin.min.css.scss +9 -1
  17. data/app/controllers/alchemy/admin/attachments_controller.rb +2 -4
  18. data/app/controllers/alchemy/admin/essence_pictures_controller.rb +34 -38
  19. data/app/controllers/alchemy/api/base_controller.rb +19 -0
  20. data/app/controllers/alchemy/api/contents_controller.rb +35 -0
  21. data/app/controllers/alchemy/api/elements_controller.rb +29 -0
  22. data/app/controllers/alchemy/api/pages_controller.rb +32 -0
  23. data/app/controllers/alchemy/contents_controller.rb +1 -0
  24. data/app/controllers/alchemy/elements_controller.rb +5 -2
  25. data/app/controllers/alchemy/pages_controller.rb +4 -1
  26. data/app/controllers/alchemy/pictures_controller.rb +4 -36
  27. data/app/helpers/alchemy/admin/essences_helper.rb +5 -2
  28. data/app/helpers/alchemy/essences_helper.rb +14 -1
  29. data/app/models/alchemy/content.rb +32 -4
  30. data/app/models/alchemy/element.rb +2 -16
  31. data/app/models/alchemy/element/presenters.rb +2 -2
  32. data/app/models/alchemy/essence_file.rb +5 -0
  33. data/app/models/alchemy/essence_picture.rb +12 -8
  34. data/app/models/alchemy/picture.rb +1 -74
  35. data/app/models/alchemy/picture/transformations.rb +249 -0
  36. data/app/serializers/alchemy/content_serializer.rb +3 -10
  37. data/app/serializers/alchemy/element_serializer.rb +6 -3
  38. data/app/serializers/alchemy/legacy_element_serializer.rb +17 -0
  39. data/app/views/alchemy/admin/dashboard/_sites.html.erb +14 -4
  40. data/app/views/alchemy/admin/essence_pictures/crop.html.erb +8 -5
  41. data/app/views/alchemy/admin/pages/edit.html.erb +9 -9
  42. data/app/views/alchemy/admin/pictures/info.html.erb +2 -3
  43. data/app/views/alchemy/admin/tags/edit.html.erb +1 -1
  44. data/app/views/alchemy/essences/_essence_boolean_editor.html.erb +8 -7
  45. data/app/views/alchemy/essences/_essence_boolean_view.html.erb +3 -3
  46. data/app/views/alchemy/essences/_essence_date_editor.html.erb +8 -2
  47. data/app/views/alchemy/essences/_essence_date_view.html.erb +10 -8
  48. data/app/views/alchemy/essences/_essence_file_editor.html.erb +48 -53
  49. data/app/views/alchemy/essences/_essence_file_view.html.erb +5 -5
  50. data/app/views/alchemy/essences/_essence_html_editor.html.erb +5 -4
  51. data/app/views/alchemy/essences/_essence_link_editor.html.erb +17 -15
  52. data/app/views/alchemy/essences/_essence_link_view.html.erb +11 -7
  53. data/app/views/alchemy/essences/_essence_picture_editor.html.erb +4 -2
  54. data/app/views/alchemy/essences/_essence_picture_view.html.erb +4 -2
  55. data/app/views/alchemy/essences/_essence_richtext_view.html.erb +5 -4
  56. data/app/views/alchemy/essences/_essence_select_editor.html.erb +22 -32
  57. data/app/views/alchemy/essences/_essence_text_view.html.erb +7 -6
  58. data/app/views/alchemy/essences/shared/_essence_picture_tools.html.erb +1 -1
  59. data/app/views/layouts/alchemy/admin.html.erb +5 -3
  60. data/config/initializers/inflections.rb +3 -0
  61. data/config/initializers/simple_form.rb +1 -1
  62. data/config/locales/alchemy.en.yml +0 -1
  63. data/config/routes.rb +14 -0
  64. data/lib/alchemy/capistrano.rb +71 -0
  65. data/lib/alchemy/engine.rb +0 -3
  66. data/lib/alchemy/essence.rb +1 -1
  67. data/lib/alchemy/permissions.rb +19 -5
  68. data/lib/alchemy/picture_attributes.rb +1 -1
  69. data/lib/alchemy/test_support/auth_helpers.rb +1 -1
  70. data/lib/alchemy/test_support/essence_shared_examples.rb +37 -22
  71. data/lib/alchemy/test_support/integration_helpers.rb +1 -1
  72. data/lib/alchemy/tinymce.rb +21 -4
  73. data/lib/alchemy/upgrader/three_point_one.rb +43 -0
  74. data/lib/alchemy/upgrader/three_point_zero.rb +13 -0
  75. data/lib/alchemy/version.rb +2 -1
  76. data/lib/rails/generators/alchemy/module/module_generator.rb +30 -0
  77. data/lib/rails/generators/alchemy/module/templates/ability.rb.tt +11 -0
  78. data/lib/rails/generators/alchemy/module/templates/controller.rb.tt +2 -0
  79. data/lib/rails/generators/alchemy/module/templates/module_config.rb.tt +15 -0
  80. data/lib/rails/generators/alchemy/scaffold/scaffold_generator.rb +0 -1
  81. data/lib/rails/templates/alchemy.rb +2 -2
  82. data/lib/tasks/alchemy/db.rake +7 -1
  83. data/spec/controllers/admin/attachments_controller_spec.rb +38 -38
  84. data/spec/controllers/admin/base_controller_spec.rb +18 -18
  85. data/spec/controllers/admin/clipboard_controller_spec.rb +23 -18
  86. data/spec/controllers/admin/contents_controller_spec.rb +33 -27
  87. data/spec/controllers/admin/dashboard_controller_spec.rb +14 -14
  88. data/spec/controllers/admin/elements_controller_spec.rb +125 -105
  89. data/spec/controllers/admin/essence_files_controller_spec.rb +6 -7
  90. data/spec/controllers/admin/essence_pictures_controller_spec.rb +52 -42
  91. data/spec/controllers/admin/languages_controller_spec.rb +3 -3
  92. data/spec/controllers/admin/pages_controller_spec.rb +81 -71
  93. data/spec/controllers/admin/pictures_controller_spec.rb +69 -72
  94. data/spec/controllers/admin/resources_controller_spec.rb +5 -5
  95. data/spec/controllers/admin/trash_controller_spec.rb +15 -12
  96. data/spec/controllers/alchemy/admin/tags_controller_spec.rb +8 -8
  97. data/spec/controllers/alchemy/api/contents_controller_spec.rb +73 -0
  98. data/spec/controllers/alchemy/api/elements_controller_spec.rb +69 -0
  99. data/spec/controllers/alchemy/api/pages_controller_spec.rb +86 -0
  100. data/spec/controllers/attachments_controller_spec.rb +8 -8
  101. data/spec/controllers/contents_controller_spec.rb +22 -0
  102. data/spec/controllers/elements_controller_spec.rb +10 -4
  103. data/spec/controllers/messages_controller_spec.rb +35 -34
  104. data/spec/controllers/pages_controller_spec.rb +37 -28
  105. data/spec/controllers/pictures_controller_spec.rb +90 -23
  106. data/spec/dummy/app/models/dummy_user.rb +0 -4
  107. data/spec/dummy/app/views/alchemy/elements/_all_you_can_eat_editor.html.erb +11 -0
  108. data/spec/dummy/config/alchemy/elements.yml +22 -1
  109. data/spec/dummy/config/alchemy/page_layouts.yml +4 -0
  110. data/spec/dummy/config/application.rb +2 -1
  111. data/spec/dummy/config/environments/test.rb +3 -1
  112. data/spec/features/admin/dashboard_spec.rb +41 -6
  113. data/spec/features/admin/language_tree_feature_spec.rb +3 -3
  114. data/spec/features/admin/legacy_page_url_management_spec.rb +1 -1
  115. data/spec/features/admin/link_overlay_spec.rb +7 -7
  116. data/spec/features/admin/locale_select_feature_spec.rb +5 -2
  117. data/spec/features/admin/modules_integration_spec.rb +1 -1
  118. data/spec/features/admin/page_creation_feature_spec.rb +3 -2
  119. data/spec/features/admin/page_editing_feature_spec.rb +66 -79
  120. data/spec/features/admin/picture_library_integration_spec.rb +8 -8
  121. data/spec/features/admin/resources_integration_spec.rb +21 -21
  122. data/spec/features/admin/tinymce_feature_spec.rb +36 -0
  123. data/spec/features/navigation_spec.rb +1 -1
  124. data/spec/features/page_feature_spec.rb +34 -34
  125. data/spec/features/picture_security_spec.rb +4 -4
  126. data/spec/features/security_spec.rb +1 -1
  127. data/spec/features/translation_integration_spec.rb +7 -7
  128. data/spec/helpers/admin/base_helper_spec.rb +51 -49
  129. data/spec/helpers/admin/contents_helper_spec.rb +11 -11
  130. data/spec/helpers/admin/elements_helper_spec.rb +20 -17
  131. data/spec/helpers/admin/essences_helper_spec.rb +42 -11
  132. data/spec/helpers/admin/navigation_helper_spec.rb +64 -54
  133. data/spec/helpers/admin/pages_helper_spec.rb +10 -10
  134. data/spec/helpers/admin/tags_helper_spec.rb +16 -16
  135. data/spec/helpers/base_helper_spec.rb +11 -11
  136. data/spec/helpers/elements_block_helper_spec.rb +24 -24
  137. data/spec/helpers/elements_helper_spec.rb +46 -46
  138. data/spec/helpers/essences_helper_spec.rb +90 -17
  139. data/spec/helpers/pages_helper_spec.rb +53 -53
  140. data/spec/helpers/picture_url_helpers_spec.rb +6 -6
  141. data/spec/helpers/url_helper_spec.rb +32 -32
  142. data/spec/libraries/config_spec.rb +9 -9
  143. data/spec/libraries/controller_actions_spec.rb +14 -14
  144. data/spec/libraries/i18n_spec.rb +6 -6
  145. data/spec/libraries/kaminari/scoped_pagination_url_helper_spec.rb +4 -4
  146. data/spec/libraries/modules_spec.rb +4 -4
  147. data/spec/libraries/mount_point_spec.rb +13 -13
  148. data/spec/libraries/page_layout_spec.rb +24 -24
  149. data/spec/libraries/permissions_spec.rb +97 -80
  150. data/spec/libraries/resource_spec.rb +37 -37
  151. data/spec/libraries/resources_helper_spec.rb +19 -19
  152. data/spec/libraries/shell_spec.rb +17 -17
  153. data/spec/libraries/template_tracker_spec.rb +14 -14
  154. data/spec/libraries/tinymce_spec.rb +8 -8
  155. data/spec/libraries/userstamp_spec.rb +2 -2
  156. data/spec/mailers/messages_spec.rb +4 -4
  157. data/spec/models/attachment_spec.rb +86 -30
  158. data/spec/models/cell_spec.rb +10 -10
  159. data/spec/models/content_spec.rb +106 -46
  160. data/spec/models/element_spec.rb +94 -115
  161. data/spec/models/essence_date_spec.rb +1 -1
  162. data/spec/models/essence_file_spec.rb +4 -4
  163. data/spec/models/essence_picture_spec.rb +56 -25
  164. data/spec/models/essence_richtext_spec.rb +1 -1
  165. data/spec/models/essence_text_spec.rb +7 -7
  166. data/spec/models/language_spec.rb +12 -12
  167. data/spec/models/legacy_page_url_spec.rb +2 -2
  168. data/spec/models/message_spec.rb +12 -5
  169. data/spec/models/page_spec.rb +259 -235
  170. data/spec/models/picture_spec.rb +72 -166
  171. data/spec/models/site_spec.rb +41 -41
  172. data/spec/models/tag_spec.rb +7 -7
  173. data/spec/routing/api_routing_spec.rb +150 -0
  174. data/spec/routing/routing_spec.rb +28 -28
  175. data/spec/spec_helper.rb +6 -5
  176. data/spec/support/hint_examples.rb +5 -5
  177. data/spec/support/transformation_examples.rb +173 -0
  178. data/spec/tasks/helpers_spec.rb +29 -29
  179. data/spec/views/essences/essence_boolean_editor_spec.rb +32 -0
  180. data/spec/views/essences/essence_boolean_view_spec.rb +2 -2
  181. data/spec/views/essences/essence_date_view_spec.rb +1 -1
  182. data/spec/views/essences/essence_link_view_spec.rb +11 -0
  183. data/spec/views/essences/essence_picture_view_spec.rb +56 -11
  184. data/spec/views/essences/essence_richtext_view_spec.rb +12 -0
  185. data/spec/views/essences/essence_text_view_spec.rb +12 -0
  186. data/vendor/assets/javascripts/tinymce/langs/de.js +20 -2
  187. data/vendor/assets/javascripts/tinymce/langs/fr.js +14 -1
  188. data/vendor/assets/javascripts/tinymce/langs/nl.js +22 -4
  189. data/vendor/assets/javascripts/tinymce/plugins/anchor/plugin.min.js +1 -1
  190. data/vendor/assets/javascripts/tinymce/plugins/autoresize/plugin.min.js +1 -1
  191. data/vendor/assets/javascripts/tinymce/plugins/charmap/plugin.min.js +1 -1
  192. data/vendor/assets/javascripts/tinymce/plugins/code/plugin.min.js +1 -1
  193. data/vendor/assets/javascripts/tinymce/plugins/directionality/plugin.min.js +1 -1
  194. data/vendor/assets/javascripts/tinymce/plugins/fullscreen/plugin.min.js +1 -1
  195. data/vendor/assets/javascripts/tinymce/plugins/hr/plugin.min.js +1 -1
  196. data/vendor/assets/javascripts/tinymce/plugins/link/plugin.min.js +1 -1
  197. data/vendor/assets/javascripts/tinymce/plugins/paste/plugin.min.js +1 -1
  198. data/vendor/assets/javascripts/tinymce/plugins/tabfocus/plugin.min.js +1 -1
  199. data/vendor/assets/javascripts/tinymce/plugins/table/plugin.min.js +1 -1
  200. data/vendor/assets/javascripts/tinymce/themes/modern/theme.min.js +1 -1
  201. data/vendor/assets/javascripts/tinymce/tinymce.min.js +11 -10
  202. metadata +72 -42
  203. data/app/views/alchemy/messages/contact_form_mail.es.text.erb +0 -12
  204. data/config/locales/alchemy.es.yml +0 -958
  205. data/config/locales/alchemy.ru.yml +0 -837
  206. data/config/locales/simple_form.es.yml +0 -6
  207. data/config/locales/simple_form.ru.yml +0 -25
  208. data/lib/rails/generators/alchemy/scaffold/files/alchemy.es.yml +0 -31
  209. data/vendor/assets/javascripts/tinymce/langs/es.js +0 -197
  210. data/vendor/assets/javascripts/tinymce/langs/ru.js +0 -197
@@ -0,0 +1,19 @@
1
+ module Alchemy
2
+ class API::BaseController < Alchemy::BaseController
3
+ layout false
4
+ respond_to :json
5
+
6
+ rescue_from CanCan::AccessDenied, with: :render_not_authorized
7
+ rescue_from ActiveRecord::RecordNotFound, with: :render_not_found
8
+
9
+ private
10
+
11
+ def render_not_authorized
12
+ render json: {error: 'Not authorized'}, status: 403
13
+ end
14
+
15
+ def render_not_found
16
+ render json: {error: 'Record not found'}, status: 404
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,35 @@
1
+ module Alchemy
2
+ class API::ContentsController < API::BaseController
3
+
4
+ # Returns all contents as json object
5
+ #
6
+ # You can either load all or only these for :element_id param
7
+ #
8
+ def index
9
+ @contents = Content.accessible_by(current_ability, :index)
10
+ if params[:element_id]
11
+ @contents = @contents.where(element_id: params[:element_id])
12
+ end
13
+ respond_with @contents
14
+ end
15
+
16
+ # Returns a json object for content
17
+ #
18
+ # You can either load it from :id param
19
+ # or even more useful via passing the element id and the name of the content
20
+ #
21
+ # $ bin/rake routes
22
+ #
23
+ # for more infos on how the url looks like.
24
+ #
25
+ def show
26
+ if params[:id]
27
+ @content = Content.find(params[:id])
28
+ elsif params[:element_id] && params[:name]
29
+ @content = Content.find_by!(element_id: params[:element_id], name: params[:name])
30
+ end
31
+ authorize! :show, @content
32
+ respond_with @content
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,29 @@
1
+ module Alchemy
2
+ class API::ElementsController < API::BaseController
3
+
4
+ # Returns all elements as json object
5
+ #
6
+ # You can either load all or only these for :page_id param
7
+ #
8
+ # If you want to only load a specific type of element pass ?named=an_element_name
9
+ #
10
+ def index
11
+ @elements = Element.accessible_by(current_ability, :index)
12
+ if params[:page_id]
13
+ @elements = @elements.where(page_id: params[:page_id])
14
+ end
15
+ if params[:named]
16
+ @elements = @elements.named(params[:named])
17
+ end
18
+ respond_with @elements
19
+ end
20
+
21
+ # Returns a json object for element
22
+ #
23
+ def show
24
+ @element = Element.find(params[:id])
25
+ authorize! :show, @element
26
+ respond_with @element
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,32 @@
1
+ module Alchemy
2
+ class API::PagesController < API::BaseController
3
+ before_action :load_page, only: [:show]
4
+
5
+ # Returns all pages as json object
6
+ #
7
+ def index
8
+ @pages = Page.accessible_by(current_ability, :index)
9
+ if params[:page_layout]
10
+ @pages = @pages.where(page_layout: params[:page_layout])
11
+ end
12
+ respond_with @pages
13
+ end
14
+
15
+ # Returns a json object for page
16
+ #
17
+ # You can either load the page via id or its urlname
18
+ #
19
+ def show
20
+ authorize! :show, @page
21
+ respond_with @page
22
+ end
23
+
24
+ private
25
+
26
+ def load_page
27
+ @page = Page.find_by(id: params[:id]) ||
28
+ Page.find_by(urlname: params[:urlname]) ||
29
+ raise(ActiveRecord::RecordNotFound)
30
+ end
31
+ end
32
+ end
@@ -10,6 +10,7 @@ module Alchemy
10
10
  respond_to :json
11
11
 
12
12
  def show
13
+ ActiveSupport::Deprecation.warn('The Alchemy contents json API moved to `api` namespace. Please use `/api/contents` for json requests instead.')
13
14
  respond_with @content
14
15
  end
15
16
 
@@ -21,8 +21,11 @@ module Alchemy
21
21
 
22
22
  respond_to do |format|
23
23
  format.html
24
- format.js { @container_id = params[:container_id] }
25
- format.json { render json: @element }
24
+ format.js { @container_id = params[:container_id] }
25
+ format.json do
26
+ ActiveSupport::Deprecation.warn("The Alchemy elements json API moved to `api` namespace. Please use `/api/elements` for json requests instead.")
27
+ render json: @element, serializer: LegacyElementSerializer
28
+ end
26
29
  end
27
30
  end
28
31
 
@@ -26,7 +26,10 @@ module Alchemy
26
26
  render xml: {error: 'Not found'}, status: 404
27
27
  end
28
28
  end
29
- format.json { render json: @page }
29
+ format.json do
30
+ ActiveSupport::Deprecation.warn('The Alchemy pages json API moved to `api` namespace. Please use `/api/pages` for json requests instead.')
31
+ render json: @page
32
+ end
30
33
  end
31
34
  end
32
35
  end
@@ -62,52 +62,20 @@ module Alchemy
62
62
  # Return the processed image dependent of size and cropping parameters
63
63
  def processed_image
64
64
  @image = @picture.image_file
65
+ @upsample = params[:upsample] == 'true' ? true : false
65
66
  if @image.nil?
66
67
  raise MissingImageFileError, "Missing image file for #{@picture.inspect}"
67
68
  end
68
69
  if @size.present?
69
- if params[:crop_size].present? && params[:crop_from].present?
70
- @image = @image.thumb xy_crop_geometry_string(params)
71
- @image.thumb(resize_geometry_string)
72
- elsif params[:crop]
73
- @image.thumb(center_crop_geometry_string)
70
+ if params[:crop_size].present? && params[:crop_from].present? || params[:crop].present?
71
+ @picture.crop(@size, params[:crop_from], params[:crop_size], @upsample)
74
72
  else
75
- @image.thumb(resize_geometry_string)
73
+ @picture.resize(@size, @upsample)
76
74
  end
77
75
  else
78
76
  @image
79
77
  end
80
78
  end
81
79
 
82
- # Returns the Imagemagick geometry string for cropping the image.
83
- def xy_crop_geometry_string(params)
84
- crop_from_x, crop_from_y = params[:crop_from].split('x')
85
- "#{params[:crop_size]}+#{crop_from_x}+#{crop_from_y}"
86
- end
87
-
88
- # Returns the Imagemagick geometry string used to resize the image.
89
- #
90
- # Prevents upscaling unless :upsample param is true.
91
- def resize_geometry_string
92
- params[:upsample] == 'true' ? @size : "#{@size}>"
93
- end
94
-
95
- # Returns the Imagemagick geometry string used to crop the image.
96
- #
97
- # Prevents upscaling unless :upsample param is true
98
- def center_crop_geometry_string
99
- params[:upsample] == 'true' ? "#{@size}#" : "#{normalized_sizes(*@size.split('x'))}#"
100
- end
101
-
102
- # Ensure we're not trying to scale the image up. Used only for cropping.
103
- def normalized_sizes(width, height)
104
- if width.to_i > @image.width
105
- width = @image.width
106
- end
107
- if height.to_i > @image.height
108
- height = @image.height
109
- end
110
- "#{width}x#{height}"
111
- end
112
80
  end
113
81
  end
@@ -59,11 +59,14 @@ module Alchemy
59
59
 
60
60
  def essence_picture_thumbnail(content, options)
61
61
  return if content.ingredient.blank?
62
+ crop = !(content.essence.crop_size.blank? && content.essence.crop_from.blank?) ||
63
+ (content_settings_value(content, :crop, options) == true || content_settings_value(content, :crop, options) == "true")
62
64
  image_options = {
63
- size: content.ingredient.cropped_thumbnail_size(content.essence.render_size.blank? ? options[:image_size] : content.essence.render_size),
65
+ size: content.essence.thumbnail_size(content.essence.render_size.blank? ? content_settings_value(content, :size, options) : content.essence.render_size, crop),
64
66
  crop_from: content.essence.crop_from.blank? ? nil : content.essence.crop_from,
65
67
  crop_size: content.essence.crop_size.blank? ? nil : content.essence.crop_size,
66
- crop: content.essence.crop_size.blank? && content.essence.crop_from.blank? ? 'crop' : nil
68
+ crop: crop ? 'crop' : nil,
69
+ upsample: content_settings_value(content, :upsample, options)
67
70
  }
68
71
  image_tag(
69
72
  alchemy.thumbnail_path({
@@ -92,7 +92,10 @@ module Alchemy
92
92
  # Renders a essence picture
93
93
  #
94
94
  def render_essence_picture_view(content, options, html_options)
95
- options = {show_caption: true, disable_link: false}.update(options)
95
+ options = {
96
+ show_caption: true,
97
+ disable_link: false
98
+ }.update(content.settings).update(options)
96
99
  return if content.ingredient.blank?
97
100
  if content.essence.caption.present? && options[:show_caption]
98
101
  caption = content_tag(:figcaption, content.essence.caption, id: "#{dom_id(content.ingredient)}_caption", class: "image_caption")
@@ -121,5 +124,15 @@ module Alchemy
121
124
  end
122
125
  end
123
126
 
127
+ # Fetches value from settings of given content
128
+ #
129
+ # @param content [Alchemy::Content] - The content that settings should be taken
130
+ # @param key [Symbol] - The hash key you want to fetch the value from
131
+ # @param options [Hash] - An optional Hash that can override the settings.
132
+ # Normally passed as options hash into the content editor view
133
+ def content_settings_value(content, key, options = {})
134
+ content.settings.update(options || {}).symbolize_keys[key.to_sym]
135
+ end
136
+
124
137
  end
125
138
  end
@@ -51,6 +51,14 @@ module Alchemy
51
51
  scope :essence_selects, -> { where(essence_type: "Alchemy::EssenceSelect") }
52
52
  scope :essence_texts, -> { where(essence_type: "Alchemy::EssenceText") }
53
53
  scope :named, ->(name) { where(name: name) }
54
+ scope :available, -> { published.not_trashed }
55
+ scope :published, -> { joins(:element).merge(Element.published) }
56
+ scope :not_trashed, -> { joins(:element).merge(Element.not_trashed) }
57
+ scope :not_restricted, -> { joins(:element).merge(Element.not_restricted) }
58
+
59
+ delegate :restricted?, to: :element, allow_nil: true
60
+ delegate :trashed?, to: :element, allow_nil: true
61
+ delegate :public?, to: :element, allow_nil: true
54
62
 
55
63
  class << self
56
64
  # Returns the translated label for a content name.
@@ -99,9 +107,7 @@ module Alchemy
99
107
  # Settings from the elements.yml definition
100
108
  def settings
101
109
  return {} if description.blank?
102
- @settings ||= description['settings']
103
- return {} if @settings.blank?
104
- @settings.symbolize_keys
110
+ @settings ||= description.fetch('settings', {}).symbolize_keys
105
111
  end
106
112
 
107
113
  def siblings
@@ -115,6 +121,25 @@ module Alchemy
115
121
  essence.ingredient
116
122
  end
117
123
 
124
+ # Serialized object representation for json api
125
+ #
126
+ def serialize
127
+ {
128
+ name: name,
129
+ value: serialized_ingredient,
130
+ link: essence.try(:link)
131
+ }.delete_if { |_k, v| v.blank? }
132
+ end
133
+
134
+ # Ingredient value from essence for json api
135
+ #
136
+ # If the essence responds to +serialized_ingredient+ method it takes this
137
+ # otherwise it uses the ingredient column.
138
+ #
139
+ def serialized_ingredient
140
+ essence.try(:serialized_ingredient) || ingredient
141
+ end
142
+
118
143
  # Sets the ingredient from essence
119
144
  def ingredient=(value)
120
145
  raise EssenceMissingError if essence.nil?
@@ -184,7 +209,10 @@ module Alchemy
184
209
 
185
210
  # Returns true if this content should be taken for element preview.
186
211
  def preview_content?
187
- !!description['take_me_for_preview']
212
+ if description['take_me_for_preview']
213
+ ActiveSupport::Deprecation.warn("Content definition's `take_me_for_preview` key is deprecated. Please use `as_element_title` instead.")
214
+ end
215
+ !!description['take_me_for_preview'] || !!description['as_element_title']
188
216
  end
189
217
 
190
218
  # Proxy method that returns the preview text from essence.
@@ -52,7 +52,7 @@ module Alchemy
52
52
  scope :trashed, -> { where(position: nil).order('updated_at DESC') }
53
53
  scope :not_trashed, -> { where(Element.arel_table[:position].not_eq(nil)) }
54
54
  scope :published, -> { where(public: true) }
55
- scope :not_restricted, -> { joins(:page).where('alchemy_pages' => {restricted: false}) }
55
+ scope :not_restricted, -> { joins(:page).merge(Page.not_restricted) }
56
56
  scope :available, -> { published.not_trashed }
57
57
  scope :named, ->(names) { where(name: names) }
58
58
  scope :excluded, ->(names) { where(arel_table[:name].not_in(names)) }
@@ -361,7 +361,7 @@ module Alchemy
361
361
  "fields.#{content_name}.#{error}".to_sym,
362
362
  "errors.#{error}".to_sym
363
363
  ],
364
- field: Content.translated_label_for(content_name, name)
364
+ field: Content.translated_label_for(content_name)
365
365
  )
366
366
  end
367
367
  end
@@ -421,20 +421,6 @@ module Alchemy
421
421
  "alchemy/elements/#{name}_view"
422
422
  end
423
423
 
424
- # Returns the key that's taken for cache path.
425
- #
426
- # Uses the page's +published_at+ value that's updated when the user publishes the page.
427
- #
428
- # If the page is the current preview it uses the element's updated_at value as cache key.
429
- #
430
- def cache_key
431
- if Page.current_preview == self.page
432
- "alchemy/elements/#{id}-#{updated_at}"
433
- else
434
- "alchemy/elements/#{id}-#{page.published_at}"
435
- end
436
- end
437
-
438
424
  private
439
425
 
440
426
  # creates the contents for this element as described in the elements.yml
@@ -39,7 +39,7 @@ module Alchemy
39
39
  #
40
40
  # It's taken from the first Content found in the +elements.yml+ description file.
41
41
  #
42
- # You can flag a Content as +take_me_for_preview+ to take this as preview.
42
+ # You can flag a Content as +as_element_title+ to take this as preview.
43
43
  #
44
44
  # @param maxlength [Fixnum] (30)
45
45
  # Length of characters after the text will be cut off.
@@ -63,7 +63,7 @@ module Alchemy
63
63
  # type: EssenceText
64
64
  # - name: text
65
65
  # type EssenceRichtext
66
- # take_me_for_preview: true
66
+ # as_element_title: true
67
67
  #
68
68
  # With "I want to tell you a funky story" as stripped_body for the EssenceRichtext Content produces:
69
69
  #
@@ -27,6 +27,11 @@ module Alchemy
27
27
  attachment.name.to_s[0..max-1]
28
28
  end
29
29
 
30
+ # Returns a serialized ingredient value for json api
31
+ def serialized_ingredient
32
+ attachment_url
33
+ end
34
+
30
35
  private
31
36
 
32
37
  def routes
@@ -26,9 +26,12 @@ module Alchemy
26
26
  acts_as_essence ingredient_column: 'picture'
27
27
 
28
28
  belongs_to :picture
29
+ delegate :image_file_width, :image_file_height, :image_file, to: :picture
29
30
  before_save :fix_crop_values
30
31
  before_save :replace_newlines
31
32
 
33
+ include Alchemy::Picture::Transformations
34
+
32
35
  # The url to show the picture.
33
36
  #
34
37
  # Takes all values like +name+ and crop sizes (+crop_from+, +crop_size+ from the build in graphical image cropper)
@@ -70,14 +73,15 @@ module Alchemy
70
73
  #
71
74
  def cropping_mask
72
75
  return if crop_from.blank? || crop_size.blank?
73
- crop_from = read_attribute(:crop_from).split('x')
74
- crop_size = read_attribute(:crop_size).split('x')
75
- {
76
- x1: crop_from[0].to_i,
77
- y1: crop_from[1].to_i,
78
- x2: crop_from[0].to_i + crop_size[0].to_i,
79
- y2: crop_from[1].to_i + crop_size[1].to_i
80
- }
76
+ crop_from = point_from_string(read_attribute(:crop_from))
77
+ crop_size = sizes_from_string(read_attribute(:crop_size))
78
+
79
+ point_and_mask_to_points(crop_from, crop_size)
80
+ end
81
+
82
+ # Returns a serialized ingredient value for json api
83
+ def serialized_ingredient
84
+ picture_url(content.settings)
81
85
  end
82
86
 
83
87
  private
@@ -22,6 +22,7 @@ module Alchemy
22
22
  include Alchemy::NameConversions
23
23
  include Alchemy::Touching
24
24
  include Alchemy::Picture::Sweeping
25
+ include Alchemy::Picture::Transformations
25
26
 
26
27
  has_many :essence_pictures, class_name: 'Alchemy::EssencePicture', foreign_key: 'picture_id'
27
28
  has_many :contents, through: :essence_pictures
@@ -143,80 +144,6 @@ module Alchemy
143
144
  convert_to_humanized_name(image_file_name, suffix)
144
145
  end
145
146
 
146
- # Returns true if picture's width is greater than it's height
147
- #
148
- def landscape_format?
149
- image_file.landscape?
150
- end
151
- alias_method :landscape?, :landscape_format?
152
-
153
- # Returns true if picture's width is smaller than it's height
154
- #
155
- def portrait_format?
156
- image_file.portrait?
157
- end
158
- alias_method :portrait?, :portrait_format?
159
-
160
- # Returns true if picture's width and height is equal
161
- #
162
- def square_format?
163
- image_file.aspect_ratio == 1.0
164
- end
165
- alias_method :square?, :square_format?
166
-
167
- # Returns the default centered image mask for a given size.
168
- #
169
- def default_mask(size)
170
- raise "No size given" if size.blank?
171
- width = size.split('x')[0].to_i
172
- height = size.split('x')[1].to_i
173
- if (width > height)
174
- zoom_factor = image_file_width.to_f / width
175
- mask_height = (height * zoom_factor).round
176
- x1 = 0
177
- x2 = image_file_width
178
- y1 = (image_file_height - mask_height) / 2
179
- y2 = y1 + mask_height
180
- elsif (width == 0 && height == 0)
181
- x1 = 0
182
- x2 = image_file_width
183
- y1 = 0
184
- y2 = image_file_height
185
- else
186
- zoom_factor = image_file_height.to_f / height
187
- mask_width = (width * zoom_factor).round
188
- x1 = (image_file_width - mask_width) / 2
189
- x2 = x1 + mask_width
190
- y1 = 0
191
- y2 = image_file_height
192
- end
193
- {
194
- x1: x1,
195
- y1: y1,
196
- x2: x2,
197
- y2: y2
198
- }
199
- end
200
-
201
- # Returns a size value String for the thumbnail used in essence picture editors.
202
- #
203
- def cropped_thumbnail_size(size)
204
- return "111x93" if size == "111x93" || size.blank?
205
- x = size.split('x')[0].to_i
206
- y = size.split('x')[1].to_i
207
- return "111x93" if x.zero? || y.zero?
208
- if (x > y)
209
- zoom_factor = 111.0 / x
210
- new_x = 111
211
- new_y = y * zoom_factor
212
- else
213
- zoom_factor = 93.0 / y
214
- new_x = x * zoom_factor
215
- new_y = 93
216
- end
217
- "#{new_x.round}x#{new_y.round}"
218
- end
219
-
220
147
  # Checks if the picture is restricted.
221
148
  #
222
149
  # A picture is only restricted if it's assigned on restricted pages only.