alchemy_cms 2.5.0.b9 → 2.5.0.rc3

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 (101) hide show
  1. data/alchemy_cms.gemspec +2 -1
  2. data/app/assets/javascripts/alchemy/alchemy.base.js +0 -27
  3. data/app/assets/javascripts/alchemy/alchemy.growler.js +1 -1
  4. data/app/assets/javascripts/alchemy/alchemy.gui.js.coffee +0 -1
  5. data/app/assets/javascripts/alchemy/alchemy.onload.js.coffee +0 -4
  6. data/app/assets/javascripts/alchemy/alchemy.page_sorter.js +0 -1
  7. data/app/assets/stylesheets/alchemy/admin.css.scss +1 -0
  8. data/app/assets/stylesheets/alchemy/archive.scss +8 -6
  9. data/app/assets/stylesheets/alchemy/base.scss +3 -92
  10. data/app/assets/stylesheets/alchemy/elements.scss +2 -2
  11. data/app/assets/stylesheets/alchemy/flash.scss +16 -12
  12. data/app/assets/stylesheets/alchemy/frame.scss +10 -5
  13. data/app/assets/stylesheets/alchemy/icons.scss +2 -2
  14. data/app/assets/stylesheets/alchemy/pagination.scss +5 -5
  15. data/app/assets/stylesheets/alchemy/tables.scss +2 -2
  16. data/app/assets/stylesheets/alchemy/toolbar.scss +79 -0
  17. data/app/controllers/alchemy/admin/base_controller.rb +3 -2
  18. data/app/controllers/alchemy/admin/dashboard_controller.rb +1 -1
  19. data/app/controllers/alchemy/admin/pictures_controller.rb +1 -1
  20. data/app/controllers/alchemy/admin/users_controller.rb +8 -2
  21. data/app/controllers/alchemy/attachments_controller.rb +1 -1
  22. data/app/controllers/alchemy/base_controller.rb +8 -0
  23. data/app/controllers/alchemy/pages_controller.rb +4 -0
  24. data/app/controllers/alchemy/passwords_controller.rb +23 -0
  25. data/app/controllers/alchemy/user_sessions_controller.rb +20 -49
  26. data/app/controllers/alchemy/users_controller.rb +49 -0
  27. data/app/mailers/alchemy/notifications.rb +5 -0
  28. data/app/models/alchemy/content.rb +6 -2
  29. data/app/models/alchemy/element.rb +9 -5
  30. data/app/models/alchemy/essence_richtext.rb +28 -16
  31. data/app/models/alchemy/essence_text.rb +22 -13
  32. data/app/models/alchemy/message.rb +1 -1
  33. data/app/models/alchemy/page.rb +7 -2
  34. data/app/models/alchemy/user.rb +66 -24
  35. data/app/views/alchemy/admin/dashboard/index.html.erb +1 -1
  36. data/app/views/alchemy/admin/essence_files/edit.html.erb +1 -0
  37. data/app/views/alchemy/admin/pages/_create_language_form.html.erb +8 -1
  38. data/app/views/alchemy/admin/partials/_flash.html.erb +1 -1
  39. data/app/views/alchemy/admin/users/_table.html.erb +2 -2
  40. data/app/views/alchemy/admin/users/_user.html.erb +1 -1
  41. data/app/views/alchemy/admin/users/index.html.erb +1 -1
  42. data/app/views/alchemy/notifications/admin_user_created.de.text.erb +11 -0
  43. data/app/views/alchemy/notifications/admin_user_created.en.text.erb +11 -0
  44. data/app/views/alchemy/notifications/registered_user_created.text.erb +2 -1
  45. data/app/views/alchemy/notifications/reset_password_instructions.de.text.erb +8 -0
  46. data/app/views/alchemy/notifications/reset_password_instructions.en.text.erb +8 -0
  47. data/app/views/alchemy/passwords/edit.html.erb +35 -0
  48. data/app/views/alchemy/passwords/new.html.erb +30 -0
  49. data/app/views/alchemy/user_sessions/leave.html.erb +3 -3
  50. data/app/views/alchemy/user_sessions/{login.html.erb → new.html.erb} +5 -2
  51. data/app/views/alchemy/{user_sessions/signup.html.erb → users/new.html.erb} +0 -0
  52. data/config/alchemy/config.yml +12 -0
  53. data/config/initializers/devise.rb +242 -0
  54. data/config/locales/alchemy.de.yml +12 -39
  55. data/config/locales/alchemy.en.yml +4 -31
  56. data/config/locales/devise.de.yml +57 -0
  57. data/config/locales/devise.en.yml +60 -0
  58. data/config/routes.rb +37 -21
  59. data/db/migrate/20130121092645_migrate_to_devise.rb +24 -0
  60. data/lib/alchemy/authentication_helpers.rb +0 -13
  61. data/lib/alchemy/engine.rb +2 -2
  62. data/lib/alchemy/essence.rb +2 -2
  63. data/lib/alchemy/upgrader.rb +33 -0
  64. data/lib/alchemy/version.rb +1 -1
  65. data/lib/alchemy_cms.rb +2 -1
  66. data/lib/rails/generators/alchemy/deploy_script/templates/deploy.rb.tt +1 -1
  67. data/lib/rails/generators/alchemy/devise/devise_generator.rb +24 -0
  68. data/lib/rails/generators/alchemy/scaffold/files/elements.yml +1 -111
  69. data/lib/rails/generators/alchemy/scaffold/templates/page_layouts.yml.tt +1 -27
  70. data/lib/tasks/ferret.rake +6 -6
  71. data/spec/controllers/admin/clipboard_controller_spec.rb +2 -3
  72. data/spec/controllers/admin/contents_controller_spec.rb +1 -2
  73. data/spec/controllers/admin/elements_controller_spec.rb +1 -2
  74. data/spec/controllers/admin/languages_controller_spec.rb +2 -3
  75. data/spec/controllers/admin/pages_controller_spec.rb +2 -3
  76. data/spec/controllers/admin/trash_controller_spec.rb +1 -2
  77. data/spec/controllers/admin/users_controller_spec.rb +36 -5
  78. data/spec/controllers/attachments_controller_spec.rb +2 -4
  79. data/spec/controllers/base_controller_spec.rb +25 -0
  80. data/spec/controllers/elements_controller_spec.rb +1 -2
  81. data/spec/controllers/passwords_controller_spec.rb +16 -0
  82. data/spec/controllers/pictures_controller_spec.rb +1 -2
  83. data/spec/controllers/user_sessions_controller_spec.rb +21 -0
  84. data/spec/controllers/users_controller_spec.rb +67 -0
  85. data/spec/dummy/db/migrate/20130121092645_migrate_to_devise.rb +24 -0
  86. data/spec/dummy/db/schema.rb +17 -16
  87. data/spec/factories.rb +2 -0
  88. data/spec/integration/pages_controller_spec.rb +9 -1
  89. data/spec/models/content_spec.rb +11 -0
  90. data/spec/models/element_spec.rb +11 -2
  91. data/spec/models/essence_richtext_spec.rb +42 -6
  92. data/spec/models/essence_text_spec.rb +41 -0
  93. data/spec/models/page_spec.rb +39 -0
  94. data/spec/models/user_spec.rb +95 -6
  95. data/spec/spec_helper.rb +2 -3
  96. data/spec/support/alchemy/controller_hacks.rb +1 -1
  97. data/spec/support/alchemy/specs_helpers.rb +4 -4
  98. metadata +47 -10
  99. data/app/models/alchemy/user_session.rb +0 -14
  100. data/app/views/alchemy/notifications/admin_user_created.text.erb +0 -13
  101. data/app/views/alchemy/user_sessions/logout.html.erb +0 -3
