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
@@ -33,25 +33,25 @@ module Alchemy
33
33
  end
34
34
 
35
35
  def create
36
- @attachment = Attachment.new(attachment_attributes)
37
- if @attachment.save
38
- render succesful_uploader_response(file: @attachment)
39
- else
40
- render failed_uploader_response(file: @attachment)
41
- end
36
+ @attachment = Attachment.create(attachment_attributes)
37
+ handle_uploader_response(status: :created)
42
38
  end
43
39
 
44
40
  def update
45
- @attachment.update_attributes(attachment_attributes)
46
- render_errors_or_redirect(
47
- @attachment,
48
- admin_attachments_path(
49
- per_page: params[:per_page],
50
- page: params[:page],
51
- q: params[:q]
52
- ),
53
- Alchemy.t("File successfully updated")
54
- )
41
+ @attachment.update(attachment_attributes)
42
+ if attachment_attributes['file'].present?
43
+ handle_uploader_response(status: :accepted)
44
+ else
45
+ render_errors_or_redirect(
46
+ @attachment,
47
+ admin_attachments_path(
48
+ per_page: params[:per_page],
49
+ page: params[:page],
50
+ q: params[:q]
51
+ ),
52
+ Alchemy.t("File successfully updated")
53
+ )
54
+ end
55
55
  end
56
56
 
57
57
  def destroy
@@ -75,6 +75,14 @@ module Alchemy
75
75
 
76
76
  private
77
77
 
78
+ def handle_uploader_response(status:)
79
+ if @attachment.valid?
80
+ render succesful_uploader_response(file: @attachment, status: status)
81
+ else
82
+ render failed_uploader_response(file: @attachment)
83
+ end
84
+ end
85
+
78
86
  def in_overlay?
79
87
  params[:content_id].present?
80
88
  end
@@ -2,7 +2,7 @@ module Alchemy
2
2
  module Admin
3
3
  class ClipboardController < Alchemy::Admin::BaseController
4
4
  authorize_resource class: :alchemy_admin_clipboard
5
- before_filter :set_clipboard
5
+ before_action :set_clipboard
6
6
 
7
7
  def index
8
8
  @clipboard_items = model_class.all_from_clipboard(@clipboard)
@@ -3,7 +3,7 @@ module Alchemy
3
3
  class EssenceFilesController < Alchemy::Admin::BaseController
4
4
  authorize_resource class: Alchemy::EssenceFile
5
5
 
6
- before_filter :load_essence_file, only: [:edit, :update]
6
+ before_action :load_essence_file, only: [:edit, :update]
7
7
 
8
8
  helper "Alchemy::Admin::Contents"
9
9
 
@@ -3,9 +3,9 @@ module Alchemy
3
3
  class EssencePicturesController < Alchemy::Admin::BaseController
4
4
  authorize_resource class: Alchemy::EssencePicture
5
5
 
6
- before_filter :load_essence_picture, only: [:edit, :crop, :update]
7
- before_filter :load_content, only: [:edit, :update, :assign]
8
- before_filter :load_options
6
+ before_action :load_essence_picture, only: [:edit, :crop, :update]
7
+ before_action :load_content, only: [:edit, :update, :assign]
8
+ before_action :load_options
9
9
 
10
10
  helper 'alchemy/admin/contents'
11
11
  helper 'alchemy/admin/essences'
@@ -74,14 +74,15 @@ module Alchemy
74
74
  @content = Content.find(params[:content_id])
75
75
  end
76
76
 
77
- # Gets the minimum size of the image to be rendered. the database render_size
78
- # has preference over the image_size parameter.
77
+ # Gets the minimum size of the image to be rendered.
78
+ #
79
+ # The +render_size+ attribute has preference over the +size+ parameter.
79
80
  #
80
81
  def sizes_from_essence_or_params
81
- if @essence_picture.render_size? && !@essence_picture.render_size.blank?
82
+ if @essence_picture.render_size?
82
83
  @essence_picture.sizes_from_string(@essence_picture.render_size)
83
- elsif @options[:image_size]
84
- @essence_picture.sizes_from_string(@options[:image_size])
84
+ elsif @options[:size]
85
+ @essence_picture.sizes_from_string(@options[:size])
85
86
  else
