alchemy-devise 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +7 -0
  2. data/Rakefile +39 -0
  3. data/app/assets/stylesheets/alchemy/custom.scss +2 -0
  4. data/app/assets/stylesheets/alchemy/login.scss +55 -0
  5. data/app/assets/stylesheets/alchemy/users.scss +44 -0
  6. data/app/controllers/alchemy/admin/users_controller.rb +81 -0
  7. data/app/controllers/alchemy/base_controller_extension.rb +12 -0
  8. data/app/controllers/alchemy/passwords_controller.rb +31 -0
  9. data/app/controllers/alchemy/user_sessions_controller.rb +68 -0
  10. data/app/controllers/alchemy/users_controller.rb +46 -0
  11. data/app/mailers/alchemy/notifications.rb +33 -0
  12. data/app/models/alchemy/user.rb +178 -0
  13. data/app/views/alchemy/admin/users/_table.html.erb +69 -0
  14. data/app/views/alchemy/admin/users/_user.html.erb +39 -0
  15. data/app/views/alchemy/admin/users/edit.html.erb +6 -0
  16. data/app/views/alchemy/admin/users/index.html.erb +58 -0
  17. data/app/views/alchemy/admin/users/new.html.erb +6 -0
  18. data/app/views/alchemy/notifications/alchemy_user_created.de.text.erb +15 -0
  19. data/app/views/alchemy/notifications/alchemy_user_created.en.text.erb +15 -0
  20. data/app/views/alchemy/notifications/registered_user_created.de.text.erb +13 -0
  21. data/app/views/alchemy/notifications/registered_user_created.en.text.erb +13 -0
  22. data/app/views/alchemy/notifications/reset_password_instructions.de.text.erb +8 -0
  23. data/app/views/alchemy/notifications/reset_password_instructions.en.text.erb +8 -0
  24. data/app/views/alchemy/passwords/edit.html.erb +35 -0
  25. data/app/views/alchemy/passwords/new.html.erb +30 -0
  26. data/app/views/alchemy/user_sessions/new.html.erb +48 -0
  27. data/app/views/alchemy/users/new.html.erb +14 -0
  28. data/config/authorization_rules.rb +30 -0
  29. data/config/initializers/alchemy.rb +19 -0
  30. data/config/initializers/devise.rb +242 -0
  31. data/config/locales/alchemy.de.yml +44 -0
  32. data/config/locales/alchemy.en.yml +41 -0
  33. data/config/locales/devise.de.yml +58 -0
  34. data/config/locales/devise.en.yml +60 -0
  35. data/config/routes.rb +26 -0
  36. data/config/spring.rb +1 -0
  37. data/db/migrate/20131015124700_create_alchemy_users.rb +33 -0
  38. data/db/migrate/20131225232042_add_alchemy_roles_to_alchemy_users.rb +19 -0
  39. data/lib/alchemy/devise.rb +6 -0
  40. data/lib/alchemy/devise/engine.rb +20 -0
  41. data/lib/alchemy/devise/version.rb +5 -0
  42. metadata +153 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 2f664a74c83a46d4bdf13818b908920cf7c2cce4