@@ -30,7 +30,8 @@ module Alchemy
30
30
 
31
31
  # Displays an error notice in the Alchemy backend.
32
32
  def show_error_notice(e)
33
- @notice = "Error: #{e.message[0..99]}"
33
+ # truncate the message, because very long error messages (i.e from mysql2) causes cookie oveflow errors
34
+ @notice = "Error: #{e.message[0..255]}"
34
35
  @trace = e.backtrace
35
36
  if request.xhr?
36
37
  render :action => "error_notice", :layout => false
@@ -127,7 +128,7 @@ module Alchemy
127
128
  def per_page_value_for_screen_size
128
129
  return 25 if session[:screen_size].blank?
129
130
  screen_height = session[:screen_size].split('x').last.to_i
130
- (screen_height / 30) - 12
131
+ (screen_height / 30) - 10
131
132
  end
132
133
 
133
134
  end
@@ -8,7 +8,7 @@ module Alchemy
8
8
  @last_edited_pages = Page.all_last_edited_from(current_user)
9
9
  @locked_pages = Page.all_locked
10
10
  @online_users = User.logged_in.to_a - [current_user]
11
- @first_time = current_user.login_count == 1 && current_user.last_login_at.nil?
11
+ @first_time = current_user.sign_in_count == 1 && current_user.last_sign_in_at.nil?
12
12
  end
