alchemy_cms 3.4.2 → 3.5.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
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