muck-users 0.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 (113) hide show
  1. data/MIT-LICENSE +20 -0
  2. data/README.rdoc +27 -0
  3. data/Rakefile +96 -0
  4. data/VERSION +1 -0
  5. data/app/controllers/admin/muck/roles_controller.rb +57 -0
  6. data/app/controllers/admin/muck/users_controller.rb +122 -0
  7. data/app/controllers/muck/activations_controller.rb +31 -0
  8. data/app/controllers/muck/password_resets_controller.rb +81 -0
  9. data/app/controllers/muck/user_sessions_controller.rb +40 -0
  10. data/app/controllers/muck/username_request_controller.rb +43 -0
  11. data/app/controllers/muck/users_controller.rb +211 -0
  12. data/app/models/muck_user_mailer.rb +53 -0
  13. data/app/models/permission.rb +17 -0
  14. data/app/models/role.rb +25 -0
  15. data/app/views/admin/roles/_role.html.erb +9 -0
  16. data/app/views/admin/roles/edit.html.erb +17 -0
  17. data/app/views/admin/roles/index.html.erb +8 -0
  18. data/app/views/admin/roles/new.html.erb +16 -0
  19. data/app/views/admin/roles/show.html.erb +7 -0
  20. data/app/views/admin/users/_activate.html.erb +5 -0
  21. data/app/views/admin/users/_ajax_search_box.html.erb +6 -0
  22. data/app/views/admin/users/_row.html.erb +6 -0
  23. data/app/views/admin/users/_search_box.html.erb +6 -0
  24. data/app/views/admin/users/_table.html.erb +21 -0
  25. data/app/views/admin/users/_user_navigation.html.erb +11 -0
  26. data/app/views/admin/users/do_search.html.erb +5 -0
  27. data/app/views/admin/users/inactive.html.erb +8 -0
  28. data/app/views/admin/users/inactive_emails.html.erb +6 -0
  29. data/app/views/admin/users/index.html.erb +12 -0
  30. data/app/views/admin/users/search.html.erb +5 -0
  31. data/app/views/muck_user_mailer/activation_confirmation.html.erb +7 -0
  32. data/app/views/muck_user_mailer/activation_instructions.html.erb +7 -0
  33. data/app/views/muck_user_mailer/password_not_active_instructions.html.erb +10 -0
  34. data/app/views/muck_user_mailer/password_reset_instructions.html.erb +10 -0
  35. data/app/views/muck_user_mailer/username_request.html.erb +3 -0
  36. data/app/views/muck_user_mailer/welcome_notification.html.erb +5 -0
  37. data/app/views/password_resets/edit.html.erb +9 -0
  38. data/app/views/password_resets/new.html.erb +11 -0
  39. data/app/views/user_sessions/new.html.erb +17 -0
  40. data/app/views/username_request/new.html.erb +11 -0
  41. data/app/views/users/_user.html.erb +15 -0
  42. data/app/views/users/activation_confirmation.html.erb +1 -0
  43. data/app/views/users/activation_instructions.html.erb +1 -0
  44. data/app/views/users/edit.html.erb +45 -0
  45. data/app/views/users/new.html.erb +51 -0
  46. data/app/views/users/show.html.erb +4 -0
  47. data/app/views/users/welcome.html.erb +4 -0
  48. data/config/muck_users_routes.rb +56 -0
  49. data/db/migrate/20090320174818_create_muck_permissions_and_roles.rb +16 -0
  50. data/db/migrate/20090512013727_add_photo_to_user.rb +13 -0
  51. data/install.rb +1 -0
  52. data/lib/action_controller/authentic_application.rb +213 -0
  53. data/lib/active_record/acts/muck_user.rb +192 -0
  54. data/lib/muck-users/exceptions.rb +5 -0
  55. data/lib/muck-users/initialize_routes.rb +8 -0
  56. data/lib/muck-users/tasks.rb +46 -0
  57. data/lib/muck-users.rb +7 -0
  58. data/locales/ar.yml +124 -0
  59. data/locales/bg.yml +124 -0
  60. data/locales/ca.yml +124 -0
  61. data/locales/cs.yml +124 -0
  62. data/locales/da.yml +124 -0
  63. data/locales/de.yml +124 -0
  64. data/locales/el.yml +124 -0
  65. data/locales/en.yml +127 -0
  66. data/locales/es.yml +124 -0
  67. data/locales/fr.yml +124 -0
  68. data/locales/it.yml +124 -0
  69. data/locales/iw.yml +124 -0
  70. data/locales/ja.yml +124 -0
  71. data/locales/ko.yml +124 -0
  72. data/locales/lt.yml +124 -0
  73. data/locales/lv.yml +124 -0
  74. data/locales/nl.yml +124 -0
  75. data/locales/no.yml +125 -0
  76. data/locales/pl.yml +124 -0
  77. data/locales/pt.yml +124 -0
  78. data/locales/ro.yml +124 -0
  79. data/locales/ru.yml +124 -0
  80. data/locales/sk.yml +124 -0
  81. data/locales/sl.yml +124 -0
  82. data/locales/sr.yml +124 -0
  83. data/locales/sv.yml +124 -0
  84. data/locales/tl.yml +124 -0
  85. data/locales/uk.yml +124 -0
  86. data/locales/vi.yml +124 -0
  87. data/locales/zh-CN.yml +124 -0
  88. data/locales/zh-TW.yml +124 -0
  89. data/locales/zh.yml +124 -0
  90. data/muck-users.gemspec +170 -0
  91. data/pkg/muck-users-0.1.0.gem +0 -0
  92. data/public/images/profile_default.jpg +0 -0
  93. data/rails/init.rb +18 -0
  94. data/tasks/muck_users_engine.rake +27 -0
  95. data/tasks/rails.rake +2 -0
  96. data/test/factories.rb +56 -0
  97. data/test/functional/activations_controller_test.rb +73 -0
  98. data/test/functional/admin/roles_controller_test.rb +10 -0
  99. data/test/functional/admin/users_controller_test.rb +55 -0
  100. data/test/functional/password_resets_controller_test.rb +60 -0
  101. data/test/functional/user_sessions_controller_test.rb +62 -0
  102. data/test/functional/users_controller_test.rb +255 -0
  103. data/test/shoulda_macros/controller.rb +43 -0
  104. data/test/shoulda_macros/forms.rb +28 -0
  105. data/test/shoulda_macros/models.rb +34 -0
  106. data/test/shoulda_macros/pagination.rb +48 -0
  107. data/test/shoulda_macros/plugins.rb +30 -0
  108. data/test/test_helper.rb +36 -0
  109. data/test/unit/muck_user_mailer_test.rb +64 -0
  110. data/test/unit/permission_test.rb +19 -0
  111. data/test/unit/role_test.rb +17 -0
  112. data/uninstall.rb +1 -0
  113. metadata +198 -0
