alchemy_cms 2.3.2 → 2.4.beta2

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 (164) hide show
  1. data/.gitignore +1 -2
  2. data/.travis.yml +1 -5
  3. data/Gemfile +4 -4
  4. data/README.md +22 -15
  5. data/alchemy_cms.gemspec +38 -38
  6. data/app/assets/javascripts/alchemy/alchemy.base.js +2 -2
  7. data/app/assets/javascripts/alchemy/alchemy.buttons.js.coffee +31 -0
  8. data/app/assets/javascripts/alchemy/alchemy.dragndrop.js +1 -1
  9. data/app/assets/javascripts/alchemy/alchemy.elements_window.js +24 -15
  10. data/app/assets/javascripts/alchemy/alchemy.gui.js.coffee +9 -3
  11. data/app/assets/javascripts/alchemy/alchemy.jquery_loader.js +2 -2
  12. data/app/assets/javascripts/alchemy/alchemy.js +1 -0
  13. data/app/assets/javascripts/alchemy/alchemy.menubar.js +4 -12
  14. data/app/assets/javascripts/alchemy/alchemy.onload.js.coffee +32 -0
  15. data/app/assets/javascripts/alchemy/alchemy.windows.js +4 -4
  16. data/app/assets/stylesheets/alchemy/_defaults.scss +0 -1
  17. data/app/assets/stylesheets/alchemy/alchemy.css +2 -1
  18. data/app/assets/stylesheets/alchemy/base.css.scss +2 -54
  19. data/app/assets/stylesheets/alchemy/elements.css.scss +8 -5
  20. data/app/assets/stylesheets/alchemy/errors.css.scss +51 -0
  21. data/app/assets/stylesheets/alchemy/flash.css.scss +0 -2
  22. data/app/assets/stylesheets/alchemy/form_elements.css.scss +31 -75
  23. data/app/assets/stylesheets/alchemy/icons.css.scss +5 -5
  24. data/app/assets/stylesheets/alchemy/menubar.css.scss +0 -2
  25. data/app/assets/stylesheets/alchemy/sitemap.css.scss +0 -1
  26. data/app/assets/stylesheets/alchemy/tables.css.scss +3 -1
  27. data/app/controllers/alchemy/admin/base_controller.rb +19 -12
  28. data/app/controllers/alchemy/admin/elements_controller.rb +52 -24
  29. data/app/controllers/alchemy/admin/pages_controller.rb +11 -5
  30. data/app/controllers/alchemy/admin/resources_controller.rb +3 -4
  31. data/app/controllers/alchemy/admin/users_controller.rb +1 -0
  32. data/app/controllers/alchemy/base_controller.rb +34 -8
  33. data/app/controllers/alchemy/pictures_controller.rb +16 -1
  34. data/app/controllers/alchemy/user_sessions_controller.rb +6 -1
  35. data/app/helpers/alchemy/base_helper.rb +14 -0
  36. data/app/helpers/alchemy/elements_helper.rb +10 -5
  37. data/app/helpers/alchemy/pages_helper.rb +1 -2
  38. data/app/helpers/alchemy/url_helper.rb +43 -24
  39. data/app/models/alchemy/element.rb +23 -16
  40. data/app/models/alchemy/page.rb +25 -14
  41. data/app/models/alchemy/picture.rb +24 -0
  42. data/app/views/alchemy/admin/contents/create.js.erb +1 -1
  43. data/app/views/alchemy/admin/elements/_new_element_form.html.erb +1 -1
  44. data/app/views/alchemy/admin/elements/create.js.erb +11 -3
  45. data/app/views/alchemy/admin/elements/fold.js.erb +1 -1
  46. data/app/views/alchemy/admin/elements/new.html.erb +1 -1
  47. data/app/views/alchemy/admin/elements/update.js.erb +1 -1
  48. data/app/views/alchemy/admin/essence_pictures/crop.html.erb +1 -1
  49. data/app/views/alchemy/admin/languages/_form.html.erb +1 -1
  50. data/app/views/alchemy/admin/pages/_new_page_form.html.erb +1 -1
  51. data/app/views/alchemy/admin/pages/_page.html.erb +1 -1
  52. data/app/views/alchemy/admin/pages/configure.html.erb +10 -6
  53. data/app/views/alchemy/admin/pages/configure_external.html.erb +1 -1
  54. data/app/views/alchemy/admin/pages/edit.html.erb +1 -2
  55. data/app/views/alchemy/admin/pages/new.html.erb +2 -2
  56. data/app/views/alchemy/admin/pages/update.js.erb +10 -2
  57. data/app/views/alchemy/admin/partials/_sub_navigation_tab.html.erb +5 -5
  58. data/app/views/alchemy/admin/pictures/_filter_bar.html.erb +2 -1
  59. data/app/views/alchemy/admin/pictures/_picture.html.erb +2 -2
  60. data/app/views/alchemy/admin/pictures/_picture_to_assign.html.erb +2 -1
  61. data/app/views/alchemy/admin/pictures/show_in_window.html.erb +3 -2
  62. data/app/views/alchemy/admin/resources/_form.html.erb +1 -1
  63. data/app/views/alchemy/admin/users/_table.html.erb +4 -4
  64. data/app/views/alchemy/admin/users/edit.html.erb +1 -1
  65. data/app/views/alchemy/admin/users/new.html.erb +1 -1
  66. data/app/views/alchemy/base/error_notice.js.erb +1 -1
  67. data/app/views/alchemy/base/remote_errors.js.erb +2 -3
  68. data/app/views/alchemy/essences/_essence_picture_editor.html.erb +10 -7
  69. data/app/views/alchemy/essences/_essence_picture_view.html.erb +24 -21
  70. data/app/views/layouts/alchemy/admin.html.erb +9 -31
  71. data/bin/alchemy +20 -24
  72. data/config/alchemy/config.yml +6 -0
  73. data/config/alchemy/page_layouts.yml +2 -0
  74. data/config/authorization_rules.rb +1 -1
  75. data/config/locales/alchemy.de.yml +3 -0
  76. data/config/locales/alchemy.en.yml +4 -1
  77. data/db/migrate/20121026100815_alchemy_two_point_three.rb +312 -0
  78. data/lib/alchemy/mount_point.rb +9 -3
  79. data/lib/alchemy/page_layout.rb +89 -73
  80. data/lib/alchemy/version.rb +1 -1
  81. data/lib/rails/generators/alchemy/deploy_script/deploy_script_generator.rb +45 -12
  82. data/lib/rails/generators/alchemy/deploy_script/templates/deploy.rb.tt +3 -1
  83. data/lib/rails/generators/alchemy/essence/essence_generator.rb +51 -0
  84. data/lib/rails/generators/alchemy/essence/templates/editor.html.erb +18 -0
  85. data/lib/rails/generators/alchemy/essence/templates/view.html.erb +2 -0
  86. data/lib/rails/templates/alchemy.rb +1 -1
  87. data/lib/tasks/install.rake +57 -2
  88. data/spec/controllers/admin/elements_controller_spec.rb +170 -22
  89. data/spec/controllers/admin/trash_controller_spec.rb +1 -1
  90. data/spec/controllers/admin/users_controller_spec.rb +36 -0
  91. data/spec/controllers/base_controller_spec.rb +12 -1
  92. data/spec/controllers/elements_controller_spec.rb +17 -13
  93. data/spec/controllers/pictures_controller_spec.rb +4 -4
  94. data/spec/dummy/config/application.rb +1 -1
  95. data/spec/dummy/db/migrate/20121026100815_alchemy_two_point_three.rb +312 -0
  96. data/spec/dummy/db/migrate/20121026104128_create_events.rb +19 -0
  97. data/spec/dummy/db/schema.rb +1 -1
  98. data/spec/factories.rb +0 -1
  99. data/spec/helpers/base_helper_spec.rb +48 -0
  100. data/spec/helpers/elements_helper_spec.rb +14 -3
  101. data/spec/helpers/url_helper_spec.rb +8 -3
  102. data/spec/integration/picture_security_spec.rb +35 -0
  103. data/spec/integration/translation_integration_spec.rb +6 -5
  104. data/spec/models/element_spec.rb +5 -5
  105. data/spec/models/page_layout_spec.rb +10 -16
  106. data/spec/models/page_spec.rb +25 -2
  107. data/spec/models/picture_spec.rb +24 -2
  108. data/spec/routing_spec.rb +115 -115
  109. data/spec/support/alchemy/specs_helpers.rb +4 -4
  110. data/{app/assets/images/alchemy → vendor/assets/images}/Jcrop.gif +0 -0
  111. data/vendor/assets/javascripts/jquery_plugins/jquery.Jcrop.min.js +19 -243
  112. data/vendor/assets/javascripts/jquery_plugins/jquery.selectboxit.min.js +1 -0
  113. data/vendor/assets/javascripts/jquery_plugins/jquery.ui.tabspaging.js +6 -6
  114. data/vendor/assets/stylesheets/jquery.Jcrop.min.css +28 -0
  115. metadata +48 -82
  116. data/app/assets/javascripts/alchemy/alchemy.buttons.js +0 -50
  117. data/app/assets/stylesheets/alchemy/jquery.Jcrop.css.scss +0 -54
  118. data/app/helpers/alchemy/pictures_helper.rb +0 -19
  119. data/db/migrate/20100607143125_create_pages.rb +0 -34
  120. data/db/migrate/20100607144254_create_elements.rb +0 -20
  121. data/db/migrate/20100607145256_create_contents.rb +0 -18
  122. data/db/migrate/20100607145719_create_users.rb +0 -32
  123. data/db/migrate/20100607150611_create_pictures.rb +0 -16
  124. data/db/migrate/20100607150812_create_attachments.rb +0 -16
  125. data/db/migrate/20100607153647_create_folded_pages.rb +0 -13
  126. data/db/migrate/20100607161345_create_essence_texts.rb +0 -19
  127. data/db/migrate/20100607162339_create_elements_pages.rb +0 -12
  128. data/db/migrate/20100607193638_create_essence_pictures.rb +0 -23
  129. data/db/migrate/20100607193646_create_essence_richtexts.rb +0 -16
  130. data/db/migrate/20100607193653_create_essence_htmls.rb +0 -13
  131. data/db/migrate/20100609111653_create_essence_dates.rb +0 -13
  132. data/db/migrate/20100609111809_create_essence_files.rb +0 -15
  133. data/db/migrate/20100609111821_create_essence_flashes.rb +0 -16
  134. data/db/migrate/20100609111837_create_essence_videos.rb +0 -18
  135. data/db/migrate/20100616150753_create_essence_audios.rb +0 -17
  136. data/db/migrate/20100812085225_add_crop_from_and_crop_size_to_essence_pictures.rb +0 -11
  137. data/db/migrate/20100909140701_change_essence_htmls_source_column_type.rb +0 -9
  138. data/db/migrate/20101109150312_alter_pages_visible_column_default.rb +0 -9
  139. data/db/migrate/20101109151812_create_languages.rb +0 -19
  140. data/db/migrate/20101216151419_add_language_id_to_pages.rb +0 -27
  141. data/db/migrate/20101216155216_add_index_to_languages.rb +0 -9
  142. data/db/migrate/20101216173323_add_default_to_languages.rb +0 -9
  143. data/db/migrate/20101218130049_add_urlname_index_to_pages.rb +0 -9
  144. data/db/migrate/20110115123343_remove_css_class_default_from_essence_pictures.rb +0 -11
  145. data/db/migrate/20110224105120_change_pages_visible_default.rb +0 -11
  146. data/db/migrate/20110228182659_remove_default_page_layout_from_pages.rb +0 -11
  147. data/db/migrate/20110414163140_remove_display_name_from_elements.rb +0 -11
  148. data/db/migrate/20110511100516_rename_essence_texts_title_to_link_title.rb +0 -9
  149. data/db/migrate/20110529130429_create_cells.rb +0 -14
  150. data/db/migrate/20110529130500_add_cell_id_to_elements.rb +0 -11
  151. data/db/migrate/20110530102804_change_pages_page_layout_column.rb +0 -11
  152. data/db/migrate/20110707190728_add_render_size_to_essence_pictures.rb +0 -9
  153. data/db/migrate/20110711142057_change_open_link_in_new_window_to_link_target.rb +0 -19
  154. data/db/migrate/20110919110451_add_default_role_to_users.rb +0 -9
  155. data/db/migrate/20111116125112_namespace_alchemy_models.rb +0 -23
  156. data/db/migrate/20120216135355_add_country_code_to_languages.rb +0 -9
  157. data/db/migrate/20120608085509_create_alchemy_essence_selects.rb +0 -11
  158. data/db/migrate/20120611221734_create_alchemy_essence_booleans.rb +0 -11
  159. data/db/migrate/20120704181529_add_upload_hash_to_alchemy_picture.rb +0 -5
  160. data/db/migrate/20120705214247_acts_as_taggable_on_migration.rb +0 -28
  161. data/db/migrate/20120728185830_add_cached_tag_list_to_alchemy_pictures.rb +0 -5
  162. data/db/migrate/20120831135441_set_alchemy_languages_country_code_default_to_empty_string.rb +0 -9
  163. data/spec/helpers/pictures_helper_spec.rb +0 -14
  164. data/vendor/assets/javascripts/jquery_plugins/jquery.selectBoxIt.js +0 -1909
