alchemy_cms 3.4.2 → 3.5.0.rc1

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 (247) hide show
  1. checksums.yaml +4 -4
  2. data/.codeclimate.yml +9 -3
  3. data/.teatro.yml +1 -0
  4. data/.travis.yml +14 -17
  5. data/CHANGELOG.md +44 -6
  6. data/Gemfile +7 -4
  7. data/README.md +60 -10
  8. data/Rakefile +1 -1
  9. data/alchemy_cms.gemspec +5 -8
  10. data/app/assets/javascripts/alchemy/admin.js +2 -0
  11. data/app/assets/javascripts/alchemy/alchemy.dialog.js.coffee +1 -0
  12. data/app/assets/javascripts/alchemy/alchemy.gui.js.coffee +1 -0
  13. data/app/assets/javascripts/alchemy/alchemy.hotkeys.js.coffee +1 -1
  14. data/app/assets/javascripts/alchemy/alchemy.initializer.js.coffee +9 -7
  15. data/app/assets/javascripts/alchemy/alchemy.link_dialog.js.coffee +1 -0
  16. data/app/assets/javascripts/alchemy/alchemy.preview_window.js.coffee +11 -7
  17. data/app/assets/javascripts/alchemy/alchemy.sitemap.js.coffee +1 -1
  18. data/app/assets/javascripts/alchemy/alchemy.tinymce.js.coffee +8 -3
  19. data/app/assets/javascripts/alchemy/alchemy.tooltips.coffee +10 -0
  20. data/app/assets/javascripts/alchemy/alchemy.uploader.js.coffee +104 -73
  21. data/app/assets/stylesheets/alchemy/_defaults.scss +1 -4
  22. data/app/assets/stylesheets/alchemy/_extends.scss +13 -35
  23. data/app/assets/stylesheets/alchemy/_mixins.scss +82 -18
  24. data/app/assets/stylesheets/alchemy/_variables.scss +21 -8
  25. data/app/assets/stylesheets/alchemy/admin.scss +4 -0
  26. data/app/assets/stylesheets/alchemy/archive.scss +8 -12
  27. data/app/assets/stylesheets/alchemy/attachments.scss +39 -0
  28. data/app/assets/stylesheets/alchemy/base.scss +26 -15
  29. data/app/assets/stylesheets/alchemy/buttons.scss +59 -31
  30. data/app/assets/stylesheets/alchemy/dashboard.scss +3 -3
  31. data/app/assets/stylesheets/alchemy/dialogs.scss +10 -8
  32. data/app/assets/stylesheets/alchemy/elements.scss +65 -41
  33. data/app/assets/stylesheets/alchemy/errors.scss +7 -0
  34. data/app/assets/stylesheets/alchemy/flash.scss +1 -1
  35. data/app/assets/stylesheets/alchemy/form_fields.scss +0 -37
  36. data/app/assets/stylesheets/alchemy/forms.scss +18 -27
  37. data/app/assets/stylesheets/alchemy/frame.scss +104 -204
  38. data/app/assets/stylesheets/alchemy/hints.scss +62 -0
  39. data/app/assets/stylesheets/alchemy/icon-font.scss +2 -1
  40. data/app/assets/stylesheets/alchemy/icons.scss +9 -4
  41. data/app/assets/stylesheets/alchemy/image_library.scss +6 -6
  42. data/app/assets/stylesheets/alchemy/jquery-ui.scss +6 -4
  43. data/app/assets/stylesheets/alchemy/lists.scss +0 -1
  44. data/app/assets/stylesheets/alchemy/menubar.scss +3 -4
  45. data/app/assets/stylesheets/alchemy/modules.scss +0 -6
  46. data/app/assets/stylesheets/alchemy/navigation.scss +242 -0
  47. data/app/assets/stylesheets/alchemy/pagination.scss +3 -3
  48. data/app/assets/stylesheets/alchemy/print.scss +1 -0
  49. data/app/assets/stylesheets/alchemy/resource_info.scss +45 -0
  50. data/app/assets/stylesheets/alchemy/search.scss +72 -1
  51. data/app/assets/stylesheets/alchemy/selects.scss +38 -44
  52. data/app/assets/stylesheets/alchemy/sitemap.scss +89 -79
  53. data/app/assets/stylesheets/alchemy/tables.scss +6 -10
  54. data/app/assets/stylesheets/alchemy/toolbar.scss +7 -36
  55. data/app/assets/stylesheets/alchemy/upload.scss +12 -3
  56. data/app/assets/stylesheets/tinymce/skins/alchemy/content.min.css.scss +6 -3
  57. data/app/assets/stylesheets/tinymce/skins/alchemy/fonts/tinymce-small.svg +58 -170
  58. data/app/assets/stylesheets/tinymce/skins/alchemy/fonts/tinymce-small.ttf +0 -0
  59. data/app/assets/stylesheets/tinymce/skins/alchemy/fonts/tinymce-small.woff +0 -0
  60. data/app/assets/stylesheets/tinymce/skins/alchemy/fonts/tinymce.svg +124 -148
  61. data/app/assets/stylesheets/tinymce/skins/alchemy/fonts/tinymce.ttf +0 -0
  62. data/app/assets/stylesheets/tinymce/skins/alchemy/fonts/tinymce.woff +0 -0
  63. data/app/assets/stylesheets/tinymce/skins/alchemy/skin.min.css.scss +426 -144
  64. data/app/controllers/alchemy/admin/attachments_controller.rb +24 -16
  65. data/app/controllers/alchemy/admin/clipboard_controller.rb +1 -1
  66. data/app/controllers/alchemy/admin/essence_files_controller.rb +1 -1
  67. data/app/controllers/alchemy/admin/essence_pictures_controller.rb +9 -8
  68. data/app/controllers/alchemy/admin/layoutpages_controller.rb +1 -0
  69. data/app/controllers/alchemy/admin/pages_controller.rb +2 -2
  70. data/app/controllers/alchemy/admin/resources_controller.rb +2 -2
  71. data/app/controllers/alchemy/admin/tags_controller.rb +1 -1
  72. data/app/controllers/alchemy/api/pages_controller.rb +16 -0
  73. data/app/controllers/alchemy/messages_controller.rb +1 -1
  74. data/app/controllers/concerns/alchemy/admin/uploader_responses.rb +2 -2
  75. data/app/helpers/alchemy/admin/attachments_helper.rb +11 -0
  76. data/app/helpers/alchemy/admin/base_helper.rb +37 -4
  77. data/app/helpers/alchemy/admin/contents_helper.rb +11 -4
  78. data/app/helpers/alchemy/admin/elements_helper.rb +0 -19
  79. data/app/helpers/alchemy/admin/essences_helper.rb +7 -30
  80. data/app/helpers/alchemy/admin/navigation_helper.rb +13 -51
  81. data/app/helpers/alchemy/admin/pages_helper.rb +21 -16
  82. data/app/helpers/alchemy/admin/pictures_helper.rb +9 -0
  83. data/app/helpers/alchemy/deprecated_pages_helper.rb +54 -0
  84. data/app/helpers/alchemy/essences_helper.rb +1 -1
  85. data/app/helpers/alchemy/pages_helper.rb +8 -109
  86. data/app/helpers/alchemy/url_helper.rb +8 -13
  87. data/app/models/alchemy/attachment.rb +7 -4
  88. data/app/models/alchemy/cell.rb +2 -2
  89. data/app/models/alchemy/content.rb +2 -2
  90. data/app/models/alchemy/content/factory.rb +12 -9
  91. data/app/models/alchemy/element.rb +6 -3
  92. data/app/models/alchemy/essence_file.rb +1 -1
  93. data/app/models/alchemy/essence_picture.rb +37 -47
  94. data/app/models/alchemy/essence_picture_view.rb +8 -1
  95. data/app/models/alchemy/folded_page.rb +3 -2
  96. data/app/models/alchemy/legacy_page_url.rb +3 -3
  97. data/app/models/alchemy/page.rb +50 -5
  98. data/app/models/alchemy/page/fixed_attributes.rb +63 -0
  99. data/app/models/alchemy/page/page_elements.rb +10 -7
  100. data/app/models/alchemy/page/page_natures.rb +19 -0
  101. data/app/models/alchemy/picture.rb +1 -0
  102. data/app/models/alchemy/picture/transformations.rb +1 -1
  103. data/app/models/alchemy/picture/url.rb +82 -0
  104. data/app/serializers/alchemy/page_tree_serializer.rb +29 -8
  105. data/app/views/alchemy/_edit_mode.html.erb +2 -0
  106. data/app/views/alchemy/_menubar.html.erb +1 -1
  107. data/app/views/alchemy/_preview_mode_code.html.erb +6 -0
  108. data/app/views/alchemy/admin/attachments/_archive_overlay.html.erb +1 -1
  109. data/app/views/alchemy/admin/attachments/_attachment.html.erb +25 -5
  110. data/app/views/alchemy/admin/attachments/_replace_button.html.erb +26 -0
  111. data/app/views/alchemy/admin/attachments/index.html.erb +1 -1
  112. data/app/views/alchemy/admin/attachments/show.html.erb +52 -0
  113. data/app/views/alchemy/admin/elements/_element_header.html.erb +6 -3
  114. data/app/views/alchemy/admin/elements/create.js.erb +0 -2
  115. data/app/views/alchemy/admin/elements/trash.js.erb +0 -1
  116. data/app/views/alchemy/admin/elements/update.js.erb +0 -2
  117. data/app/views/alchemy/admin/essence_pictures/crop.html.erb +1 -4
  118. data/app/views/alchemy/admin/essence_pictures/edit.html.erb +1 -1
  119. data/app/views/alchemy/admin/languages/index.html.erb +1 -0
  120. data/app/views/alchemy/admin/layoutpages/_layoutpage.html.erb +26 -27
  121. data/app/views/alchemy/admin/layoutpages/edit.html.erb +1 -1
  122. data/app/views/alchemy/admin/pages/_form.html.erb +13 -40
  123. data/app/views/alchemy/admin/pages/_locked_page.html.erb +1 -1
  124. data/app/views/alchemy/admin/pages/_page.html.erb +119 -61
  125. data/app/views/alchemy/admin/pages/_page_for_links.html.erb +4 -2
  126. data/app/views/alchemy/admin/pages/_page_infos.html.erb +12 -12
  127. data/app/views/alchemy/admin/pages/_page_status.html.erb +1 -1
  128. data/app/views/alchemy/admin/pages/_publication_fields.html.erb +35 -0
  129. data/app/views/alchemy/admin/pages/edit.html.erb +13 -2
  130. data/app/views/alchemy/admin/pages/index.html.erb +3 -8
  131. data/app/views/alchemy/admin/pages/info.html.erb +15 -2
  132. data/app/views/alchemy/admin/pages/sort.js.erb +1 -1
  133. data/app/views/alchemy/admin/pages/update.js.erb +1 -14
  134. data/app/views/alchemy/admin/partials/_main_navigation_entry.html.erb +12 -8
  135. data/app/views/alchemy/admin/partials/_remote_search_form.html.erb +4 -4
  136. data/app/views/alchemy/admin/partials/_search_form.html.erb +1 -1
  137. data/app/views/alchemy/admin/partials/_sub_navigation.html.erb +9 -6
  138. data/app/views/alchemy/admin/pictures/_filter_and_size_bar.html.erb +1 -1
  139. data/app/views/alchemy/admin/pictures/_picture.html.erb +1 -6
  140. data/app/views/alchemy/admin/pictures/_picture_to_assign.html.erb +1 -6
  141. data/app/views/alchemy/admin/pictures/index.html.erb +1 -1
  142. data/app/views/alchemy/admin/pictures/show.html.erb +1 -6
  143. data/app/views/alchemy/admin/uploader/_button.html.erb +4 -4
  144. data/app/views/alchemy/base/500.html.erb +15 -1
  145. data/app/views/alchemy/essences/_essence_boolean_editor.html.erb +13 -15
  146. data/app/views/alchemy/essences/_essence_boolean_view.html.erb +1 -3
  147. data/app/views/alchemy/essences/_essence_date_editor.html.erb +0 -2
  148. data/app/views/alchemy/essences/_essence_date_view.html.erb +0 -2
  149. data/app/views/alchemy/essences/_essence_file_editor.html.erb +2 -7
  150. data/app/views/alchemy/essences/_essence_file_view.html.erb +1 -3
  151. data/app/views/alchemy/essences/_essence_html_editor.html.erb +0 -2
  152. data/app/views/alchemy/essences/_essence_html_view.html.erb +1 -3
  153. data/app/views/alchemy/essences/_essence_link_editor.html.erb +0 -2
  154. data/app/views/alchemy/essences/_essence_link_view.html.erb +0 -2
  155. data/app/views/alchemy/essences/_essence_picture_editor.html.erb +47 -49
  156. data/app/views/alchemy/essences/_essence_picture_view.html.erb +1 -3
  157. data/app/views/alchemy/essences/_essence_richtext_editor.html.erb +0 -2
  158. data/app/views/alchemy/essences/_essence_richtext_view.html.erb +1 -3
  159. data/app/views/alchemy/essences/_essence_select_editor.html.erb +27 -29
  160. data/app/views/alchemy/essences/_essence_select_view.html.erb +1 -3
  161. data/app/views/alchemy/essences/_essence_text_editor.html.erb +17 -19
  162. data/app/views/alchemy/essences/_essence_text_view.html.erb +0 -2
  163. data/app/views/alchemy/pages/_meta_data.html.erb +9 -0
  164. data/app/views/layouts/alchemy/admin.html.erb +9 -11
  165. data/bin/alchemy +1 -2
  166. data/config/alchemy/config.yml +1 -1
  167. data/config/alchemy/modules.yml +0 -16
  168. data/config/initializers/dragonfly.rb +0 -18
  169. data/config/initializers/mini_profiler.rb +6 -0
  170. data/config/locales/alchemy.de.yml +9 -1
  171. data/config/locales/alchemy.en.yml +7 -1
  172. data/config/locales/alchemy.es.yml +6 -0
  173. data/config/locales/alchemy.fr.yml +2 -0
  174. data/config/locales/alchemy.it.yml +3 -1
  175. data/config/locales/alchemy.nl.yml +2 -0
  176. data/config/locales/alchemy.ru.yml +2 -0
  177. data/config/routes.rb +3 -8
  178. data/db/migrate/20160912223112_add_index_to_alchemy_pages_rgt.rb +9 -0
  179. data/db/migrate/20160927205604_add_foreign_key_indices_and_null_constraints.rb +20 -0
  180. data/db/migrate/20160928080104_add_foreign_keys.rb +27 -0
  181. data/lib/alchemy/admin/locale.rb +4 -3
  182. data/lib/alchemy/engine.rb +2 -4
  183. data/lib/alchemy/errors.rb +9 -2
  184. data/lib/alchemy/forms/builder.rb +8 -0
  185. data/lib/alchemy/modules.rb +20 -19
  186. data/lib/alchemy/permissions.rb +15 -4
  187. data/lib/alchemy/resources_helper.rb +4 -2
  188. data/lib/alchemy/sass_support.rb +9 -0
  189. data/lib/alchemy/seeder.rb +89 -1
  190. data/lib/alchemy/test_support/essence_shared_examples.rb +2 -0
  191. data/lib/alchemy/test_support/factories/attachment_factory.rb +1 -1
  192. data/lib/alchemy/test_support/factories/content_factory.rb +1 -0
  193. data/lib/alchemy/test_support/factories/element_factory.rb +1 -0
  194. data/lib/alchemy/test_support/factories/picture_factory.rb +1 -1
  195. data/lib/alchemy/test_support/fixtures/image.png +0 -0
  196. data/lib/alchemy/tinymce.rb +2 -6
  197. data/lib/alchemy/upgrader.rb +4 -55
  198. data/lib/alchemy/upgrader/tasks/install_dragonfly_config.rb +14 -0
  199. data/lib/alchemy/upgrader/three_point_five.rb +32 -0
  200. data/lib/alchemy/upgrader/three_point_four.rb +2 -8
  201. data/lib/alchemy/upgrader/three_point_one.rb +30 -30
  202. data/lib/alchemy/upgrader/three_point_three.rb +31 -31
  203. data/lib/alchemy/upgrader/three_point_two.rb +25 -25
  204. data/lib/alchemy/upgrader/three_point_zero.rb +59 -59
  205. data/lib/alchemy/version.rb +1 -1
  206. data/lib/rails/generators/alchemy/elements/templates/view.html.erb +1 -1
  207. data/lib/rails/generators/alchemy/elements/templates/view.html.haml +1 -1
  208. data/lib/rails/generators/alchemy/elements/templates/view.html.slim +1 -1
  209. data/lib/rails/generators/alchemy/essence/templates/editor.html.erb +1 -3
  210. data/lib/rails/generators/alchemy/install/files/_article_view.html.erb +1 -1
  211. data/lib/rails/generators/alchemy/install/files/application.html.erb +3 -4
  212. data/lib/rails/generators/alchemy/install/install_generator.rb +4 -0
  213. data/lib/rails/generators/alchemy/install/templates/dragonfly.rb.tt +35 -0
  214. data/lib/rails/generators/alchemy/module/module_generator.rb +1 -1
  215. data/lib/tasks/alchemy/db.rake +6 -0
  216. data/lib/tasks/alchemy/tidy.rake +85 -0
  217. data/lib/tasks/alchemy/upgrade.rake +165 -16
  218. data/vendor/assets/javascripts/clipboard.min.js +7 -0
  219. data/vendor/assets/javascripts/fileupload/jquery.fileupload-process.js +4 -4
  220. data/vendor/assets/javascripts/fileupload/jquery.fileupload-validate.js +2 -2
  221. data/vendor/assets/javascripts/fileupload/jquery.fileupload.js +29 -14
  222. data/vendor/assets/javascripts/fileupload/jquery.iframe-transport.js +2 -2
  223. data/vendor/assets/javascripts/tinymce/langs/es.js +2 -2
  224. data/vendor/assets/javascripts/tinymce/langs/fr.js +1 -1
  225. data/vendor/assets/javascripts/tinymce/langs/it.js +1 -1
  226. data/vendor/assets/javascripts/tinymce/langs/nl.js +3 -3
  227. data/vendor/assets/javascripts/tinymce/tinymce.min.js +15 -12
  228. metadata +44 -88
  229. data/app/assets/stylesheets/tinymce/skins/alchemy/fonts/readme.md +0 -1
  230. data/app/assets/stylesheets/tinymce/skins/alchemy/fonts/tinymce-small.eot +0 -0
  231. data/app/assets/stylesheets/tinymce/skins/alchemy/fonts/tinymce.eot +0 -0
  232. data/app/assets/stylesheets/tinymce/skins/alchemy/img/wline.gif +0 -0
  233. data/app/assets/stylesheets/tinymce/skins/alchemy/skin.ie7.min.css +0 -1
  234. data/app/controllers/alchemy/pictures_controller.rb +0 -97
  235. data/app/views/alchemy/admin/elements/_refresh_editor.js.erb +0 -8
  236. data/vendor/assets/javascripts/tinymce/plugins/anchor/plugin.min.js +0 -1
  237. data/vendor/assets/javascripts/tinymce/plugins/autoresize/plugin.min.js +0 -1
  238. data/vendor/assets/javascripts/tinymce/plugins/charmap/plugin.min.js +0 -1
  239. data/vendor/assets/javascripts/tinymce/plugins/code/plugin.min.js +0 -1
  240. data/vendor/assets/javascripts/tinymce/plugins/directionality/plugin.min.js +0 -1
  241. data/vendor/assets/javascripts/tinymce/plugins/fullscreen/plugin.min.js +0 -1
  242. data/vendor/assets/javascripts/tinymce/plugins/hr/plugin.min.js +0 -1
  243. data/vendor/assets/javascripts/tinymce/plugins/link/plugin.min.js +0 -1
  244. data/vendor/assets/javascripts/tinymce/plugins/paste/plugin.min.js +0 -1
  245. data/vendor/assets/javascripts/tinymce/plugins/tabfocus/plugin.min.js +0 -1
  246. data/vendor/assets/javascripts/tinymce/plugins/table/plugin.min.js +0 -1
  247. data/vendor/assets/javascripts/tinymce/themes/modern/theme.min.js +0 -1