13
13
 
14
14
  end
@@ -157,7 +157,7 @@ module Alchemy
157
157
  when 'large'
158
158
  per_page = in_overlay? ? 4 : (per_page_value_for_screen_size / 1.7).floor + 1
159
159
  else
160
- per_page = in_overlay? ? 9 : (per_page_value_for_screen_size / 1.0).ceil + 1
160
+ per_page = in_overlay? ? 9 : (per_page_value_for_screen_size / 1.0).ceil + 4
161
161
  end
162
162
  return per_page
163
163
  end
@@ -50,8 +50,14 @@ module Alchemy
50
50
  def update
51
51
  # User is fetched via before filter
52
52
  params[:user].delete(:role) unless permitted_to?(:update_role)
53
- @user.update_attributes(params[:user])
54
- Notifications.admin_user_created(@user).deliver if params[:send_credentials]
53
+ if params[:user][:password].present?
54
+ @user.update_attributes(params[:user])
55
+ else
56
+ @user.update_without_password(params[:user])
57
+ end
58
+ if params[:send_credentials]
59
+ Notifications.admin_user_created(@user).deliver
60
+ end
55
61
  render_errors_or_redirect(
56
62
  @user,
57
63
  admin_users_path,
@@ -1,5 +1,5 @@
1
1
  module Alchemy
2
- class AttachmentsController < Alchemy::BaseController
2
+ class AttachmentsController < BaseController
3
3
 
4
4
  filter_access_to [:show, :download], :attribute_check => true, :model => Alchemy::Attachment, :load_method => :load_attachment
5
5
 
@@ -9,6 +9,7 @@ module Alchemy
9
9
  before_filter :set_current_site
10
10
  before_filter :set_language
11
11
  before_filter :mailer_set_url_options
12
+ before_filter :store_user_request_time
12
13
 
13
14
  helper_method :current_server, :current_site
14
15
 
@@ -187,6 +188,13 @@ module Alchemy
187
188
  redirect_to url_for(protocol: 'https')
188
189
  end
189
190
 
191
+ # Stores the users request time.
192
+ def store_user_request_time
193
+ if user_signed_in?
194
+ current_user.store_request_time!
195
+ end
196
+ end
197
+
190
198
  protected
191
199
 
192
200
  def permission_denied
@@ -76,6 +76,10 @@ module Alchemy
76
76
  # currently active language.
77
77
  Page.language_root_for(@language.id)
78
78
  end
79
+ rescue ArgumentError => e
80
+ # If encoding errors raises (ie. because of invalid byte chars in params),
81
+ # we render a 404 page
82
+ raise ActionController::RoutingError.new(e.message)
79
83
  end
80
84
 
81
85
  def enforce_primary_host_for_site
@@ -0,0 +1,23 @@
1
+ module Alchemy
2
+ class PasswordsController < Devise::PasswordsController
3
+
4
+ before_filter { enforce_ssl if ssl_required? && !request.ssl? }
5
+ before_filter :set_translation
6
+
7
+ layout 'alchemy/admin'
8
+
9
+ helper 'Alchemy::Admin::Base'
10
+
11
+ private
12
+
13
+ # Override for Devise method
14
+ def new_session_path(resource_name)
15
+ alchemy.login_path
16
+ end
17
+
18
+ def edit_password_url(resource, options={})
19
+ alchemy.edit_password_url(options)
20
+ end
21
+
22
+ end
23
+ end
@@ -1,56 +1,31 @@
1
1
  module Alchemy
2
- class UserSessionsController < Alchemy::BaseController
2
+ class UserSessionsController < Devise::SessionsController
3
3
 
4
4
  before_filter { enforce_ssl if ssl_required? && !request.ssl? }
5
5
  before_filter :set_translation
6
- before_filter :check_user_count, :only => :login
6
+ before_filter :check_user_count, :only => :new
7
7
 
8
8
  layout 'alchemy/admin'
9
9
 
10
10
  helper 'Alchemy::Admin::Base'
11
11
 
12
- # Signup only works if no user is present in database.
13
- def signup
14
- @user_genders = User.genders_for_select
15
- if request.get?
16
- redirect_to admin_dashboard_path if User.count != 0
17
- @user = User.new({:role => 'admin'})
18
- else
19
- @user = User.new(params[:user])
20
- if @user.save
21
- if params[:send_credentials]
22
- Notifications.admin_user_created(@user).deliver
23
- end
24
- flash[:notice] = _t('Successfully signup admin user')
25
- redirect_to admin_dashboard_path
26
- end
27
- end
28
- rescue Errno::ECONNREFUSED => e
29
- flash[:error] = _t(:signup_mail_delivery_error)
30
- redirect_to admin_dashboard_path
12
+ def new
13
+ super
31
14
  end
32
15
 
33
- def login
34
- if current_user
35
- redirect_to admin_dashboard_path, :notice => _t('You are already logged in')
36
- else
37
- if request.get?
38
- @user_session = UserSession.new()
39
- flash.now[:info] = params[:message] || _t("welcome_please_identify_notice")
16
+ def create
17
+ authenticate_user!
18
+ if user_signed_in?
19
+ store_screen_size
20
+ if session[:redirect_path].blank?
21
+ redirect_path = admin_dashboard_path
40
22
  else
41
- @user_session = UserSession.new(params[:alchemy_user_session])
42
- store_screen_size
43
- if @user_session.save
44
- if session[:redirect_path].blank?
45
- redirect_to admin_dashboard_path
46
- else
47
- # We have to strip double slashes from beginning of path, because of strange rails/rack bug.
48
- redirect_to session[:redirect_path].gsub(/^\/{2,}/, '/')
49
- end
50
- else
51
- render
52
- end
23
+ # We have to strip double slashes from beginning of path, because of strange rails/rack bug.
24
+ redirect_path = session[:redirect_path].gsub(/^\/{2,}/, '/')
53
25
  end
26
+ redirect_to redirect_path, :notice => t(:signed_in, :scope => 'devise.sessions')
27
+ else
28
+ super
54
29
  end
55
30
  end
56
31
 
@@ -58,21 +33,17 @@ module Alchemy
58
33
  render :layout => false
59
34
  end
60
35
 
61
- def logout
62
- message = params[:message] || _t("logged_out")
63
- @user_session = UserSession.find
64
- if @user_session
65
- @user_session.destroy
66
- end
67
- flash[:info] = message
68
- redirect_to root_url
36
+ def destroy
37
+ cookies.clear
38
+ session.clear
39
+ super
69
40
  end
70
41
 
71
42
  private
72
43
 
73
44
  def check_user_count
74
45
  if User.count == 0
75
- redirect_to :action => 'signup'
46
+ redirect_to signup_path
76
47
  else
77
48
  return true
78
49
  end
@@ -0,0 +1,49 @@
1
+ module Alchemy
2
+ class UsersController < BaseController
3
+
4
+ before_filter { enforce_ssl if ssl_required? && !request.ssl? }
5
+ before_filter :set_translation
6
+ before_filter :check_user_count
7
+ before_filter :load_genders
8
+
9
+ layout 'alchemy/admin'
10
+
11
+ helper 'Alchemy::Admin::Base'
12
+
13
+ def new
14
+ @signup = true
15
+ @user = User.new(:role => 'admin')
16
+ end
17
+
18
+ def create
19
+ @user = User.new(params[:user])
20
+ if @user.save
21
+ if params[:send_credentials]
22
+ Notifications.admin_user_created(@user).deliver
23
+ end
24
+ flash[:notice] = _t('Successfully signup admin user')
25
+ sign_in :user, @user
26
+ redirect_to admin_dashboard_path
27
+ else
28
+ @signup = true
29
+ render :new
30
+ end
31
+ rescue Errno::ECONNREFUSED => e
32
+ flash[:error] = _t(:signup_mail_delivery_error)
33
+ redirect_to admin_dashboard_path
34
+ end
35
+
36
+ private
37
+
38
+ def load_genders
39
+ @user_genders = User.genders_for_select
40
+ end
41
+
42
+ def check_user_count
43
+ if User.count > 0
44
+ redirect_to admin_dashboard_path
45
+ end
46
+ end
47
+
48
+ end
49
+ end
@@ -21,5 +21,10 @@ module Alchemy
21
21
  )