86
87
  { width: 0, height: 0 }
87
88
  end
@@ -2,6 +2,7 @@ module Alchemy
2
2
  module Admin
3
3
  class LayoutpagesController < Alchemy::Admin::BaseController
4
4
  authorize_resource class: :alchemy_admin_layoutpages
5
+ helper Alchemy::Admin::PagesHelper
5
6
 
6
7
  def index
7
8
  @layout_root = Page.find_or_create_layout_root_for(Language.current.id)
@@ -81,7 +81,7 @@ module Alchemy
81
81
  def edit
82
82
  # fetching page via before filter
83
83
  if page_is_locked?
84
- flash[:notice] = Alchemy.t('This page is locked', name: @page.locker_name)
84
+ flash[:warning] = Alchemy.t('This page is locked', name: @page.locker_name)
85
85
  redirect_to admin_pages_path
86
86
  elsif page_needs_lock?
87
87
  @page.lock_to!(current_alchemy_user)
@@ -348,7 +348,7 @@ module Alchemy
348
348
  end
349
349
 
350
350
  def redirect_path_after_create_page
351
- if @page.redirects_to_external?
351
+ if @page.redirects_to_external? || !@page.editable_by?(current_alchemy_user)
352
352
  admin_pages_path
353
353
  else
354
354
  params[:redirect_to] || edit_admin_page_path(@page)
@@ -10,10 +10,10 @@ module Alchemy
10
10
  helper Alchemy::ResourcesHelper, TagsHelper
11
11
  helper_method :resource_handler
12
12
 
13
- before_filter :load_resource,
13
+ before_action :load_resource,
14
14
  only: [:show, :edit, :update, :destroy]
15
15
 
16
- before_filter do
16
+ before_action do
17
17
  authorize!(action_name.to_sym, resource_instance_variable || resource_handler.model)
18
18
  end
19
19
 
@@ -1,7 +1,7 @@
1
1
  module Alchemy
2
2
  module Admin
3
3
  class TagsController < ResourcesController
4
- before_filter :load_tag, only: [:edit, :update, :destroy]
4
+ before_action :load_tag, only: [:edit, :update, :destroy]
5
5
 
6
6
  def index
7
7
  @query = ActsAsTaggableOn::Tag.ransack(params[:q])
@@ -12,6 +12,22 @@ module Alchemy
12
12
  respond_with @pages
13
13
  end
14
14
 
15
+ # Returns all pages as nested json object for tree views
16
+ #
17
+ # Pass a page_id param to only load tree for this page
18
+ #
19
+ # Pass elements=true param to include elements for pages
20
+ #
21
+ def nested
22
+ @page = Page.find_by(id: params[:page_id]) || Language.current_root_page
23
+
24
+ render json: PageTreeSerializer.new(@page,
25
+ ability: current_ability,
26
+ user: current_alchemy_user,
27
+ elements: params[:elements],
28
+ full: true)
29
+ end
30
+
15
31
  # Returns a json object for page
16
32
  #
17
33
  # You can either load the page via id or its urlname
@@ -42,7 +42,7 @@ module Alchemy
42
42
  # Please have a look at the +alchemy/config/config.yml+ file for further Message settings.
43
43
  #
44
44
  class MessagesController < Alchemy::BaseController
45
- before_filter :get_page, except: :create
45
+ before_action :get_page, except: :create
46
46
 
47
47
  helper 'alchemy/pages'
48
48
 
@@ -3,7 +3,7 @@ module Alchemy
3
3
  module UploaderResponses
4
4
  extend ActiveSupport::Concern
5
5
 