@@ -25,7 +25,7 @@ module Alchemy
25
25
  class EssencePicture < ActiveRecord::Base
26
26
  acts_as_essence ingredient_column: 'picture'
27
27
 
28
- belongs_to :picture
28
+ belongs_to :picture, required: false
29
29
  delegate :image_file_width, :image_file_height, :image_file, to: :picture
30
30
  before_save :fix_crop_values
31
31
  before_save :replace_newlines
@@ -56,7 +56,37 @@ module Alchemy
56
56
  #
57
57
  def picture_url(options = {})
58
58
  return if picture.nil?
59
- routes.show_picture_path(picture_params(options))
59
+
60
+ options = {
61
+ format: picture.default_render_format,
62
+ crop_from: crop_from,
63
+ crop_size: crop_size
64
+ }.merge(options)
65
+
66
+ picture.url(options)
67
+ end
68
+
69
+ # Renders a thumbnail representation of the assigned image
70
+ #
71
+ # It takes cropping values into account, so it always represents the current
72
+ # image displayed in the frontend.
73
+ #
74
+ def thumbnail_url(options = {})
75
+ return if picture.nil?
76
+
77
+ crop = crop_values_present? || content.settings_value(:crop, options)
78
+ size = render_size || content.settings_value(:size, options)
79
+
80
+ options = {
81
+ size: thumbnail_size(size, crop),
82
+ crop: !!crop,
83
+ crop_from: crop_from.presence,
84
+ crop_size: crop_size.presence,
85
+ flatten: true,
86
+ format: picture.image_file_format
87
+ }
88
+
89
+ picture.url(options)
60
90
  end