22
22
  end
23
23
 
24
+ def reset_password_instructions(user, opts={})
25
+ @user = user
26
+ mail :to => user.email, :subject => Alchemy::I18n.t("Reset password instructions")
27
+ end
28
+
24
29
  end
25
30
  end
@@ -166,8 +166,12 @@ module Alchemy
166
166
  essence.ingredient = value
167
167
  end
168
168
 
169
- # Calls essence.update_attributes. Called from +Alchemy::Element#save_contents+
170
- # Ads errors to self.base if essence validation fails.
169
+ # Calls essence.update_attributes.
170
+ #
171
+ # Called from +Alchemy::Element#save_contents+
172
+ #
173
+ # Adds errors to self.base if essence validation fails.
174
+ #
171
175
  def update_essence(params={})
172
176
  raise EssenceMissingError if essence.nil?
173
177
  if essence.update_attributes(params)
@@ -44,6 +44,10 @@ module Alchemy
44
44
  scope :excluded, lambda { |names| where(arel_table[:name].not_in(names)) }
45
45
  scope :not_in_cell, where(:cell_id => nil)
46
46
  scope :in_cell, where("#{self.table_name}.cell_id IS NOT NULL")
47
+ # Scope for only the elements from Alchemy::Site.current
48
+ scope :from_current_site, lambda { where(:alchemy_languages => {site_id: Site.current}).joins(:page => :language) }
49
+ # TODO: add this as default_scope
50
+ #default_scope { from_current_site }
47
51
 