4
+ data.tar.gz: f8ac7952a6e5b557e3e0eb3b347e7e6034bad0d5
5
+ SHA512:
6
+ metadata.gz: f471962c57a4a487a68a64c51aa34c69c12e36c15a04ff524688cdc029da868ed346f2e4253b8d386e2a556a01a03fb06929a1980dad252746afe1815118d0a3
7
+ data.tar.gz: 0171f59a9621bc95d5bf746527f5d2e541aac892cfd9bc95f061d6c218c9e45b7ed99b8bad3cc9b7c112807491bc122558d910680e59474d652c3ae7d3ac852c
@@ -0,0 +1,39 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'rdoc/task'
8
+
9
+ RDoc::Task.new(:rdoc) do |rdoc|
10
+ rdoc.rdoc_dir = 'rdoc'
11
+ rdoc.title = 'AlchemyDevise'
12
+ rdoc.options << '--line-numbers'
13
+ rdoc.rdoc_files.include('README.md')
14
+ rdoc.rdoc_files.include('lib/**/*.rb')
15
+ rdoc.rdoc_files.include('app/**/*.rb')
16
+ end
17
+
18
+ APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__)
19
+ load 'rails/tasks/engine.rake'
20
+
21
+ require 'rspec/core'
22
+ require 'rspec/core/rake_task'
23
+
24
+ RSpec::Core::RakeTask.new(:spec)
25
+
26
+ task :default => ['alchemy:spec:prepare', :spec]
27
+
28
+ Bundler::GemHelper.install_tasks
29
+
30
+ namespace :alchemy do
31
+ namespace :spec do
32
+
33
+ desc "Prepares database for testing Alchemy"
34
+ task :prepare do
35
+ system 'cd spec/dummy && RAILS_ENV=test bundle exec rake db:drop db:create db:migrate:reset alchemy:db:seed && cd -'
36
+ end
37
+
38
+ end
39
+ end
@@ -0,0 +1,2 @@
1
+ @import 'alchemy/users';
2
+ @import 'alchemy/login';
@@ -0,0 +1,55 @@
1
+ div#alchemy_greeting {
2
+ width: 370px;
3
+ margin-right: auto;
4
+ margin-left: auto;
5
+ padding: 2*$default-padding;
6
+ margin-top: -4em;
7
+ text-align: center;
8
+
9
+ p {
10
+ margin-top: 1em;
11
+ margin-bottom: 1em;
12
+ }
13
+
14
+ h1 {
15
+ margin-top: 1em;
16
+ margin-bottom: 0;
17
+ font-size: 1.2em;
18
+ }
19
+ }
20
+
21
+ div.login_signup_box {
22
+ width: 390px;
23
+ padding: 4*$default-padding;
24
+ border: $default-border;
25
+ background-color: $medium-gray;
26
+ @extend .rounded-border;
27
+ margin: 2em auto;
28
+
29
+ input[type="text"], input.thin_border { width: 250px }
30
+ }
31
+
32
+ html.no-js {
33
+ div.login_signup_box, div#alchemy_greeting h1 {
34
+ display: none;
35
+ }
36
+ }
37
+
38
+ #login_box {
39
+ height: 310px;
40
+ width: 394px;
41
+ margin-top: -200px;
42
+ margin-left: -197px;
43
+ position: absolute;
44
+ top: 50%;
45
+ left: 50%;
46
+ }
47
+
48
+ #login table tbody tr td.label {
49
+ width: 155px;
50
+ text-align: right;
51
+ }
52
+
53
+ form#login.new_user_session {
54
+ text-align: right;
55
+ }
@@ -0,0 +1,44 @@
1
+ td#user_roles {
2
+ padding: 9px 0;
3
+
4
+ label {
5
+ @include inline-block;
6
+ white-space: nowrap;
7
+ min-width: 40%;
8
+ }
9
+ }
10
+
11
+ .icon {
12
+
13
+ &.user {
14
+ background-position: 0 -104px;
15
+
16
+ &.female {
17
+ background-position: -128px -104px;
18
+ }
19
+ }
20
+
21
+ &.user_add {
22
+ background-position: -96px -104px;
23
+ }
24
+
25
+ &.user_delete {
26
+ background-position: -32px -104px;
27
+
28
+ &.female {
29
+ background-position: -160px -104px;
30
+ }
31
+ }
32
+
33
+ &.user_edit {
34
+ background-position: -64px -104px;
35
+
36
+ &.female {
37
+ background-position: -192px -104px;
38
+ }
39
+ }
40
+ }
41
+
42
+ span.module.users {
43
+ background-position: -80px 0;
44
+ }
@@ -0,0 +1,81 @@
1
+ module Alchemy
2
+ module Admin
3
+ class UsersController < ResourcesController
4
+
5
+ filter_access_to [:edit, :update, :destroy], :attribute_check => true, :load_method => :load_user, :model => Alchemy::User
6
+ filter_access_to [:index, :new, :create], :attribute_check => false
7
+
8
+ before_filter :set_roles_and_genders, :except => [:index, :destroy]
9
+
10
+ handles_sortable_columns do |c|
11
+ c.default_sort_value = :login
12
+ end
13
+
14
+ def index
15
+ if !params[:query].blank?
16
+ users = User.where([
17
+ "login LIKE ? OR email LIKE ? OR firstname LIKE ? OR lastname LIKE ?",
18
+ "%#{params[:query]}%",
19
+ "%#{params[:query]}%",
20
+ "%#{params[:query]}%",
21
+ "%#{params[:query]}%"
22
+ ])
23
+ else
24
+ users = User.scoped
25
+ end
26
+ @users = users.page(params[:page] || 1).per(per_page_value_for_screen_size).order(sort_order)
27
+ end
28
+
29
+ def create
30
+ @user = User.create(params[:user])
31
+ render_errors_or_redirect(
32
+ @user,
33
+ admin_users_path,
34
+ _t("User created", :name => @user.name)
35
+ )
36
+ end
37
+
38
+ def update
39
+ # User is fetched via before filter
40
+ params[:user].delete(:alchemy_roles) unless permitted_to?(:update_roles)
41
+ if params[:user][:password].present?
42
+ @user.update_attributes(params[:user])
43
+ else
44
+ @user.update_without_password(params[:user])
45
+ end
46
+ render_errors_or_redirect(
47
+ @user,
48
+ admin_users_path,
49
+ _t("User updated", :name => @user.name)
50
+ )
51
+ end
52
+
53
+ def destroy
54
+ # User is fetched via before filter
55
+ name = @user.name
56
+ if @user.destroy
57
+ flash[:notice] = _t("User deleted", :name => name)
58
+ end
59
+ respond_to do |format|
60
+ format.html { redirect_to admin_users_path }
61
+ format.js do
62
+ @redirect_url = admin_users_path
63
+ render action: 'redirect'
64
+ end
65
+ end
66
+ end
67
+
68
+ private
69
+
70
+ def load_user
71
+ @user = User.find(params[:id])
72
+ end
73
+
74
+ def set_roles_and_genders
75
+ @user_roles = User::ROLES.map { |role| [User.human_rolename(role), role] }
76
+ @user_genders = User.genders_for_select
77
+ end
78
+
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,12 @@
1
+ Alchemy::BaseController.class_eval do
2
+ before_filter :store_user_request_time
3
+
4
+ private
5
+
6
+ # Stores the users request time.
7
+ def store_user_request_time
8
+ if alchemy_user_signed_in?
9
+ current_alchemy_user.store_request_time!
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,31 @@
1
+ module Alchemy
2
+ class PasswordsController < ::Devise::PasswordsController
3
+ include Ferret::Search
4
+ helper 'Alchemy::Admin::Base', 'Alchemy::Pages'
5
+
6
+ before_filter { enforce_ssl if ssl_required? && !request.ssl? }
7
+ before_filter :set_translation
8
+
9
+ layout 'alchemy/login'
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
+ def after_sign_in_path_for(resource_or_scope)
23
+ if permitted_to?(:index, :alchemy_admin_dashboard)
24
+ alchemy.admin_dashboard_path
25
+ else
26
+ alchemy.root_path
27
+ end
28
+ end
29
+
30
+ end
31
+ end
@@ -0,0 +1,68 @@
1
+ module Alchemy
2
+ class UserSessionsController < ::Devise::SessionsController
3
+ # Necessary because this controller is also used for general login.
4
+ include Ferret::Search
5
+ helper 'Alchemy::Admin::Base', 'Alchemy::Pages'
6
+
7
+ before_filter(except: 'destroy') { enforce_ssl if ssl_required? && !request.ssl? }
8
+ before_filter :set_translation
9
+ before_filter :check_user_count, :only => :new
10
+
11
+ layout 'alchemy/login'
12
+
13
+ def new
14
+ super
15
+ end
16
+
17
+ def create
18
+ authenticate_user!
19
+ if alchemy_user_signed_in?
20
+ store_screen_size
21
+ if session[:redirect_path].blank?
22
+ redirect_path = admin_dashboard_path
23
+ else
24
+ # We have to strip double slashes from beginning of path, because of strange rails/rack bug.
25
+ redirect_path = session[:redirect_path].gsub(/^\/{2,}/, '/')
26
+ end
27
+ redirect_to redirect_path, :notice => t(:signed_in, :scope => 'devise.sessions')
28
+ else
29
+ super
30
+ end
31
+ end
32
+
33
+ def leave
34
+ render layout: !request.xhr?
35
+ end
36
+
37
+ def destroy
38
+ current_alchemy_user.try(:unlock_pages!)
39
+ cookies.clear
40
+ session.clear
41
+ super
42
+ end
43
+
44
+ private
45
+
46
+ def check_user_count
47
+ if User.count == 0
48
+ redirect_to signup_path
49
+ else
50
+ return true
51
+ end
52
+ end
53
+
54
+ def store_screen_size
55
+ session[:screen_size] = params[:user_screensize]
56
+ end
57
+
58
+ # Ovewriting the default of Devise
59
+ def after_sign_out_path_for(resource_or_scope)
60
+ if request.referer.blank? || request.referer.to_s =~ /admin/
61
+ root_path
62
+ else
63
+ request.referer
64
+ end
65
+ end
66
+
67
+ end
68
+ end
@@ -0,0 +1,46 @@
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(:alchemy_roles => 'admin')
16
+ end
17
+
18
+ def create
19
+ @user = User.new(params[:user])
20
+ if @user.save
21
+ flash[:notice] = _t('Successfully signup admin user')
22
+ sign_in :user, @user
23
+ redirect_to admin_dashboard_path
24
+ else
25
+ @signup = true
26
+ render :new
27
+ end
28
+ rescue Errno::ECONNREFUSED => e
29
+ flash[:error] = _t(:signup_mail_delivery_error)
30
+ redirect_to admin_dashboard_path
31
+ end
32
+
33
+ private
34
+
35
+ def load_genders
36
+ @user_genders = User.genders_for_select
37
+ end
38
+
39
+ def check_user_count
40
+ if User.count > 0
41
+ redirect_to admin_dashboard_path
42
+ end
43
+ end
44
+
45
+ end
46
+ end
@@ -0,0 +1,33 @@
1
+ module Alchemy
2
+ class Notifications < ActionMailer::Base
3
+
4
+ default(from: Config.get(:mailer)['mail_from'])
5
+
6
+ def registered_user_created(user)
7
+ @user = user
8
+ @url = login_url
9
+ mail(
10
+ to: user.email,
11
+ subject: I18n.t("Your user credentials")
12
+ )
13
+ end
14
+
15
+ def alchemy_user_created(user)
16
+ @user = user
17
+ @url = admin_url
18
+ mail(
19
+ to: user.email,
20
+ subject: I18n.t("Your Alchemy Login")
21
+ )
22
+ end
23
+
24
+ def reset_password_instructions(user, opts={})
25
+ @user = user
26
+ mail(
27
+ to: user.email,
28
+ subject: I18n.t("Reset password instructions")
29
+ )
30
+ end
31
+
32
+ end
33
+ end
@@ -0,0 +1,178 @@
1
+ require 'userstamp'
2
+ require 'acts-as-taggable-on'
3
+
4
+ module Alchemy
5
+ class User < ActiveRecord::Base
6
+
7
+ DEVISE_MODULES = [
8
+ :database_authenticatable,
9
+ :trackable,
10
+ :validatable,
11
+ :timeoutable,
12
+ :recoverable
13
+ ]
14
+ # If the app uses an old encryption it uses the devise-encryptable gem
15
+ # therefore we have to load the devise module
16
+ if (::Devise::Models::Encryptable rescue false)
17
+ DEVISE_MODULES.push(:encryptable)
18
+ end
19
+ devise *DEVISE_MODULES
20
+
21
+ acts_as_taggable
22
+ acts_as_tagger
23
+
24
+ attr_accessible(
25
+ :alchemy_roles,
26
+ :firstname,
27
+ :lastname,
28
+ :login,
29
+ :email,
30
+ :gender,
31
+ :language,
32
+ :password,
33
+ :password_confirmation,
34
+ :send_credentials,
35
+ :tag_list
36
+ )
37
+
38
+ attr_accessor :send_credentials
39
+
40
+ has_many :folded_pages
41
+
42
+ validates_uniqueness_of :login
43
+ validates_presence_of :alchemy_roles
44
+
45
+ # Unlock all locked pages before destroy.
46
+ before_destroy :unlock_pages!
47
+
48
+ after_save :deliver_welcome_mail, if: -> { send_credentials == '1' }
49
+
50
+ scope :admins, -> { where(arel_table[:alchemy_roles].matches('%admin%')) }
51
+ scope :logged_in, -> { where('last_request_at > ?', logged_in_timeout.seconds.ago) }
52
+ scope :logged_out, -> { where('last_request_at is NULL or last_request_at <= ?', logged_in_timeout.seconds.ago) }
53
+
54
+ ROLES = Config.get(:user_roles)
55
+
56
+ class << self
57
+ def human_rolename(role)
58
+ I18n.t("user_roles.#{role}")
59
+ end
60
+
61
+ def genders_for_select
62
+ [
63
+ [I18n.t('male'), 'male'],
64
+ [I18n.t('female'), 'female']
65
+ ]
66
+ end
67
+
68
+ def logged_in_timeout
69
+ Config.get(:auto_logout_time).minutes.to_i
70
+ end
71
+ end
72
+
73
+ def role_symbols
74
+ alchemy_roles.map(&:to_sym)
75
+ end
76
+
77
+ def role
78
+ alchemy_roles.first
79
+ end
80
+
81
+ def alchemy_roles
82
+ read_attribute(:alchemy_roles).split(' ')
83
+ end
84
+
85
+ def alchemy_roles=(roles_string)
86
+ if roles_string.is_a? Array
87
+ write_attribute(:alchemy_roles, roles_string.join(' '))
88
+ elsif roles_string.is_a? String
89
+ write_attribute(:alchemy_roles, roles_string)
90
+ end
91
+ end
92
+
93
+ def add_role(role)
94
+ self.alchemy_roles = self.alchemy_roles.push(role.to_s).uniq
95
+ end
96
+
97
+ # Returns true if the user ahs admin role
98
+ def is_admin?
99
+ has_role? 'admin'
100
+ end
101
+ alias_method :admin?, :is_admin?
102
+
103
+ # Returns true if the user has the given role.
104
+ def has_role?(role)
105
+ alchemy_roles.include? role.to_s
106
+ end
107
+
108
+ # Calls unlock on all locked pages
109
+ def unlock_pages!
110
+ pages_locked_by_me.map(&:unlock!)
111
+ end
112
+
113
+ # Returns all pages locked by user.
114
+ #
115
+ # A page gets locked, if the user requests to edit the page.
116
+ #
117
+ def pages_locked_by_me
118
+ Page.where(:locked => true).where(:locked_by => self.id).order(:updated_at)
119
+ end
120
+ alias_method :locked_pages, :pages_locked_by_me
121
+
122
+ # Returns the firstname and lastname as a string
123
+ #
124
+ # If both are blank, returns the login
125
+ #
126
+ # @option options :flipped (false)
127
+ # Flip the firstname and lastname
128
+ #
129
+ def fullname(options = {})
130
+ if lastname.blank? && firstname.blank?
131
+ login
132
+ else
133
+ options = {:flipped => false}.merge(options)
134
+ fullname = options[:flipped] ? "#{lastname}, #{firstname}" : "#{firstname} #{lastname}"
135
+ fullname.squeeze(" ").strip
136
+ end
137
+ end
138
+ alias_method :name, :fullname
139
+
140
+ # Returns true if the last request not longer ago then the logged_in_time_out
141
+ def logged_in?
142
+ raise "Can not determine the records login state because there is no last_request_at column" if !respond_to?(:last_request_at)
143
+ !last_request_at.nil? && last_request_at > logged_in_timeout.seconds.ago
144
+ end
145
+
146
+ # Opposite of logged_in?
147
+ def logged_out?
148
+ !logged_in?
149
+ end
150
+
151
+ def human_roles_string
152
+ alchemy_roles.map do |role|
153
+ self.class.human_rolename(role)
154
+ end.to_sentence
155
+ end
156
+
157
+ def store_request_time!
158
+ update_column(:last_request_at, Time.now)
159
+ end
160
+
161
+ private
162
+
163
+ def logged_in_timeout
164
+ self.class.logged_in_timeout
165
+ end
166
+
167
+ # Delivers a welcome mail depending from user's role.
168
+ #
169
+ def deliver_welcome_mail
170
+ if has_role?('author') || has_role?('editor') || has_role?('admin')
171
+ Notifications.alchemy_user_created(self).deliver
172
+ else
173
+ Notifications.registered_user_created(self).deliver
174
+ end
175
+ end
176
+
177
+ end
178
+ end