61
91
 
62
92
  # The name of the picture used as preview text in element editor views.
@@ -88,11 +118,15 @@ module Alchemy
88
118
  def allow_image_cropping?(options = {})
89
119
  content && content.settings_value(:crop, options) && picture &&
90
120
  picture.can_be_cropped_to(
91
- content.settings_value(:image_size, options),
121
+ content.settings_value(:size, options),
92
122
  content.settings_value(:upsample, options)
93
123
  )
94
124
  end
95
125
 
126
+ def crop_values_present?
127
+ crop_from.present? && crop_size.present?
128
+ end
129
+
96
130
  private
97
131
 
98
132
  def fix_crop_values
@@ -114,49 +148,5 @@ module Alchemy
114
148
  return nil if caption.nil?
115
149
  caption.gsub!(/(\r\n|\r|\n)/, "<br/>")
116
150
  end
117
-
118
- # Returns Alchemy's url helpers.
119
- def routes
120
- @routes ||= Engine.routes.url_helpers
121
- end
122
-
123
- # Params for picture_path and picture_url methods
124
- #
125
- # @see +picture_url+ for options
126
- #
127
- def picture_params(options = {})
128
- return {} if picture.nil?
129
-
130
- params = {
131
- id: picture.id,
132
- name: picture.urlname,
133
- format: picture.default_render_format
134
- }.merge(options)
135
-
136
- if options[:crop] && crop_from.present? && crop_size.present?
137
- params = {
138
- crop: true,
139
- crop_from: crop_from,
140
- crop_size: crop_size
141
- }.merge(params)
142
- end
143
-
144
- params = clean_picture_params(params)
145
- params.merge(sh: picture.security_token(params))
146
- end
147
-
148
- # Ensures correct and clean params for show picture path.
149
- #
150
- def clean_picture_params(params)
151
- if params[:crop] == true
152
- params[:crop] = 'crop'
153
- end
154
- if params[:image_size]
155
- params[:size] = params.delete(:image_size)
156
- end
157
- secure_attributes = PictureAttributes::SECURE_ATTRIBUTES.dup
158
- secure_attributes += %w(name format sh)
159
- params.delete_if { |k, v| !secure_attributes.include?(k.to_s) || v.blank? }
160
- end
161
151
  end