48
52
  # class methods
49
53
  class << self
@@ -77,7 +81,7 @@ module Alchemy
77
81
  #
78
82
  def descriptions
79
83
  if ::File.exists? "#{::Rails.root}/config/alchemy/elements.yml"
80
- ::YAML.load_file("#{::Rails.root}/config/alchemy/elements.yml")
84
+ ::YAML.load_file("#{::Rails.root}/config/alchemy/elements.yml") || []
81
85
  else
82
86
  raise LoadError, "Could not find elements.yml file! Please run: rails generate alchemy:scaffold"
83
87
  end
@@ -198,16 +202,16 @@ module Alchemy
198
202
  # Pass an element name to get next of this kind.
199
203
  def next(name = nil)
200
204
  elements = page.elements.published.where(Element.arel_table[:position].gt(position))
201
- elements = elements.where(:name => name) if name.present?
202
- elements.order("position ASC").limit(1).first
205
+ elements = elements.named(name) if name.present?
206
+ elements.reorder("position ASC").limit(1).first
203
207
  end
204
208
 
205
209
  # Returns previous public element from same page.
206
210
  # Pass an element name to get previous of this kind.
207
211
  def prev(name = nil)
208
212
  elements = page.elements.published.where(Element.arel_table[:position].lt(position))
209
- elements = elements.where(:name => name) if name.present?
210
- elements.order("position DESC").limit(1).first
213
+ elements = elements.named(name) if name.present?
214
+ elements.reorder("position DESC").limit(1).first
211
215
  end