@@ -95,7 +95,6 @@ div.sitemap_page:hover a.sitemap_pagename_link {
95
95
  }
96
96
 
97
97
  div.sitemap_left_images {
98
- width: 16px;
99
98
  margin-top: 3px;
100
99
  float: left;
101
100
  margin-right: 8px;
@@ -32,7 +32,7 @@ table tr td.tools {
32
32
  @include disable-user-select;
33
33
  }
34
34
 
35
- td.label, td.input, td.submit, td.select, td.value {
35
+ td.label, td.input, td.submit, td.select, td.value, td.note {
36
36
  padding: 2px 0;
37
37
  vertical-align: top;
38
38
  }
@@ -45,6 +45,8 @@ td.input, td.submit, td.select, td.value {
45
45
  }
46
46
  }
47
47
 
48
+ td.submit p.foot_note { float: left; padding-top: 1em }
49
+
48
50
  td.label {
49
51
  white-space: nowrap;
50
52
  width: 80px;
@@ -3,14 +3,14 @@ module Alchemy
3
3
  class BaseController < Alchemy::BaseController
4
4
 
5
5
  include Userstamp
6
-
6
+ before_filter { enforce_ssl if ssl_required? && !request.ssl? }
7
7
  before_filter :set_translation
8
8
 
9
9
  helper_method :clipboard_empty?, :trash_empty?, :get_clipboard, :is_admin?
10
10
 
11
11
  filter_access_to :all
12
12
 
13
- rescue_from Exception, :with => :exception_handler
13
+ rescue_from Exception, :with => :exception_handler unless Rails.env == 'test'
14
14
 
15
15
  layout 'alchemy/admin'
16
16
 
@@ -24,16 +24,13 @@ module Alchemy
24
24
 
25
25
  # Logs the current exception to the error log.
26
26
  def exception_logger(e)
27
- message = "\n+++++++++ Error: #{e} +++++++++++++\n\n"
28
- e.backtrace.each do |line|
29
- message += "#{line}\n"
30
- end
31
- logger.error(message)
27
+ Rails.logger.error("\n#{e.class} #{e.message} in #{e.backtrace.first}")
28
+ Rails.logger.error(e.backtrace[1..50].each { |l| l.gsub(/#{Rails.root.to_s}/, '') }.join("\n"))
32
29
  end
33
30
 
34
31
  # Displays an error notice in the Alchemy backend.
35
32
  def show_error_notice(e)
36
- @notice = "Error: #{e}"
33
+ @notice = "Error: #{e.message[0..99]}"
37
34
  @trace = e.backtrace
38
35
  if request.xhr?
39
36
  render :action => "error_notice", :layout => false
@@ -86,18 +83,28 @@ module Alchemy
86
83
  current_user.admin?
87
84
  end
88
85
 
89
- def render_errors_or_redirect(object, redirect_url, flash_notice, button = nil)
86
+ # Displays errors in a #errors div if any errors are present on the object.
87
+ # Or redirects to the given redirect url.
88
+ def render_errors_or_redirect(object, redirect_url, flash_notice)
90
89
  if object.errors.empty?
91
90
  @redirect_url = redirect_url
92
91
  flash[:notice] = t(flash_notice)
93
92
  render :action => :redirect
94
93
  else
95
- render_remote_errors(object, button)
94
+ render_remote_errors(object)
96
95
  end
97
96
  end
98
97
 
99
- def render_remote_errors(object, button = nil)
100
- @button = button
98
+ # Displays an unordered list of objects errors in an errors div.
99
+ #
100
+ # Note: You have to have a hidden div with the id +#errors+ in your form, to make this work.
101
+ #
102
+ # You can pass a div id as second argument to display the errors in alternative div.
103
+ #
104
+ # Hint: If you use an alternative div, please use the +errors+ css class to get the correct styling.
105
+ #
106
+ def render_remote_errors(object, error_div_id = nil)
107
+ @error_div_id = error_div_id || '#errors'
101
108
  @errors = ("<ul>" + object.errors.full_messages.map { |e| "<li>#{e}</li>" }.join + "</ul>").html_safe
102
109
  render :action => :remote_errors
103
110
  end
@@ -37,30 +37,31 @@ module Alchemy
37
37
  # Creates a element as discribed in config/alchemy/elements.yml on page via AJAX.
38
38
  def create
39
39
  @page = Page.find(params[:element][:page_id])
40
- @element_name = params[:element][:name] # storing the original element name, because the model alters the params hash
41
- @paste_from_clipboard = !params[:paste_from_clipboard].blank?
42
- if @paste_from_clipboard
43
- source_element = Element.find(element_from_clipboard[:id])
44
- @element = Element.copy(source_element, {:page_id => @page.id})
45
- if element_from_clipboard[:action] == 'cut'
46
- @cutted_element_id = source_element.id
47
- @clipboard.remove :elements, source_element.id
48
- source_element.destroy
40
+ Element.transaction do
41
+ if @paste_from_clipboard = params[:paste_from_clipboard].present?
42
+ @element = paste_element_from_clipboard
43
+ else
44
+ @element = Element.new_from_scratch(params[:element])
45
+ if @page.can_have_cells?
46
+ @cell = find_or_create_cell
47
+ @element.cell = @cell
48
+ end
49
+ @element.save!
50
+ end
51
+ if @insert_at_top = @page.definition['insert_elements_at'] == 'top'
52
+ @element.move_to_top
49
53
  end
50
- else
51
- @element = Element.new_from_scratch(params[:element])
52
54
  end
53
- put_element_in_cell if @page.can_have_cells?
54
- if @element.save
55
+ @cell_name = @cell.nil? ? "for_other_elements" : @cell.name
56
+ if @element.valid?
55
57
  render :action => :create
56
58
  else
57
- render_remote_errors(@element, 'form#new_alchemy_element button.button')
59
+ render_remote_errors(@element, params[:paste_from_clipboard].nil? ? nil : '#paste_element_errors')
58
60
  end
59
61
  end
60
62
 
61
- # Saves all contents in the elements by calling save_content on each content
63
+ # Saves all contents in the elements by calling save_contents.
62
64
  # And then updates the element itself.
63
- # If a Ferret::FileNotFoundError raises we gonna catch it and rebuilding the index.
64
65
  def update
65
66
  @element = Element.find_by_id(params[:id])
66
67
  if @element.save_contents(params)
@@ -102,16 +103,26 @@ module Alchemy
102
103
 
103
104
  private
104
105
 
105
- def put_element_in_cell
106
- element_with_cell_name = @paste_from_clipboard ? params[:paste_from_clipboard] : @element_name
107
- cell_definition = Cell.definition_for(element_with_cell_name.split('#').last) if !element_with_cell_name.blank?
108
- if cell_definition
109
- @cell = @page.cells.find_or_create_by_name(cell_definition['name'])
110
- @element.cell = @cell
111
- return true
106
+ # Returns the cell for element name in params.
107
+ # Creates the cell if necessary.
108
+ def find_or_create_cell
109
+ if @paste_from_clipboard
110
+ element_with_cell_name = params[:paste_from_clipboard]
112
111
  else
113
- return false
112
+ element_with_cell_name = params[:element][:name]
113
+ end
114
+ if element_with_cell_name.blank?
115
+ raise "No element with cell name given. Please provide the cell name after the element name (or id) seperated by #."
116
+ end
117
+ unless element_with_cell_name.include?('#')
118
+ return nil
114
119
  end
120
+ cell_name = element_with_cell_name.split('#').last
121
+ cell_definition = Cell.definition_for(cell_name)
122
+ if cell_definition.blank?
123
+ raise "Cell definition not found for #{cell_name}"
124
+ end
125
+ @page.cells.find_or_create_by_name(cell_definition['name'])
115
126
  end
116
127
 
117
128
  def element_from_clipboard
@@ -119,6 +130,23 @@ module Alchemy
119
130
  @clipboard.get(:elements, params[:paste_from_clipboard])
120
131
  end
121
132
 
133
+ def paste_element_from_clipboard
134
+ @source_element = Element.find(element_from_clipboard[:id])
135
+ new_attributes = {:page_id => @page.id}
136
+ if @page.can_have_cells?
137
+ new_attributes = new_attributes.merge({:cell_id => find_or_create_cell.id})
138
+ end
139
+ element = Element.copy(@source_element, new_attributes)
140
+ cut_element if element_from_clipboard[:action] == 'cut'
141
+ element
142
+ end
143
+
144
+ def cut_element
145
+ @cutted_element_id = @source_element.id
146
+ @clipboard.remove :elements, @source_element.id
147
+ @source_element.destroy
148
+ end
149
+
122
150
  end
123
151
  end
124
152
  end
@@ -34,11 +34,14 @@ module Alchemy
34
34
  # Setting the locale to pages language. so the page content has its correct translation
35
35
  ::I18n.locale = @page.language_code
36
36
  render :layout => layout_for_page
37
+ rescue Exception => e
38
+ exception_logger(e)
39
+ render :file => Rails.root.join('public', '500.html'), :status => 500, :layout => false
37
40
  end
38
41
 
39
42
  def new
40
43
  @page = Page.new(:layoutpage => params[:layoutpage] == 'true', :parent_id => params[:parent_id])
41
- @page_layouts = PageLayout.get_layouts_for_select(session[:language_id], @page.layoutpage?)
44
+ @page_layouts = PageLayout.layouts_for_select(session[:language_id], @page.layoutpage?)
42
45
  @clipboard_items = Page.all_from_clipboard_for_select(get_clipboard[:pages], session[:language_id], @page.layoutpage?)
43
46
  render :layout => false
44
47
  end
@@ -49,18 +52,18 @@ module Alchemy
49
52
  params[:page][:language_code] ||= parent.language ? parent.language.code : Language.get_default.code
50
53
  if !params[:paste_from_clipboard].blank?
51
54
  source_page = Page.find(params[:paste_from_clipboard])
52
- page = Page.copy(source_page, {
55
+ @page = Page.copy(source_page, {
53
56
  :name => params[:page][:name].blank? ? source_page.name + ' (' + t('Copy') + ')' : params[:page][:name],
54
57
  :urlname => '',
55
58
  :title => '',
56
59
  :parent_id => params[:page][:parent_id],
57
60
  :language => parent.language
58
61
  })
59
- source_page.copy_children_to(page) unless source_page.children.blank?
62
+ source_page.copy_children_to(@page) unless source_page.children.blank?
60
63
  else
61
- page = Page.create(params[:page])
64
+ @page = Page.create(params[:page])
62
65
  end
63
- render_errors_or_redirect(page, parent.layoutpage? ? admin_layoutpages_path : admin_pages_path, t("Page created", :name => page.name), '#alchemyOverlay button.button')
66
+ render_errors_or_redirect(@page, @page.valid? ? edit_admin_page_path(@page) : admin_pages_path, t("Page created", :name => @page.name))
64
67
  end
65
68
 
66
69
  # Edit the content of the page and all its elements and contents.
@@ -82,12 +85,15 @@ module Alchemy
82
85
  if @page.redirects_to_external?
83
86
  render :action => 'configure_external', :layout => false
84
87
  else
88
+ @page_layouts = PageLayout.layouts_with_own_for_select(@page.page_layout, session[:language_id], @page.layoutpage?)
85
89
  render :layout => false
86
90
  end
87
91
  end
88
92
 
89
93
  def update
90
94
  # fetching page via before filter
95
+ # storing old page_layout value, because unfurtunally rails @page.changes does not work here.
96
+ @old_page_layout = @page.page_layout
91
97
  if @page.update_attributes(params[:page])
92
98
  @notice = t("Page saved", :name => @page.name)
93
99
  @while_page_edit = request.referer.include?('edit')
@@ -63,10 +63,9 @@ module Alchemy
63
63
  @_resource_handler ||= Alchemy::Resource.new(controller_path, alchemy_module)
64
64
  end
65
65
 
66
- protected
66
+ protected
67
67
 
68
-
69
- def render_errors_or_redirect(object, redirect_url, flash_notice, button = nil)
68
+ def render_errors_or_redirect(object, redirect_url, flash_notice)
70
69
  if object.errors.empty?
71
70
  @redirect_url = redirect_url
72
71
  flash[:notice] = t(flash_notice)
@@ -76,7 +75,7 @@ module Alchemy
76
75
  end
77
76
  else
78
77
  respond_to do |format|
79
- format.js { render_remote_errors(object, button) }
78
+ format.js { render_remote_errors(object) }
80
79
  format.html { render :action => :new }
81
80
  end
82
81
  end
@@ -51,6 +51,7 @@ module Alchemy
51
51
 
52
52
  def update
53
53
  # User is fetched via before filter
54
+ params[:user].delete(:role) unless permitted_to?(:update_role)
54
55
  @user.update_attributes(params[:user])
55
56
  Notifications.admin_user_created(@user).deliver if params[:send_credentials]
56
57
  render_errors_or_redirect(
@@ -15,15 +15,19 @@ module Alchemy
15
15
  def current_server
16
16
  # For local development server
17
17
  if request.port != 80
18
- "http://#{request.host}:#{request.port}"
18
+ "#{request.protocol}#{request.host}:#{request.port}"
19
19
  # For remote production server
20
20
  else
21
- "http://#{request.host}"
21
+ "#{request.protocol}#{request.host}"
22
22
  end
23
23
  end
24
24
 
25
+ # Returns the configuratin value of given key.
26
+ #
27
+ # Config file is in +config/alchemy/config.yml+
28
+ #
25
29
  def configuration(name)
26
- return Alchemy::Config.get(name)
30
+ Alchemy::Config.get(name)
27
31
  end
28
32
 
29
33
  def multi_language?
@@ -56,6 +60,8 @@ module Alchemy
56
60
  session[:current_locale] = ::I18n.locale = params[:locale]
57
61
  elsif current_user && current_user.language.present?
58
62
  ::I18n.locale = current_user.language
63
+ elsif Rails.env == 'test' # OMG I hate to do this. But it helps...
64
+ ::I18n.locale = 'en'
59
65
  else
60
66
  ::I18n.locale = request.env['HTTP_ACCEPT_LANGUAGE'].try(:scan, /^[a-z]{2}/).try(:first)
61
67
  end
@@ -66,8 +72,7 @@ module Alchemy
66
72
  if params[:lang].blank? and session[:language_id].blank?
67
73
  set_language_to_default
68
74
  elsif !params[:lang].blank?
69
- set_language_from(params[:lang])
70
- ::I18n.locale = params[:lang]
75
+ ::I18n.locale = set_language_from(params[:lang])
71
76
  end
72
77
  end
73
78
 
@@ -95,9 +100,8 @@ module Alchemy
95
100
 
96
101
  def store_language_in_session(language)
97
102
  if language && language.id
98
- return if language.id == session[:language_id]
99
- session[:language_code] = language.code
100
103
  session[:language_id] = language.id
104
+ session[:language_code] = language.code
101
105
  else
102
106
  logger.warn "!!!! Language not found for #{language.inspect}. Setting to default!"
103
107
  set_language_to_default
@@ -159,6 +163,28 @@ module Alchemy
159
163
  render :file => Rails.root.join("public/404.html"), :status => 404, :layout => !@page.nil?
160
164
  end
161
165
 
166
+ # Enforce ssl for login and all admin modules.
167
+ #
168
+ # Default is +false+
169
+ #
170
+ # === Usage
171
+ #
172
+ # #config.yml
173
+ # require_ssl: true
174
+ #
175
+ # === Note
176
+ #
177
+ # You have to create a ssl certificate if you want to use the ssl protection
178
+ #
179
+ def ssl_required?
180
+ (Rails.env == 'production' || Rails.env == 'staging') && configuration(:require_ssl)
181
+ end
182
+
183
+ # Redirects request to ssl.
184
+ def enforce_ssl
185
+ redirect_to url_for(protocol: 'https')
186
+ end
187
+
162
188
  protected
163
189
 
164
190
  def permission_denied
@@ -171,7 +197,7 @@ module Alchemy
171
197
  elsif request.xhr?
172
198
  respond_to do |format|
173
199
  format.js {
174
- render :js => "Alchemy.growl('#{t('You are not authorized')}', 'warning'); Alchemy.enableButton('button.button, a.button, input.button');"
200
+ render :js => "Alchemy.growl('#{t('You are not authorized')}', 'warning'); Alchemy.Buttons.enable();"
175
201
  }
176
202
  format.html {
177
203
  render :partial => 'alchemy/admin/partials/flash', :locals => {:message => t('You are not authorized'), :flash_type => 'warning'}
@@ -3,7 +3,7 @@ module Alchemy
3
3
 
4
4
  caches_page :show, :thumbnail, :zoom
5
5
 
6
- before_filter :load_picture
6
+ before_filter :load_picture, :ensure_secure_params
7
7
 
8
8
  filter_access_to :show, :attribute_check => true, :model => Alchemy::Picture, :load_method => :load_picture
9
9
  filter_access_to :thumbnail
@@ -60,5 +60,20 @@ module Alchemy
60
60
  @picture ||= Picture.find(params[:id])
61
61
  end
62
62
 
63
+ def ensure_secure_params
64
+ token = params[:sh]
65
+ digest = Digest::SHA1.hexdigest(secured_params)[0..15]
66
+ bad_request unless token && (token == digest)
67
+ end
68
+
69
+ def secured_params
70
+ [params[:id], params[:size], params[:crop], params[:crop_from], params[:crop_size], Rails.configuration.secret_token].join('-')
71
+ end
72
+
73
+ def bad_request
74
+ render :text => "Bad picture parameters in #{request.path}", :status => 400
75
+ return false
76
+ end
77
+
63
78
  end
64
79
  end
@@ -1,6 +1,7 @@
1
1
  module Alchemy
2
2
  class UserSessionsController < Alchemy::BaseController
3
3
 
4
+ before_filter { enforce_ssl if ssl_required? && !request.ssl? }
4
5
  before_filter :set_translation
5
6
  before_filter :check_user_count, :only => :login
6
7
 
@@ -20,9 +21,13 @@ module Alchemy
20
21
  if params[:send_credentials]
21
22
  Notifications.admin_user_created(@user).deliver
22
23
  end
24
+ flash[:notice] = t('Successfully signup admin user')
23
25
  redirect_to admin_dashboard_path
24
26
  end
25
27
  end
28
+ rescue Errno::ECONNREFUSED => e
29
+ flash[:error] = t(:signup_mail_delivery_error)
30
+ redirect_to admin_dashboard_path
26
31
  end
27
32
 
28
33
  def login
@@ -63,7 +68,7 @@ module Alchemy
63
68
  redirect_to root_url
64
69
  end
65
70
 
66
- private
71
+ private
67
72
 
68
73
  def check_user_count
69
74
  if User.count == 0
@@ -45,6 +45,20 @@ module Alchemy
45
45
  content_tag('span', '', :class => "icon #{icon_class}")
46
46
  end
47
47
 
48
+ # Returns a div with an icon and the passed content
49
+ # The default message type is info, but you can also pass
50
+ # other types like :warning or :error
51
+ #
52
+ # === Usage:
53
+ #
54
+ # <%= render_message :warning do
55
+ # <p>Caution! This is a warning!</p>
56
+ # <% end %>
57
+ #
58
+ def render_message(type = :info, &blk)
59
+ content_tag :div, render_icon(type) + capture(&blk), :class => "#{type} message"
60
+ end
61
+
48
62
  # Returns an array of all pages in the same branch from current.
49
63
  # I.e. used to find the active page in navigation.
50
64
  def breadcrumb(current)