@@ -0,0 +1,45 @@
1
+ <div id="edit-user" class="common-form">
2
+ <h1><%= t('muck.users.update_user') %></h1>
3
+
4
+ <% custom_form_for @user, :url => user_path(@user), :html => {:id => "edit-user-form", :name => 'edit-user-form', :method => :put, :multipart => true } do |f| -%>
5
+
6
+ <%= output_errors(t('muck.users.problem_editing_account'), {:class => 'help-box'}, @user) %>
7
+
8
+ <%= f.text_field :email, { :label => t('muck.users.email_address'),
9
+ :tip => t('muck.users.email_help') } -%>
10
+ <%= f.text_field :first_name, { :label => t('muck.users.first_name') } %>
11
+ <%= f.text_field :last_name, { :label => t('muck.users.last_name') } %>
12
+ <%= image_tag @user.photo.url(:thumb) %>
13
+ <%= f.file_field :photo, { :label => t('muck.users.upload_photo') } %>
14
+ <div class="button form-row">
15
+ <%= f.submit t('muck.general.save') %>
16
+ </div>
17
+ <%= hidden_field_tag :redirect_to, edit_user_path(@user) %>
18
+ <% end %>
19
+
20
+ </div>
21
+
22
+ <div id="change-password" class="common-form">
23
+ <h2><%= t('muck.users.change_password') %></h2>
24
+ <% custom_form_for @user, :url => user_path(@user), :html => { :id => "change-password-form", :name => 'change-password-form', :method => :put } do |f| %>
25
+ <%= f.password_field :password, { :label => t('muck.users.password'),
26
+ :tip => t('muck.users.password_help')} -%>
27
+ <%= f.password_field :password_confirmation, { :label => t('muck.users.confirm_password'),
28
+ :tip => t('muck.users.password_confirmation_help') } -%>
29
+ <div class="button form-row">
30
+ <%= submit_tag t('muck.users.change_password'), :class => 'button' %>
31
+ </div>
32
+ <%= hidden_field_tag :redirect_to, edit_user_path(@user) %>
33
+ <% end -%>
34
+ </div>
35
+
36
+ <% if GlobalConfig.let_users_delete_their_account -%>
37
+ <div id="delete-account" class="common-form">
38
+ <% custom_form_for :user, :url => users_path, :html => {:id => "delete-user-form", :name => 'delete-user-form', :method => :delete, :confirm => t("users.confirm_delete_account")} do |f| -%>
39
+ <h2><%= t('muck.users.remove_my_account') %></h2>
40
+ <div class="button form-row">
41
+ <%= f.submit t('muck.general.delete_my_account') %>
42
+ </div>
43
+ <% end -%>
44
+ </div>
45
+ <% end -%>
@@ -0,0 +1,51 @@
1
+ <div id="registration" class="common-form">
2
+ <h1><%= t('muck.users.register_account', :application_name => GlobalConfig.application_name) %></h1>
3
+ <p><%= t('muck.users.already_registered') %> <a href="<%=login_url%>"><%= t('muck.users.sign_in_now') %></a></p>
4
+
5
+ <% custom_form_for :user, :url => users_path, :html => {:id => "register-user-form", :name => 'register-user-form'} do |f| -%>
6
+
7
+ <%= output_errors(t('muck.users.problem_creating_account'), {:class => 'help-box'}, @user) %>
8
+
9
+ <%= f.text_field :login, { :label => t('muck.users.choose_member_name'),
10
+ :extra_html => '<span id="username-availibility" class="availability"></span>',
11
+ :tip => t('muck.users.username_help'),
12
+ :required_label => t('muck.users.username') } -%>
13
+ <%= f.text_field :email, { :label => t('muck.users.email_address'),
14
+ :tip => t('muck.users.email_help'),
15
+ :extra_html => '<span id="email-availibility" class="availability"></span>' } -%>
16
+ <%= f.password_field :password, { :label => t('muck.users.password'),
17
+ :tip => t('muck.users.password_help')} -%>
18
+ <%= f.password_field :password_confirmation, { :label => t('muck.users.confirm_password'),
19
+ :tip => t('muck.users.password_confirmation_help') } -%>
20
+ <%= f.file_field :photo, { :label => t('muck.users.upload_photo') } %>
21
+ <% if GlobalConfig.use_recaptcha -%>
22
+ <div class="recaptcha">
23
+ <%= recaptcha_tags %>
24
+ </div>
25
+ <% end -%>
26
+
27
+ <div class="button form-row">
28
+ <%= f.submit t('muck.users.sign_up_now') %>
29
+ </div>
30
+
31
+ <% end %>
32
+ </div>
33
+
34
+ <% content_for :javascript do -%>
35
+ jQuery("#user_login").blur(function(){
36
+ jQuery.post("is_login_available",{ user_login:jQuery(this).val() } ,function(data){
37
+ jQuery("#username-availibility").html(data);
38
+ });
39
+ });
40
+ jQuery("#user_login").keydown(function() {
41
+ jQuery("#username-availibility").html('');
42
+ });
43
+ jQuery("#user_email").blur(function(){
44
+ jQuery.post("is_email_available",{ user_email:jQuery(this).val() } ,function(data){
45
+ jQuery("#email-availibility").html(data);
46
+ });
47
+ });
48
+ jQuery("#user_email").keydown(function() {
49
+ jQuery("#email-availibility").html('');
50
+ });
51
+ <% end -%>
@@ -0,0 +1,4 @@
1
+ <div id="user" class="common-content">
2
+ <h1><%= I18n.t('muck.users.my_dashboard') %></h1>
3
+ <%= h @user.login %>
4
+ </div>
@@ -0,0 +1,4 @@
1
+ <div id="welcome" class="common-content">
2
+ Welcome to muck. This system provides the basic components to help you build your website.
3
+ <%= link_to 'View Your Account', user_path(current_user) %>
4
+ </div>
@@ -0,0 +1,56 @@
1
+ ActionController::Routing::Routes.draw do |map|
2
+
3
+ # users
4
+ map.resources :users, :controller => 'muck/users',
5
+ :member => { :enable => :put, :welcome => :get, :activation_instructions => :get },
6
+ :collection => { :is_login_available => :post, :is_email_available => :post }
7
+
8
+ map.with_options(:controller => 'muck/users') do |users|
9
+ users.signup "/signup", :action => 'new'
10
+ users.signup_complete "/users/signup_complete/:id", :action => 'welcome'
11
+ users.activation_complete "/users/activation_complete/:id", :action => 'welcome'
12
+ users.signup_complete_activation_required '/signup_complete_activate/:id', :action => 'activation_instructions'
13
+ users.is_login_available '/is_login_available', :action => 'is_login_available'
14
+ users.is_email_available '/is_email_available', :action => 'is_email_available'
15
+ users.account "account", :action => 'show'
16
+ end
17
+
18
+ # activations
19
+ map.resources :activations, :controller => 'muck/activations'
20
+ map.with_options(:controller => 'muck/activations') do |activations|
21
+ activations.activate '/activate/:activation_code', :action => 'new'
22
+ end
23
+
24
+ # passwords
25
+ map.resource :password_resets, :controller => 'muck/password_resets'
26
+
27
+ map.with_options(:controller => 'muck/password_resets') do |password_resets|
28
+ password_resets.forgot_password "/forgot_password", :action => 'new'
29
+ password_resets.reset_password "/reset_password/:id", :action => 'edit'
30
+ end
31
+
32
+ # username
33
+ map.resource :username_request, :controller => 'muck/username_request'
34
+
35
+ map.with_options(:controller => 'muck/username_request') do |username_request|
36
+ username_request.forgot_username "/forgot_username", :action => 'new'
37
+ end
38
+
39
+ # sessions
40
+ map.resource :user_session, :controller => 'muck/user_sessions'
41
+ map.with_options(:controller => 'muck/user_sessions') do |user_sessions|
42
+ user_sessions.login "/login", :action => 'new'
43
+ user_sessions.logout "/logout", :action => 'destroy'
44
+ user_sessions.signup_complete_login_required '/signup_complete_login/:id', :action => 'new'
45
+ end
46
+
47
+ # admin
48
+ map.namespace :admin do |a|
49
+ a.resources :users, :controller => 'muck/users', :collection => { :inactive => :get, :inactive_emails => :get, :activate_all => :get, :search => :post, :ajax_search => :post }
50
+ a.resources :roles, :controller => 'muck/roles'
51
+ a.resources :permissions, :controller => 'muck/permissions'
52
+ end
53
+
54
+ # public (replace with something more useful in your application)
55
+ map.public_user '/profiles/:id', :controller => 'default', :action => 'index'
56
+ end
@@ -0,0 +1,16 @@
1
+ class CreateMuckPermissionsAndRoles < ActiveRecord::Migration
2
+
3
+ create_table "permissions", :force => true do |t|
4
+ t.integer "role_id", :null => false
5
+ t.integer "user_id", :null => false
6
+ t.datetime "created_at"
7
+ t.datetime "updated_at"
8
+ end
9
+
10
+ create_table "roles", :force => true do |t|
11
+ t.string "rolename"
12
+ t.datetime "created_at"
13
+ t.datetime "updated_at"
14
+ end
15
+
16
+ end
@@ -0,0 +1,13 @@
1
+ class AddPhotoToUser < ActiveRecord::Migration
2
+ def self.up
3
+ add_column :users, :photo_file_name, :string
4
+ add_column :users, :photo_content_type, :string
5
+ add_column :users, :photo_file_size, :integer
6
+ end
7
+
8
+ def self.down
9
+ remove_column :users, :photo_file_name
10
+ remove_column :users, :photo_content_type
11
+ remove_column :users, :photo_file_size
12
+ end
13
+ end
data/install.rb ADDED
@@ -0,0 +1 @@
1
+ # Install hook code here
@@ -0,0 +1,213 @@
1
+ module ActionController
2
+
3
+ module AuthenticApplication
4
+
5
+ # Module automatically mixed into the all controllers
6
+ def self.included(base)
7
+ base.class_eval do
8
+ include InstanceMethods
9
+ end
10
+ # Inclusion hook to make #current_user and #logged_in?, etc available as ActionView helper methods.
11
+ base.send :helper_method, :current_user_session, :current_user, :logged_in?, :admin?, :is_me?, :is_owner?
12
+ base.send :filter_parameter_logging, :password, :password_confirmation
13
+ end
14
+
15
+ module InstanceMethods
16
+
17
+ protected
18
+
19
+ # Returns true or false if the user is logged in.
20
+ def logged_in?
21
+ !current_user.blank?
22
+ end
23
+
24
+ def current_user_session
25
+ return @current_user_session if defined?(@current_user_session)
26
+ @current_user_session = UserSession.find
27
+ end
28
+
29
+ def current_user
30
+ return @current_user if defined?(@current_user)
31
+ @current_user = current_user_session && current_user_session.record
32
+ end
33
+
34
+ def login_required
35
+ unless logged_in?
36
+ store_location
37
+ flash[:notice] = I18n.t('muck.users.login_requred')
38
+ access_denied
39
+ end
40
+ end
41
+
42
+ def not_logged_in_required
43
+ if logged_in?
44
+ store_location
45
+ flash[:notice] = t('muck.users.logout_required')
46
+ enforce_logout_required
47
+ end
48
+ end
49
+
50
+ def check_role(role)
51
+ unless logged_in? && current_user.has_role?(role)
52
+ if logged_in?
53
+ permission_denied
54
+ else
55
+ store_referer
56
+ access_denied
57
+ end
58
+ end
59
+ end
60
+
61
+ def admin?
62
+ logged_in? && current_user.admin?
63
+ end
64
+
65
+ # check to see if the current user is the owner of the specified object
66
+ def is_owner?(obj)
67
+ obj.user_id == current_user.id
68
+ end
69
+
70
+ def is_owner?(user, user_id)
71
+ user.id == user_id
72
+ end
73
+
74
+ # check to see if the given user is the same as the current user
75
+ def is_me?(user)
76
+ user == current_user
77
+ end
78
+
79
+ # checks permissions on an object. Redirects if the current user
80
+ # doesn't own it or have admin rights
81
+ def protect_owner(obj)
82
+ if is_owner?(obj) || admin?
83
+ true
84
+ else
85
+ permission_denied
86
+ false
87
+ end
88
+ end
89
+
90
+ # allow or deny access depending on options specified
91
+ def allowed_access?(options)
92
+ if !options[:owner].nil? && !options[:object_user_id].nil?
93
+ return true if is_owner?(options[:owner], options[:object_user_id])
94
+ end
95
+
96
+ options[:permit_roles].each do |role|
97
+ return true if current_user.has_role?(role)
98
+ end
99
+
100
+ # access denied
101
+ permission_denied
102
+ false
103
+ end
104
+
105
+ def can_access?(user, object, roles, &block)
106
+ if logged_in? && user.is_in_role?(event, roles)
107
+ content = capture(&block)
108
+ concat(content, block.binding)
109
+ end
110
+ end
111
+
112
+ def is_mine?(user, &block)
113
+ if logged_in? && (current_user.id == user.id)
114
+ content = capture(&block)
115
+ concat(content, block.binding)
116
+ end
117
+ end
118
+
119
+ # Redirect as appropriate when an access request fails.
120
+ #
121
+ # The default action is to redirect to the login screen.
122
+ #
123
+ # Override this method in your controllers if you want to have special
124
+ # behavior in case the user is not authorized
125
+ # to access the requested action. For example, a popup window might
126
+ # simply close itself.
127
+ def access_denied
128
+ respond_to do |format|
129
+ format.html do
130
+ store_location
131
+ flash[:error] = I18n.t('muck.users.access_denied')
132
+ redirect_to login_path
133
+ end
134
+ format.xml do
135
+ request_http_basic_authentication 'Web Password'
136
+ end
137
+ end
138
+ end
139
+
140
+ def permission_denied
141
+ respond_to do |format|
142
+ format.html do
143
+ #Put your domain name here ex. http://www.example.com
144
+ domain_name = GlobalConfig.application_base_url
145
+ http_referer = session[:refer_to]
146
+ if http_referer.nil?
147
+ store_referer
148
+ http_referer = ( session[:refer_to] || domain_name )
149
+ end
150
+ flash[:error] = I18n.t('muck.users.permission_denied')
151
+ #The [0..20] represents the 21 characters in http://localhost:3000
152
+ #You have to set that to the number of characters in your domain name
153
+ if http_referer[0..domain_name.length] != domain_name
154
+ session[:refer_to] = nil
155
+ redirect_to root_path
156
+ else
157
+ redirect_to_referer_or_default(root_path)
158
+ end
159
+ end
160
+ format.xml do
161
+ headers["Status"] = "Unauthorized"
162
+ headers["WWW-Authenticate"] = %(Basic realm="Web Password")
163
+ render :text => I18n.t('muck.users.permission_denied'), :status => '401 Unauthorized'
164
+ end
165
+ format.js do
166
+ render :text => I18n.t('muck.users.permission_denied')
167
+ end
168
+ end
169
+ end
170
+
171
+ def enforce_logout_required
172
+ respond_to do |format|
173
+ format.html do
174
+ redirect_to current_user
175
+ end
176
+ end
177
+ end
178
+
179
+ # Store the URI of the current request in the session.
180
+ # We can return to this location by calling #redirect_back_or_default.
181
+ # Only store html requests so we don't redirect a user back to and rss or xml feed
182
+ def store_location
183
+ if request.format == :html
184
+ session[:return_to] = request.request_uri
185
+ end
186
+ end
187
+
188
+ def store_referer
189
+ session[:refer_to] = request.env["HTTP_REFERER"]
190
+ end
191
+
192
+ # Redirect to the URI stored by the most recent store_location call or
193
+ # to the passed default.
194
+ def redirect_back_or_default(default)
195
+ redirect_to(session[:return_to] || default)
196
+ session[:return_to] = nil
197
+ end
198
+
199
+ def redirect_to_referer_or_default(default)
200
+ redirect_to(session[:refer_to] || default)
201
+ session[:refer_to] = nil
202
+ end
203
+
204
+ # Called from #current_user. Now, attempt to login by basic authentication information.
205
+ # def login_from_basic_auth
206
+ # authenticate_with_http_basic do |username, password|
207
+ # self.current_user = User.authenticate(username, password)
208
+ # end
209
+ # end
210
+
211
+ end
212
+ end
213
+ end
@@ -0,0 +1,192 @@
1
+ module ActiveRecord
2
+ module Acts #:nodoc:
3
+ module MuckUser #:nodoc:
4
+ def self.included(base)
5
+ base.extend(ClassMethods)
6
+ end
7
+
8
+ module ClassMethods
9
+
10
+ def acts_as_muck_user(options = {})
11
+
12
+ has_many :permissions
13
+ has_many :roles, :through => :permissions
14
+
15
+ named_scope :by_newest, :order => "created_at DESC"
16
+ named_scope :active, :conditions => "activated_at IS NOT NULL"
17
+ named_scope :inactive, :conditions => "activated_at IS NULL"
18
+ named_scope :recent, lambda { { :conditions => ['created_at > ?', 1.week.ago] } }
19
+
20
+ email_name_regex = '[\w\.%\+\-]+'.freeze
21
+ domain_head_regex = '(?:[A-Z0-9\-]+\.)+'.freeze
22
+ domain_tld_regex = '(?:[A-Z]{2}|com|org|net|edu|gov|mil|biz|info|mobi|name|aero|jobs|museum)'.freeze
23
+ email_regex = /\A#{email_name_regex}@#{domain_head_regex}#{domain_tld_regex}\z/i
24
+
25
+ has_attached_file :photo,
26
+ :styles => { :medium => "300x300>",
27
+ :thumb => "100x100>",
28
+ :icon => "50x50>" },
29
+ :default_url => "/images/profile_default.jpg"
30
+
31
+ class_eval <<-EOV
32
+ validates_format_of :login, :with => /^[a-z0-9-]+$/i, :message => 'may only contain letters, numbers or a hyphen.'
33
+ validates_format_of :email, :with => email_regex, :message => 'does not look like a valid email address.'
34
+
35
+ # prevents a user from submitting a crafted form that bypasses activation
36
+ attr_protected :crypted_password, :password_salt, :persistence_token, :single_access_token, :perishable_token, :login_count,
37
+ :failed_login_count, :last_request_at, :last_login_at, :current_login_at, :current_login_ip, :last_login_ip,
38
+ :terms_of_service, :time_zone, :disabled_at, :activated_at, :created_at, :updated_at, :photo_file_name,
39
+ :photo_content_type, :photo_file_size
40
+ EOV
41
+
42
+ include ActiveRecord::Acts::MuckUser::InstanceMethods
43
+ extend ActiveRecord::Acts::MuckUser::SingletonMethods
44
+
45
+ end
46
+ end
47
+
48
+ # class methods
49
+ module SingletonMethods
50
+ def do_search( query )
51
+ User.find(:all, :conditions => [ "email LIKE ? OR first_name LIKE ? OR last_name LIKE ?", '%' + query + '%', '%' + query + '%', '%' + query + '%' ])
52
+ end
53
+
54
+ def inactive_count
55
+ User.count :conditions => "activated_at is null"
56
+ end
57
+
58
+ def activate_all
59
+ User.update_all("activated_at = '#{Time.now}'", 'activated_at IS NULL')
60
+ end
61
+
62
+ # Finds the user with the corresponding activation code, activates their account and returns the user.
63
+ #
64
+ # Raises:
65
+ # +User::ActivationCodeNotFound+ if there is no user with the corresponding activation code
66
+ # +User::AlreadyActivated+ if the user with the corresponding activation code has already activated their account
67
+ def find_and_activate!(activation_code)
68
+ raise ArgumentError if activation_code.nil?
69
+ user = find_by_activation_code(activation_code)
70
+ raise ActivationCodeNotFound if !user
71
+ raise AlreadyActivated.new(user) if user.active?
72
+ user.send(:activate!)
73
+ user
74
+ end
75
+
76
+ # checks to see if a given login is already in the database
77
+ def login_exists?(login)
78
+ if User.find_by_login(login).nil?
79
+ false
80
+ else
81
+ true
82
+ end
83
+ end
84
+
85
+ # checks to see if a given email is already in the database
86
+ def email_exists?(email)
87
+ if User.find_by_email(email).nil?
88
+ false
89
+ else
90
+ true
91
+ end
92
+ end
93
+
94
+ end
95
+
96
+ # All the methods available to a record that has had <tt>acts_as_muck_user</tt> specified.
97
+ module InstanceMethods
98
+
99
+ def deliver_welcome_email
100
+ MuckUserMailer.deliver_welcome_notification(self) if GlobalConfig.send_welcome
101
+ end
102
+
103
+ def deliver_activation_confirmation!
104
+ reset_perishable_token!
105
+ MuckUserMailer.deliver_activation_confirmation(self)
106
+ end
107
+
108
+ def deliver_activation_instructions!
109
+ reset_perishable_token!
110
+ MuckUserMailer.deliver_activation_instructions(self)
111
+ end
112
+
113
+ def deliver_password_reset_instructions!
114
+ if self.active?
115
+ reset_perishable_token!
116
+ MuckUserMailer.deliver_password_reset_instructions(self)
117
+ else
118
+ MuckUserMailer.deliver_password_not_active_instructions(self)
119
+ end
120
+ end
121
+
122
+ def deliver_username_request!
123
+ MuckUserMailer.deliver_username_request(self)
124
+ end
125
+
126
+
127
+ # Since password reset doesn't need to change openid_identifier,
128
+ # we save without block as usual.
129
+ def reset_password!(user)
130
+ self.password = user[:password]
131
+ self.password_confirmation = user[:password_confirmation]
132
+ save
133
+ end
134
+
135
+ def has_role?(rolename)
136
+ @roles ||= self.roles.map{|role| role.rolename}
137
+ return false unless @roles
138
+ @roles.include?(rolename)
139
+ end
140
+
141
+ def admin?
142
+ has_role?('administrator')
143
+ end
144
+
145
+ def can_edit?(user)
146
+ return false if user.nil?
147
+ self.id == user.id || user.admin?
148
+ end
149
+
150
+ def to_xml(options = {})
151
+ options[:except] ||= []
152
+ options[:except] << :email << :crypted_password << :salt << :remember_token << :remember_token_expires_at << :activation_code
153
+ options[:except] << :activated_at << :password_reset_code << :enabled << :terms_of_service << :can_send_messages << :identity_url
154
+ options[:except] << :tmp_password << :protected_profile << :public_profile
155
+ super
156
+ end
157
+
158
+ # Authlogic automatically executes the following methods
159
+ def active?
160
+ !activated_at.blank?
161
+ end
162
+
163
+ # def approved?
164
+ # end
165
+ #
166
+ # def confirmed?
167
+ # end
168
+
169
+ def activate!
170
+ self.update_attribute(:activated_at, Time.now.utc)
171
+ end
172
+
173
+ def short_name
174
+ CGI::escapeHTML(self.first_name) || self.display_name
175
+ end
176
+
177
+ def full_name
178
+ if self.first_name.blank? && self.last_name.blank?
179
+ self.display_name rescue 'Deleted user'
180
+ else
181
+ ((CGI::escapeHTML(self.first_name) || '') + ' ' + (CGI::escapeHTML(self.last_name) || '')).strip
182
+ end
183
+ end
184
+
185
+ def display_name
186
+ CGI::escapeHTML(self.login)
187
+ end
188
+
189
+ end
190
+ end
191
+ end
192
+ end
@@ -0,0 +1,5 @@
1
+ class MuckUsersEngine
2
+ module Exceptions
3
+ class InvalidPasswordResetCode < StandardError; end
4
+ end
5
+ end
@@ -0,0 +1,8 @@
1
+ class ActionController::Routing::RouteSet
2
+ def load_routes_with_muck_users!
3
+ muck_users_routes = File.join(File.dirname(__FILE__), *%w[.. .. config muck_users_routes.rb])
4
+ add_configuration_file(muck_users_routes) unless configuration_files.include? muck_users_routes
5
+ load_routes_without_muck_users!
6
+ end
7
+ alias_method_chain :load_routes!, :muck_users
8
+ end
@@ -0,0 +1,46 @@
1
+ require 'rake'
2
+ require 'rake/tasklib'
3
+ require 'fileutils'
4
+
5
+ module Disguise
6
+ class Tasks < ::Rake::TaskLib
7
+ def initialize
8
+ define
9
+ end
10
+
11
+ private
12
+ def define
13
+
14
+ namespace :muck do
15
+ namespace :users do
16
+ desc "Sync files from muck users."
17
+ task :sync do
18
+ path = File.join(File.dirname(__FILE__), *%w[.. ..])
19
+ system "rsync -ruv #{path}/db ."
20
+ system "rsync -ruv #{path}/public ."
21
+ end
22
+
23
+ desc "Setup default admin user"
24
+ task :create_admin => :environment do
25
+ ['administrator'].each {|r| Role.create(:rolename => r) }
26
+ user = User.new
27
+ user.login = "admin"
28
+ user.email = GlobalConfig.admin_email
29
+ user.password = "asdfasdf"
30
+ user.password_confirmation = "asdfasdf"
31
+ user.first_name = "Administrator"
32
+ user.last_name = "Administrator"
33
+ user.save
34
+ user.activate!
35
+
36
+ user.roles << Role.find_by_rolename('administrator')
37
+
38
+ puts 'created admin user'
39
+ end
40
+ end
41
+ end
42
+
43
+ end
44
+ end
45
+ end
46
+ Disguise::Tasks.new
data/lib/muck-users.rb ADDED
@@ -0,0 +1,7 @@
1
+ require 'muck-users/exceptions'
2
+
3
+ ActionController::Base.send :include, ActionController::AuthenticApplication
4
+ ActiveRecord::Base.class_eval { include ActiveRecord::Acts::MuckUser }
5
+ ActiveRecord::Base.class_eval { include MuckUsersEngine::Exceptions }
6
+
7
+ I18n.load_path += Dir[ File.join(File.dirname(__FILE__), '..', 'locales', '*.{rb,yml}') ]