6
- def succesful_uploader_response(file:)
6
+ def succesful_uploader_response(file:, status: :created)
7
7
  message = Alchemy.t(:upload_success,
8
8
  scope: [:uploader, file.class.model_name.i18n_key],
9
9
  name: file.name
@@ -11,7 +11,7 @@ module Alchemy
11
11
 
12
12
  {
13
13
  json: uploader_response(file: file, message: message),
14
- status: :created
14
+ status: status
15
15
  }
16
16
  end
17
17
 
@@ -6,6 +6,17 @@ module Alchemy
6
6
  def mime_to_human(mime)
7
7
  Alchemy.t(mime, scope: 'mime_types', default: Alchemy.t(:document))
8
8
  end
9
+
10
+ def attachment_preview_size(attachment)
11
+ case attachment.icon_css_class
12
+ when 'image' then '600x475'
13
+ when 'audio' then '600x190'
14
+ when 'video' then '600x485'
15
+ when 'pdf' then '600x500'
16
+ else
17
+ '600x145'
18
+ end
19
+ end
9
20
  end
10
21
  end
11
22
  end
@@ -58,7 +58,7 @@ module Alchemy
58
58
 
59
59
  # Used for translations selector in Alchemy cockpit user settings.
60
60
  def translations_for_select
61
- Alchemy::I18n.available_locales.map do |locale|
61
+ Alchemy::I18n.available_locales.sort.map do |locale|
62
62
  [Alchemy.t(locale, scope: :translations), locale]
63
63
  end
64
64
  end
@@ -397,16 +397,24 @@ module Alchemy
397
397
  current_params.merge(p).delete_if { |_k, v| v.blank? }
398
398
  end
399
399
 
400
+ # Render a hint icon with tooltip for given object.
401
+ # The model class needs to include the hints module
400
402
  def render_hint_for(element)
401
403
  return unless element.has_hint?
402
- link_to '#', class: 'hint' do
403
- render_icon(:hint) + content_tag(:span, element.hint.html_safe, class: 'bubble')
404
+ content_tag :span, class: 'hint-with-icon' do
405
+ render_icon(:questionmark) +
406
+ content_tag(:span, element.hint.html_safe, class: 'hint-bubble')
404
407
  end
405
408
  end
406
409
 
407
410
  # Appends the current controller and action to body as css class.
408
411
  def alchemy_body_class
409
- "#{controller_name} #{action_name}"
412
+ [
413
+ controller_name,
414
+ action_name,
415
+ content_for(:main_menu_style),
416
+ content_for(:alchemy_body_class)
417
+ ].compact
410
418
  end
411
419
 
412
420
  # (internal) Returns options for the clipboard select tag
@@ -426,6 +434,31 @@ module Alchemy
426
434
  Alchemy::Config.get(:format_matchers)['link_url'] || /^(mailto:|\/|[a-z]+:\/\/)/
427
435
  end
428
436
 
437
+ # Renders a hint with tooltip
438
+ #
439
+ # == Example
440
+ #
441
+ # <%= hint_with_tooltip('Page layout is missing', class: 'warning icon') %>
442
+ #
443
+ # @param text [String] - The text displayed in the tooltip
444
+ # @param html_options [Hash] - Options passed to the wrapper `content_tag`
445
+ #
446
+ def hint_with_tooltip(text, html_options = {})
447
+ css_class = "#{html_options[:class]} with-hint"
448
+ content_tag :span, html_options.merge(class: css_class) do
449
+ content_tag(:span, text, class: 'hint-bubble')
450
+ end
451
+ end
452
+
453
+ # Renders a warning icon with a hint
454
+ # that explains the user that the page layout is missing
455
+ def page_layout_missing_warning
456
+ hint_with_tooltip(
457
+ Alchemy.t(:page_definition_missing),
458
+ class: 'inline warning icon'
459
+ )
460
+ end
461
+
429
462
  private
430
463
 
431
464
  def permission_from_options(options)
@@ -13,14 +13,21 @@ module Alchemy
13
13
  if content.blank?
14
14
  warning('Content is nil')
15
15
  return
16
- else
17
- content_name = content.name_for_label
18
16
  end
17
+
18
+ content_name = content.name_for_label
19
+
19
20
  if content.definition.blank?
20
21
  warning("Content #{content.name} is missing its definition")
21
- title = Alchemy.t(:content_definition_missing)
22
- content_name = %(<span class="warning icon" title="#{title}"></span>&nbsp;#{content_name}).html_safe
22
+
23
+ icon = hint_with_tooltip(
24
+ Alchemy.t(:content_definition_missing),
25
+ class: 'inline warning icon'
26
+ )
27
+
28
+ content_name = "#{icon} #{content_name}".html_safe
23
29
  end
30
+
24
31
  if content.has_validations?
25
32
  "#{content_name}<span class='validation_indicator'>*</span>".html_safe
26
33
  else
@@ -94,25 +94,6 @@ module Alchemy
94
94
  end
95
95
  end
96
96
 
97
- # This helper loads all elements from page that have EssenceSelects in them.
98
- #
99
- # It returns a javascript function that replaces all editor partials of this elements.
100
- #
101
- # We need this while updating, creating or trashing an element,
102
- # because another element on the same page could have a element selector in it.
103
- #
104
- # In cases like this one wants Ember.js databinding!
105
- #
106
- def update_essence_select_elements(page, element)
107
- elements = page.elements.not_trashed.joins(:contents)
108
- .where(["#{Content.table_name}.element_id != ?", element.id])
109
- .where(Content.table_name => {essence_type: "Alchemy::EssenceSelect"})
110
- return if elements.blank?
111
- elements.collect do |el|
112
- render 'alchemy/admin/elements/refresh_editor', element: el
113
- end.join.html_safe
114
- end
115
-
116
97
  # CSS classes for the element editor partial.
117
98
  def element_editor_classes(element, local_assigns)
118
99
  [
@@ -56,41 +56,18 @@ module Alchemy
56
56
  render 'alchemy/admin/contents/missing', {element: element, name: name, options: options}
57
57
  end
58
58
 
59
- def essence_picture_thumbnail(content, options)
60
- ingredient = content.ingredient
59
+ # Renders a thumbnail for given EssencePicture content with correct cropping and size
60
+ def essence_picture_thumbnail(content, options = {})
61
+ picture = content.ingredient
61
62
  essence = content.essence
62
- return if ingredient.blank?
63
63
 
64
- crop = !(essence.crop_size.blank? && essence.crop_from.blank?) ||
65
- (
66
- content.settings_value(:crop, options) == true ||
67
- content.settings_value(:crop, options) == "true"
68
- )
69
-
70
- size = if essence.render_size.blank?
71
- content.settings_value(:size, options)
72
- else
73
- essence.render_size
74
- end
75
-
76
- image_options = {
77
- size: essence.thumbnail_size(size, crop),
78
- crop_from: essence.crop_from.blank? ? nil : essence.crop_from,
79
- crop_size: essence.crop_size.blank? ? nil : essence.crop_size,
80
- crop: crop ? 'crop' : nil,
81
- upsample: content.settings_value(:upsample, options)
82
- }
64
+ return if picture.nil?
83
65
 
84
66
  image_tag(
85
- alchemy.thumbnail_path({
86
- id: ingredient.id,
87
- name: ingredient.urlname,
88
- sh: ingredient.security_token(image_options),
89
- format: ingredient.image_file_format
90
- }.merge(image_options)),
91
- alt: ingredient.name,
67
+ essence.thumbnail_url(options),
68
+ alt: picture.name,
92
69
  class: 'img_paddingtop',
93
- title: Alchemy.t(:image_name) + ": #{ingredient.name}"
70
+ title: Alchemy.t(:image_name) + ": #{picture.name}"
94
71
  )
95
72
  end
96
73
 
@@ -11,24 +11,11 @@ module Alchemy
11
11
  def alchemy_main_navigation_entry(alchemy_module)
12
12
  render(
13
13
  'alchemy/admin/partials/main_navigation_entry',
14
- alchemy_module: alchemy_module.stringify_keys,
15
- navigation: module_main_navigation(alchemy_module)
14
+ alchemy_module: alchemy_module,
15
+ navigation: alchemy_module['navigation']
16
16
  )
17
17
  end
18
18
 
19
- # Renders the subnavigation from current module
20
- #
21
- # We find the module from current controller and index action.
22
- #
23
- def admin_subnavigation
24
- if current_alchemy_module.present?
25
- render(
26
- 'alchemy/admin/partials/sub_navigation',
27
- entries: current_sub_navigation
28
- )
29
- end
30
- end
31
-
32
19
  # Used for checking the main navi permissions
33
20
  #
34
21
  # To let your module be navigatable by the user you have to provide an Ability for it.
@@ -46,7 +33,6 @@ module Alchemy
46
33
  # can :index, :my_admin_posts
47
34
  #
48
35
  def navigate_module(navigation)
49
- navigation.stringify_keys!
50
36
  [
51
37
  navigation['action'].to_sym,
52
38
  navigation['controller'].to_s.gsub(/\A\//, '').gsub(/\//, '_').to_sym
@@ -58,8 +44,9 @@ module Alchemy
58
44
  def main_navigation_css_classes(navigation)
59
45
  [
60
46
  'main_navi_entry',
61
- admin_mainnavi_active?(navigation) ? 'active' : nil
62
- ].compact.join(' ')
47
+ admin_mainnavi_active?(navigation) ? 'active' : nil,
48
+ navigation.key?('sub_navigation') ? 'has_sub_navigation' : nil
49
+ ].compact
63
50
  end
64
51
 
65
52
  # Returns true if given navi entry is in params controller and action
@@ -148,7 +135,7 @@ module Alchemy
148
135
  # A Alchemy module definition
149
136
  #
150
137
  def url_options_for_module(alchemy_module)
151
- url_options_for_navigation_entry(module_main_navigation(alchemy_module))
138
+ url_options_for_navigation_entry(alchemy_module['navigation'] || {})
152
139
  end
153
140
 
154
141
  # Returns a url options hash for given navigation entry.
@@ -157,7 +144,6 @@ module Alchemy
157
144
  # A Alchemy module navigation entry
158
145
  #
159
146
  def url_options_for_navigation_entry(entry)
160
- entry.stringify_keys!
161
147
  {
162
148
  controller: entry['controller'],
163
149
  action: entry['action'],
@@ -172,40 +158,15 @@ module Alchemy
172
158
  module_definition_for(controller: params[:controller], action: 'index')
173
159
  end
174
160
 
175
- # Returns the sub navigation for current Alchemy module.
176
- #
177
- def current_sub_navigation
178
- module_sub_navigation(module_main_navigation(current_alchemy_module))
179
- end
180
-
181
- # Returns navigation entries from given module.
182
- #
183
- def module_main_navigation(alchemy_module)
184
- alchemy_module.fetch('navigation', {}).stringify_keys
185
- end
186
-
187
- # Returns sub navigation entries from given module.
188
- #
189
- def module_sub_navigation(alchemy_module)
190
- alchemy_module.fetch('sub_navigation', []).map(&:stringify_keys)
191
- end
192
-
193
- # Returns nested navigation entries for given module.
194
- #
195
- def module_nested_navigation(alchemy_module)
196
- alchemy_module.fetch('nested', []).map(&:stringify_keys)
197
- end
198
-
199
161
  # Returns true if the current controller and action is in a modules navigation definition.
200
162
  #
201
- def admin_mainnavi_active?(main_navigation)
202
- main_navigation.stringify_keys!
163
+ def admin_mainnavi_active?(navigation)
203
164
  # Has the given navigation entry a active sub navigation?
204
- has_active_entry?(module_sub_navigation(main_navigation)) ||
165
+ has_active_entry?(navigation['sub_navigation'] || []) ||
205
166
  # Has the given navigation entry a active nested navigation?
206
- has_active_entry?(module_nested_navigation(main_navigation)) ||
167
+ has_active_entry?(navigation['nested'] || []) ||
207
168
  # Is the navigation entry active?
208
- entry_active?(main_navigation)
169
+ entry_active?(navigation || {})
209
170
  end
210
171
 
211
172
  # Returns true if the given entry's controller is current controller
@@ -219,7 +180,8 @@ module Alchemy
219
180
  # Also checks if given entry has a +nested_actions+ key, if so it checks if one of them is current controller's action
220
181
  #
221
182
  def is_entry_action_active?(entry)
222
- entry['action'] == params[:action] || entry['nested_actions'].to_a.include?(params[:action])
183
+ entry['action'] == params[:action] ||
184
+ entry.fetch('nested_actions', []).include?(params[:action])
223
185
  end
224
186
 
225
187
  # Returns true if an entry of given entries is active.
@@ -228,7 +190,7 @@ module Alchemy
228
190
  # Alchemy module navigation entries.
229
191
  #
230
192
  def has_active_entry?(entries)
231
- !entries.detect { |entry| entry_active?(entry) }.nil?
193
+ entries.any? { |entry| entry_active?(entry) }
232
194
  end
233
195
  end
234
196
  end