muck-users 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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}') ]