162
152
  end
@@ -15,6 +15,13 @@ module Alchemy
15
15
  def initialize(content, options = {}, html_options = {})
16
16
  @content = content
17
17
  @options = DEFAULT_OPTIONS.merge(content.settings).merge(options)
18
+ if @options[:image_size].present?
19
+ ActiveSupport::Deprecation.warn(
20
+ "Passing `image_size` to EssencePicture is deprecated. Please use `size` instead.",
21
+ caller.unshift
22
+ )
23
+ @options[:size] = @options.delete(:image_size)
24
+ end
18
25
  @html_options = html_options
19
26
  @essence = content.essence
20
27
  @picture = essence.picture
@@ -49,7 +56,7 @@ module Alchemy
49
56
 
50
57
  def img_tag
51
58
  @_img_tag ||= image_tag(
52
- essence.picture_url(options), {
59
+ essence.picture_url(options.except(*DEFAULT_OPTIONS.keys)), {
53
60
  alt: essence.alt_tag.presence,
54
61
  title: essence.title.presence,
55
62
  class: caption ? nil : essence.css_class.presence
@@ -10,7 +10,8 @@
10
10
 
11
11
  module Alchemy
12
12
  class FoldedPage < ActiveRecord::Base
13
- belongs_to :page
14
- belongs_to :user, class_name: Alchemy.user_class_name
13
+ belongs_to :page, required: true
14
+ belongs_to :user, required: true,
15
+ class_name: Alchemy.user_class_name
15
16
  end
16
17
  end
@@ -10,10 +10,10 @@
10
10
  #
11
11
 
12
12
  class Alchemy::LegacyPageUrl < ActiveRecord::Base
13
- belongs_to :page, class_name: 'Alchemy::Page'
13
+ belongs_to :page,
14
+ class_name: 'Alchemy::Page',
15
+ required: true
14
16
 
15
- validates :page_id,
16
- presence: true
17
17
  validates :urlname,
18
18
  presence: true,
19
19
  format: {with: /\A[:\.\w\-+_\/\?&%;=]*\z/}
@@ -87,7 +87,7 @@ module Alchemy
87
87
 
88
88
  stampable stamper_class_name: Alchemy.user_class_name
89
89
 
90
- belongs_to :language
90
+ belongs_to :language, required: false
91
91
 
92
92
  has_one :site, through: :language
93
93
  has_many :site_languages, through: :site, source: :languages
@@ -115,9 +115,12 @@ module Alchemy
115
115
  unless: :systempage?
116
116
 
117
117
  before_save :set_published_at,
118
- if: -> { public? && published_at.nil? },
118
+ if: -> { public_on.present? && published_at.nil? },
119
119
  unless: :systempage?
120
120
 
121
+ before_save :set_fixed_attributes,
122
+ if: -> { fixed_attributes.any? }
123
+
121
124
  before_create :set_language_from_parent_or_default,
122
125
  if: -> { language_id.blank? },
123
126
  unless: :systempage?
@@ -336,7 +339,7 @@ module Alchemy
336
339
  def fold!(user_id, status)
337
340
  folded_page = folded_pages.find_or_create_by(user_id: user_id)
338
341
  folded_page.folded = status
339
- folded_page.save
342
+ folded_page.save!
340
343
  end
341
344
 
342
345
  def set_restrictions_to_child_pages
@@ -382,8 +385,8 @@ module Alchemy
382
385
  current_time = Time.current
383
386
  update_columns(
384
387
  published_at: current_time,
385
- public_on: current_time,
386
- public_until: nil
388
+ public_on: already_public_for?(current_time) ? public_on : current_time,
389
+ public_until: still_public_for?(current_time) ? public_until : nil
387
390
  )
388
391
  end
389
392
 
@@ -406,8 +409,50 @@ module Alchemy
406
409
  update_columns(hash)
407
410
  end
408
411
 
412
+ # Holds an instance of +FixedAttributes+
413
+ def fixed_attributes
414
+ @_fixed_attributes ||= Alchemy::Page::FixedAttributes.new(self)
415
+ end
416
+
417
+ # True if given attribute name is defined as fixed
418
+ def attribute_fixed?(name)
419
+ fixed_attributes.fixed?(name)
420
+ end
421
+
422
+ # Checks the current page's list of editors, if defined.
423
+ #
424
+ # This allows us to pass in a user and see if any of their roles are enable
425
+ # them to make edits
426
+ #
427
+ def editable_by?(user)
428
+ return true unless has_limited_editors?
429
+ (editor_roles & user.alchemy_roles).any?
430
+ end
431
+
432
+ # Returns the value of +public_on+ attribute
433
+ #
434
+ # If it's a fixed attribute then the fixed value is returned instead
435
+ #
436
+ def public_on
437
+ attribute_fixed?(:public_on) ? fixed_attributes[:public_on] : self[:public_on]
438
+ end
439
+
440
+ # Returns the value of +public_until+ attribute
441
+ #
442
+ # If it's a fixed attribute then the fixed value is returned instead
443
+ #
444
+ def public_until
445
+ attribute_fixed?(:public_until) ? fixed_attributes[:public_until] : self[:public_until]
446
+ end
447
+
409
448
  private
410
449
 
450
+ def set_fixed_attributes
451
+ fixed_attributes.all.each do |attribute, value|
452
+ send("#{attribute}=", value)
453
+ end
454
+ end
455
+
411
456
  # Returns the next or previous page on the same level or nil.
412
457
  #
413
458
  # @param [String]
@@ -0,0 +1,63 @@
1
+ module Alchemy
2
+ # = Fixed page attributes
3
+ #
4
+ # Fixed page attributes are not allowed to be changed by the user.
5
+ #
6
+ # Define fixed page attributes on the page layout definition of a page.
7
+ #
8
+ # == Example
9
+ #
10
+ # # page_layout.yml
11
+ # - name: Index
12
+ # unique: true
13
+ # fixed_attributes:
14
+ # - public_on: nil
15
+ # - public_until: nil
16
+ # - visible: false
17
+ #
18
+ class Page::FixedAttributes
19
+ attr_reader :page
20
+
21
+ def initialize(page)
22
+ @page = page
23
+ end
24
+
25
+ # All fixed attributes defined on page
26
+ #
27
+ # Aliased as +#all+
28
+ #
29
+ # @return Hash
30
+ #
31
+ def attributes
32
+ @_attributes ||= page.definition.fetch('fixed_attributes', {}).symbolize_keys
33
+ end
34
+ alias_method :all, :attributes
35
+
36
+ # True if fixed attributes are defined on page
37
+ #
38
+ # Aliased as +#present?+
39
+ #
40
+ # @return Boolean
41
+ #
42
+ def any?
43
+ attributes.present?
44
+ end
45
+ alias_method :present?, :any?
46
+
47
+ # True if given attribute name is defined on page
48
+ #
49
+ # @return Boolean
50
+ #
51
+ def fixed?(name)
52
+ return false if name.nil?
53
+ attributes.key?(name.to_sym)
54
+ end
55
+
56
+ # Returns the attribute by key
57
+ #
58
+ def [](name)
59
+ return nil if name.nil?
60
+ attributes[name.to_sym]
61
+ end
62
+ end
63
+ end
@@ -24,13 +24,6 @@ module Alchemy
24
24
  after_create :autogenerate_elements, unless: -> { systempage? || do_not_autogenerate }
25
25
  after_update :trash_not_allowed_elements!, if: :page_layout_changed?
26
26
  after_update :autogenerate_elements, if: :page_layout_changed?
27
-
28
- after_destroy do
29
- elements.each do |element|
30
- next if element.trashed?
31
- element.destroy
32
- end
33
- end
34
27
  end
35
28
 
36
29
  module ClassMethods
@@ -150,6 +143,16 @@ module Alchemy
150
143
  @_element_definitions ||= element_definitions_by_name(element_definition_names)
151
144
  end
152
145
 
146
+ # All element definitions defined for page's page layout including nestable element definitions
147
+ #
148
+ def descendent_element_definitions
149
+ definitions = element_definitions_by_name(element_definition_names)
150
+ definitions.select { |d| d.key?('nestable_elements') }.each do |d|
151
+ definitions += element_definitions_by_name(d['nestable_elements'])
152
+ end
153
+ definitions.uniq { |d| d['name'] }
154
+ end
155
+
153
156
  # All names of elements that are defined in the corresponding
154
157
  # page and cell definition.
155
158
  #
@@ -33,6 +33,25 @@ module Alchemy
33
33
  definition["feed"]
34
34
  end
35
35
 
36
+ # Returns an Array of Alchemy roles which are able to edit this template
37
+ #
38
+ # # config/alchemy/page_layouts.yml
39
+ # - name: contact
40
+ # editable_by:
41
+ # - freelancer
42
+ # - admin
43
+ #
44
+ # @returns Array
45
+ #
46
+ def has_limited_editors?
47
+ definition["editable_by"].present?
48
+ end
49
+
50
+ def editor_roles
51
+ return unless has_limited_editors?
52
+ definition["editable_by"]
53
+ end
54
+
36
55
  # Returns true or false if the pages definition for config/alchemy/page_layouts.yml contains redirects_to_external: true
37
56
  def redirects_to_external?
38
57
  !!definition["redirects_to_external"]
@@ -26,6 +26,7 @@ module Alchemy
26
26
  include Alchemy::Touching
27
27
  include Alchemy::Picture::Sweeping
28
28
  include Alchemy::Picture::Transformations
29
+ include Alchemy::Picture::Url
29
30
 
30
31
  has_many :essence_pictures, class_name: 'Alchemy::EssencePicture', foreign_key: 'picture_id'
31
32
  has_many :contents, through: :essence_pictures
@@ -120,7 +120,7 @@ module Alchemy
120
120
  # Returns true if the class we're included in has a meaningful render_size attribute
121
121
  #
122
122
  def render_size?
123
- respond_to?(:render_size) && !render_size.nil? && !render_size.empty?
123
+ respond_to?(:render_size) && render_size.present?
124
124
  end
125
125
 
126
126
  # Returns true if the class we're included in has a meaningful crop_size attribute
@@ -0,0 +1,82 @@
1
+ module Alchemy
2
+ module Picture::Url
3
+ TRANSFORMATION_OPTIONS = [
4
+ :crop,
5
+ :crop_from,
6
+ :crop_size,
7
+ :flatten,
8
+ :format,
9
+ :quality,
10
+ :size,
11
+ :upsample
12
+ ]
13
+
14
+ # Returns a path to picture for use inside a image_tag helper.
15
+ #
16
+ # Any additional options are passed to the url_helper, so you can add arguments to your url.
17
+ #
18
+ # Example:
19
+ #
20
+ # <%= image_tag picture.url(size: '320x200', format: 'png') %>
21
+ #
22
+ def url(options = {})
23
+ image = image_file
24
+
25
+ raise MissingImageFileError, "Missing image file for #{inspect}" if image.nil?
26
+
27
+ image = processed_image(image, options)
28
+ image = encoded_image(image, options)
29
+
30
+ image.url(options.except(*TRANSFORMATION_OPTIONS).merge(name: name))
31
+ end
32
+
33
+ private
34
+
35
+ # Returns the processed image dependent of size and cropping parameters
36
+ def processed_image(image, options = {})
37
+ size = options[:size]
38
+ upsample = !!options[:upsample]
39
+
40
+ return image unless size.present? && has_convertible_format?
41
+
42
+ if options[:crop_size].present? && options[:crop_from].present? || options[:crop].present?
43
+ crop(size, options[:crop_from], options[:crop_size], upsample)
44
+ else
45
+ resize(size, upsample)
46
+ end
47
+ end
48
+
49
+ # Returns the encoded image
50
+ #
51
+ # Flatten animated gifs, only if converting to a different format.
52
+ # Can be overwritten via +options[:flatten]+.
53
+ #
54
+ def encoded_image(image, options = {})
55
+ target_format = options[:format] || default_render_format
56
+ raise WrongImageFormatError if !target_format.in?(Alchemy::Picture.allowed_filetypes)
57
+
58
+ options = {
59
+ flatten: target_format != 'gif' && image_file_format == 'gif'
60
+ }.merge(options)
61
+
62
+ encoding_options = []
63
+
64
+ if target_format =~ /jpe?g/
65
+ quality = options[:quality] || Config.get(:output_image_jpg_quality)
66
+ encoding_options << "-quality #{quality}"
67
+ end
68
+
69
+ if options[:flatten]
70
+ encoding_options << '-flatten'
71
+ end
72
+
73
+ convertion_needed = target_format != image_file_format || encoding_options.present?
74
+
75
+ if has_convertible_format? && convertion_needed
76
+ image = image.encode(target_format, encoding_options.join(' '))
77
+ end
78
+
79
+ image
80
+ end
81
+ end
82
+ end