212
216
 
213
217
  # Stores the page into `to_be_sweeped_pages` (Pages that have to be sweeped after updating element).
@@ -7,20 +7,38 @@ module Alchemy
7
7
 
8
8
  attr_accessible :do_not_index, :body, :public, :stripped_body
9
9
 
10
- # Require acts_as_ferret only if Ferret full text search is enabled (default).
10
+ before_save :strip_content
11
+
12
+
13
+ # Enable Ferret indexing.
14
+ #
15
+ # But only, if Ferret full text search is enabled (default).
16
+ #
11
17
  # You can disable it in +config/alchemy/config.yml+
18
+ #
12
19
  if Config.get(:ferret) == true
13
20
  require 'acts_as_ferret'
14
- acts_as_ferret(
15
- :fields => {
16
- :stripped_body => {:store => :yes}
17
- },
18
- :remote => false
19
- )
20
- before_save :check_ferret_indexing
21
- end
21
+ acts_as_ferret(:fields => { :stripped_body => {:store => :yes} }, :remote => false)
22
22
 
23
- before_save :strip_content
23
+ # Ensures that the current setting for do_not_index gets updated in the db.
24
+ before_save { write_attribute(:do_not_index, description['do_not_index'] || false); return true }
25
+
26
+ # Disables the ferret indexing, if do_not_index attribute is set to true
27
+ #
28
+ # You can disable indexing in the elements.yml file.
29
+ #
30
+ # === Example
31
+ #
32
+ # name: secrets
33
+ # contents:
34
+ # - name: confidential
35
+ # type: EssenceRichtext
36
+ # do_not_index: true
37
+ #
38
+ def ferret_enabled?(is_bulk_index = false)
39
+ !do_not_index?
40
+ end
41
+ end
24
42
 
25
43
  private
26
44
 
@@ -28,12 +46,6 @@ module Alchemy
28
46
  self.stripped_body = strip_tags(self.body)
29
47
  end
30
48
 
31
- def check_ferret_indexing
32
- if self.do_not_index
33
- self.disable_ferret(:always)
34
- end
35
- end
36
-
37
49
  # Stripping HTML Tags and only returns plain text.
38
50
  def strip_tags(html)
39
51
  return html if html.blank?
@@ -13,24 +13,33 @@ module Alchemy
13
13
  :link_target
14
14
  )
15
15
 
16
- # Require acts_as_ferret only if Ferret full text search is enabled (default).
16
+ # Enable Ferret indexing.
17
+ #
18
+ # But only, if Ferret full text search is enabled (default).
19
+ #
17
20
  # You can disable it in +config/alchemy/config.yml+
21
+ #
18
22
  if Config.get(:ferret) == true
19
23
  require 'acts_as_ferret'
20
- acts_as_ferret(
21
- :fields => {
22
- :body => {:store => :yes}
23
- },
24
- :remote => false
25
- )
26
- before_save :check_ferret_indexing
27
- end
24
+ acts_as_ferret(:fields => { :body => {:store => :yes} }, :remote => false)
28
25
 
29
- private
26
+ # Ensures that the current setting for do_not_index gets updated in the db.
27
+ before_save { write_attribute(:do_not_index, description['do_not_index'] || false); return true }
30
28
 
31
- def check_ferret_indexing
32
- if self.do_not_index
33
- self.disable_ferret(:always)
29
+ # Disables the ferret indexing, if do_not_index attribute is set to true
30
+ #
31
+ # You can disable indexing in the elements.yml file.
32
+ #
33
+ # === Example
34
+ #
35
+ # name: contact_form
36
+ # contents:
37
+ # - name: email
38
+ # type: EssenceText
39
+ # do_not_index: true
40
+ #
41
+ def ferret_enabled?(is_bulk_index = false)
42
+ !do_not_index?
34
43
  end
35
44
  end
36
45