alchemy-devise 1.1.0

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 (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