Class: Droom::User
- Inherits:
-
ActiveRecord::Base
- Object
- ActiveRecord::Base
- Droom::User
- Defined in:
- app/models/droom/user.rb
Instance Attribute Summary collapse
-
#defer_confirmation ⇒ Object
People are often invited into the system in batches or after offline contact.
-
#send_confirmation ⇒ Object
People are often invited into the system in batches or after offline contact.
Class Method Summary collapse
-
.for_selection ⇒ Object
For select box.
- .vcards_for(users = []) ⇒ Object
Instance Method Summary collapse
- #active_for_authentication? ⇒ Boolean
- #add_personal_folders(folders = []) ⇒ Object
- #admit_to(groups) ⇒ Object
- #as_json_for_coca(options = {}) ⇒ Object
- #as_search_result ⇒ Object
- #as_suggestion ⇒ Object
-
#authenticate_token(token) ⇒ Object
Auth tokens.
- #clear_session_id! ⇒ Object
-
#colloquial_name ⇒ Object
This is our best shot at a representation of the usual third person form of this person’s name.
- #confirmed=(value) ⇒ Object
- #data_room_user? ⇒ Boolean
-
#database_authenticatable ⇒ Object
Authentication.
- #defer_confirmation! ⇒ Object
- #defer_confirmation? ⇒ Boolean
- #documents ⇒ Object
- #dropbox_client ⇒ Object
- #dropbox_token ⇒ Object
-
#dropbox_tokens ⇒ Object
Dropbox links.
- #ensure_authentication_token ⇒ Object
- #expel_from(group) ⇒ Object
-
#formal_name ⇒ Object
This shuold be a reasonable formal first-person form of address.
-
#full_name ⇒ Object
### Completeness.
- #has_folder?(folder) ⇒ Boolean
- #icon ⇒ Object
-
#image ⇒ Object
Mugshot.
-
#informal_name ⇒ Object
Names.
- #invitation_to(event) ⇒ Object
-
#invitations ⇒ Object
Event invitations.
- #invite_to(event) ⇒ Object
- #invited_to?(event) ⇒ Boolean
- #member_of?(group) ⇒ Boolean
- #membership_of(group) ⇒ Object
-
#memberships ⇒ Object
Group memberships.
- #name ⇒ Object
-
#official_name ⇒ Object
### Compatibility.
-
#organisation ⇒ Object
Organisation affiliation.
- #password_match? ⇒ Boolean
- #password_required? ⇒ Boolean
- #password_set? ⇒ Boolean
- #permission_codes ⇒ Object
- #permission_codes=(codes) ⇒ Object
- #permissions_elsewhere? ⇒ Boolean
- #permitted?(key) ⇒ Boolean
-
#personal_folders ⇒ Object
Folder permissions.
-
#pref(key) ⇒ Object
Preferences.
- #pref?(key) ⇒ Boolean
-
#preference(key) ⇒ Object
‘User#preference(key)` always returns a preference object and is used to build control panels.
- #remove_personal_folders(folders = []) ⇒ Object
- #reset_authentication_token! ⇒ Object
-
#reset_session_id! ⇒ Object
Session ID.
-
#scraps ⇒ Object
Other ownership.
-
#send_confirmation? ⇒ Boolean
Called after save by our own late-confirmation mechanism.
-
#send_confirmation_notification? ⇒ Boolean
Called on create by devise’s immediate confirmation mechanism.
-
#set_pref(key, value) ⇒ Object
Setting preferences is normally handled either by the PreferencesController or by nesting preferences in a user form.
- #thumbnail ⇒ Object
- #title ⇒ Object
- #title_if_it_matters ⇒ Object
-
#title_ordinary? ⇒ Boolean
### Formality.
- #to_vcf ⇒ Object
- #uninvite_from(event) ⇒ Object
-
#user_permissions ⇒ Object
Permissions.
-
#valid_password?(password) ⇒ Boolean
Our old user accounts store passwords as salted sha512 digests.
Instance Attribute Details
#defer_confirmation ⇒ Object
People are often invited into the system in batches or after offline contact. set user.defer_confirmation to a true or call user.defer_confirmation! before saving if you want to create a user account without sending out any messages yet.
When you do want to invite that person, call user.resend_confirmation_token or set the send_confirmation flag on a save.
32 33 34 |
# File 'app/models/droom/user.rb', line 32 def defer_confirmation @defer_confirmation end |
#send_confirmation ⇒ Object
People are often invited into the system in batches or after offline contact. set user.defer_confirmation to a true or call user.defer_confirmation! before saving if you want to create a user account without sending out any messages yet.
When you do want to invite that person, call user.resend_confirmation_token or set the send_confirmation flag on a save.
32 33 34 |
# File 'app/models/droom/user.rb', line 32 def send_confirmation @send_confirmation end |
Class Method Details
.for_selection ⇒ Object
For select box
325 326 327 |
# File 'app/models/droom/user.rb', line 325 def self.for_selection self.published.map{|p| [p.name, p.id] } end |
.vcards_for(users = []) ⇒ Object
450 451 452 |
# File 'app/models/droom/user.rb', line 450 def self.vcards_for(users=[]) users.map(&:vcf).join("\n") end |
Instance Method Details
#active_for_authentication? ⇒ Boolean
56 57 58 |
# File 'app/models/droom/user.rb', line 56 def active_for_authentication? true end |
#add_personal_folders(folders = []) ⇒ Object
241 242 243 |
# File 'app/models/droom/user.rb', line 241 def add_personal_folders(folders=[]) self.folders << folders if folders end |
#admit_to(groups) ⇒ Object
177 178 179 180 181 182 |
# File 'app/models/droom/user.rb', line 177 def admit_to(groups) groups = [groups].flatten groups.each do |group| memberships.where(group_id: group.id).first_or_create end end |
#as_json_for_coca(options = {}) ⇒ Object
146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 |
# File 'app/models/droom/user.rb', line 146 def as_json_for_coca(={}) ensure_uid! ensure_authentication_token { uid: uid, authentication_token: authentication_token, title: title, given_name: given_name, family_name: family_name, chinese_name: chinese_name, email: email, permissions: .join(','), image: thumbnail } end |
#as_search_result ⇒ Object
313 314 315 316 317 318 319 320 |
# File 'app/models/droom/user.rb', line 313 def as_search_result { :type => 'person', :prompt => name, :value => name, :id => id } end |
#as_suggestion ⇒ Object
304 305 306 307 308 309 310 311 |
# File 'app/models/droom/user.rb', line 304 def as_suggestion { :type => 'person', :prompt => formal_name, :value => formal_name, :id => id } end |
#authenticate_token(token) ⇒ Object
Auth tokens
Are no longer native to devise but we use them for domain-cookie auth.
86 87 88 |
# File 'app/models/droom/user.rb', line 86 def authenticate_token(token) Devise.secure_compare(self.authentication_token, token) end |
#clear_session_id! ⇒ Object
78 79 80 |
# File 'app/models/droom/user.rb', line 78 def clear_session_id! self.update_column(:session_id, "") end |
#colloquial_name ⇒ Object
This is our best shot at a representation of the usual third person form of this person’s name. It combines the informal name (which includes some logic to show chinese, anglo and mixed names correctly) with the title, if the title is not ordinary.
409 410 411 |
# File 'app/models/droom/user.rb', line 409 def colloquial_name [title_if_it_matters, informal_name, honours].compact.join(' ') end |
#confirmed=(value) ⇒ Object
103 104 105 |
# File 'app/models/droom/user.rb', line 103 def confirmed=(value) self.confirmed_at = Time.now if value.present? and value != "false" end |
#data_room_user? ⇒ Boolean
530 531 532 |
# File 'app/models/droom/user.rb', line 530 def data_room_user? admin? || permitted?('droom.login') end |
#database_authenticatable ⇒ Object
Authentication
13 14 15 16 17 18 19 |
# File 'app/models/droom/user.rb', line 13 devise :database_authenticatable, :cookie_authenticatable, :recoverable, :trackable, :confirmable, :rememberable, :reconfirmable => false |
#defer_confirmation! ⇒ Object
34 35 36 |
# File 'app/models/droom/user.rb', line 34 def defer_confirmation! self.defer_confirmation = true end |
#defer_confirmation? ⇒ Boolean
38 39 40 |
# File 'app/models/droom/user.rb', line 38 def defer_confirmation? !!self.defer_confirmation end |
#documents ⇒ Object
253 254 255 |
# File 'app/models/droom/user.rb', line 253 def documents Document.visible_to(self) end |
#dropbox_client ⇒ Object
270 271 272 |
# File 'app/models/droom/user.rb', line 270 def dropbox_client dropbox_token.dropbox_client if dropbox_token end |
#dropbox_token ⇒ Object
263 264 265 266 267 268 |
# File 'app/models/droom/user.rb', line 263 def dropbox_token unless @dropbox_token @dropbox_token = dropbox_tokens.by_date.last || 'nope' end @dropbox_token unless @dropbox_token == 'nope' end |
#dropbox_tokens ⇒ Object
Dropbox links
260 |
# File 'app/models/droom/user.rb', line 260 has_many :dropbox_tokens, :foreign_key => "created_by_id" |
#ensure_authentication_token ⇒ Object
97 98 99 100 101 |
# File 'app/models/droom/user.rb', line 97 def ensure_authentication_token if authentication_token.blank? self.authentication_token = generate_authentication_token end end |
#expel_from(group) ⇒ Object
184 185 186 |
# File 'app/models/droom/user.rb', line 184 def expel_from(group) memberships.of_group(group).destroy_all end |
#formal_name ⇒ Object
This shuold be a reasonable formal first-person form of address.
401 402 403 |
# File 'app/models/droom/user.rb', line 401 def formal_name [title, family_name].join(' ') end |
#full_name ⇒ Object
### Completeness
For record-keeping purposes we show the whole name: Chan Tai Wan, Jimmy.
417 418 419 |
# File 'app/models/droom/user.rb', line 417 def full_name [family_name, given_name].compact.join(' ') end |
#has_folder?(folder) ⇒ Boolean
249 250 251 |
# File 'app/models/droom/user.rb', line 249 def has_folder?(folder) folder && personal_folders.of_folder(folder).any? end |
#icon ⇒ Object
290 291 292 |
# File 'app/models/droom/user.rb', line 290 def icon image.url(:icon) if image? end |
#image ⇒ Object
Mugshot
276 277 278 279 280 281 282 |
# File 'app/models/droom/user.rb', line 276 has_attached_file :image, :default_url => ActionController::Base.helpers.image_path("droom/missing/:style.png"), :styles => { :standard => "520x520#", :icon => "32x32#", :thumb => "130x130#" } |
#informal_name ⇒ Object
Names
With Anglo-Chinese Hong Kong names it is difficult to be sure of the right presentation.
We hold the name in three fields: title, given name and family name. People with both a Chinese and an English forename are encouraged to enter their given name in the form Tai Wan, Jimmy. The family_name should always be a single, usually Chinese, surname: Chan or Smith.
When a comma is found in the given name, we assume that they have followed the chinese, english format. If not, we assume the whole name is Chinese.
### Polite informality
People with an English forename would normally be addressed as Jimmy Chan. People with only a Chinese forename should be addressed as Chan Tai Wan.
346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 |
# File 'app/models/droom/user.rb', line 346 def informal_name # The standard form of the given name is Tai Wan, Ray chinese, english = given_name.split(/,\s*/) # But some people are known only as Ray. # Here we can't tell the difference between people with one chinese given name and one anglo given name # but the order of names is reversed in the latter case. For now we assume that the presence of a chinese # name indicates that the chinese word ordering should be used. unless chinese_name? english ||= chinese.strip.split(/\s+/).first end if english # People with an english name are called Ray Chan, by default [english, family_name].join(' ') else # People without are called Chan Tai Wan [family_name, chinese].join(' ') end end |
#invitation_to(event) ⇒ Object
220 221 222 |
# File 'app/models/droom/user.rb', line 220 def invitation_to(event) invitations.to_event(event).first end |
#invitations ⇒ Object
Event invitations
205 |
# File 'app/models/droom/user.rb', line 205 has_many :invitations, :dependent => :destroy |
#invite_to(event) ⇒ Object
208 209 210 |
# File 'app/models/droom/user.rb', line 208 def invite_to(event) invitations.where(event_id: event.id).first_or_create if event end |
#invited_to?(event) ⇒ Boolean
216 217 218 |
# File 'app/models/droom/user.rb', line 216 def invited_to?(event) event && !!invitation_to(event) end |
#member_of?(group) ⇒ Boolean
188 189 190 |
# File 'app/models/droom/user.rb', line 188 def member_of?(group) group && memberships.of_group(group).any? end |
#membership_of(group) ⇒ Object
192 193 194 |
# File 'app/models/droom/user.rb', line 192 def membership_of(group) memberships.find_by(group_id: group.id) end |
#memberships ⇒ Object
Group memberships
169 |
# File 'app/models/droom/user.rb', line 169 has_many :memberships, :dependent => :destroy |
#name ⇒ Object
365 366 367 368 369 370 371 372 373 374 375 |
# File 'app/models/droom/user.rb', line 365 def name chinese, english = given_name.split(/,\s*/) unless chinese_name? english ||= chinese.split(/\s+/).first end if english [english, family_name].join(' ') else [family_name, chinese].join(' ') end end |
#official_name ⇒ Object
### Compatibility
An HKID card will normally show only the translitered Chinese name: Chan Tai Wan
425 426 427 428 |
# File 'app/models/droom/user.rb', line 425 def official_name chinese, english = given_name.split(/,\s*/) [family_name, chinese].join(' ') end |
#organisation ⇒ Object
Organisation affiliation
164 |
# File 'app/models/droom/user.rb', line 164 belongs_to :organisation |
#password_match? ⇒ Boolean
107 108 109 110 111 112 |
# File 'app/models/droom/user.rb', line 107 def password_match? self.errors[:password] << "can't be blank" if password.blank? self.errors[:password_confirmation] << "can't be blank" if password_confirmation.blank? self.errors[:password_confirmation] << "does not match password" if password != password_confirmation password == password_confirmation && !password.blank? end |
#password_required? ⇒ Boolean
60 61 62 |
# File 'app/models/droom/user.rb', line 60 def password_required? confirmed? && (!password.blank?) end |
#password_set? ⇒ Boolean
64 65 66 |
# File 'app/models/droom/user.rb', line 64 def password_set? encrypted_password? end |
#permission_codes ⇒ Object
514 515 516 |
# File 'app/models/droom/user.rb', line 514 def .map(&:slug).compact.uniq end |
#permission_codes=(codes) ⇒ Object
518 519 520 |
# File 'app/models/droom/user.rb', line 518 def (codes) #TODO end |
#permissions_elsewhere? ⇒ Boolean
526 527 528 |
# File 'app/models/droom/user.rb', line 526 def .select{|pc| pc !~ /droom/}.any? end |
#permitted?(key) ⇒ Boolean
522 523 524 |
# File 'app/models/droom/user.rb', line 522 def permitted?(key) .include?(key) end |
#personal_folders ⇒ Object
Folder permissions
To simplify the business of showing and listing documents, we have adopted the convention that all documents live in a folder. User accounts have links to those folders through the very thin PersonalFolder joining class, and at the view level we only ever show folder and subfolder lists. The only place we ever need a list of all the documents visible to this person is when searching, and for that we use the Document.visible_to scope, usually by way of the #documents method defined below.
Personal folders are created and destroyed along with invitations and memberships.
238 |
# File 'app/models/droom/user.rb', line 238 has_many :personal_folders |
#pref(key) ⇒ Object
Preferences
User settings are held as an association with Preference objects, which are simple key:value pairs. The keys are usually colon:separated for namespacing purposes, eg:
current_user.pref("email:enabled?")
current_user.pref("dropbox:enabled?")
Default settings are defined in Droom.user_defaults and can be defined in an initializer if the default droom defaults are not right for your application.
‘User#pref(key)` returns the value of the preference (whether set or default) for the given key. It is intended for use in views:
- if current_user.pref("dropbox:enabled?")
= link_to "copy to dropbox", dropbox_folder_url(folder)
472 473 474 475 476 477 478 |
# File 'app/models/droom/user.rb', line 472 def pref(key) if pref = preferences.find_by(key: key) pref.value else Droom.user_default(key) end end |
#pref?(key) ⇒ Boolean
480 481 482 |
# File 'app/models/droom/user.rb', line 480 def pref?(key) !!pref(key) end |
#preference(key) ⇒ Object
‘User#preference(key)` always returns a preference object and is used to build control panels. If no preference is saved for the given key, we return a new (unsaved) one with that key and the default value.
487 488 489 490 491 |
# File 'app/models/droom/user.rb', line 487 def preference(key) pref = preferences.find_or_initialize_by_key(key) pref.value = Droom.user_default(key) unless pref.persisted? pref end |
#remove_personal_folders(folders = []) ⇒ Object
245 246 247 |
# File 'app/models/droom/user.rb', line 245 def remove_personal_folders(folders=[]) self.folders.delete(folders) if folders end |
#reset_authentication_token! ⇒ Object
90 91 92 93 94 95 |
# File 'app/models/droom/user.rb', line 90 def reset_authentication_token! $cache.delete "/api/authenticate/#{authentication_token}" if $cache && authentication_token token = generate_authentication_token self.update_column(:authentication_token, token) token end |
#reset_session_id! ⇒ Object
Session ID
Allows us to invalidate a session by remote control if someone signs out on a satellite site.
72 73 74 75 76 |
# File 'app/models/droom/user.rb', line 72 def reset_session_id! token = generate_authentication_token self.update_column(:session_id, token) token end |
#scraps ⇒ Object
Other ownership
536 |
# File 'app/models/droom/user.rb', line 536 has_many :scraps, :foreign_key => "created_by_id" |
#send_confirmation? ⇒ Boolean
Called after save by our own late-confirmation mechanism. If the send_confirmation flag has been set, we confirm.
45 46 47 |
# File 'app/models/droom/user.rb', line 45 def send_confirmation? !!self.send_confirmation end |
#send_confirmation_notification? ⇒ Boolean
Called on create by devise’s immediate confirmation mechanism. If the defer_confirmation flag has been set, we postpone.
52 53 54 |
# File 'app/models/droom/user.rb', line 52 def send_confirmation_notification? super && !defer_confirmation? end |
#set_pref(key, value) ⇒ Object
Setting preferences is normally handled either by the PreferencesController or by nesting preferences in a user form. ‘User#set_pref` is a convenient console method but not otherwise used much.
Preferences are set in a simple key:value way, where key usually includes some namespacing prefixes:
user.set_pref("email:enabled", true)
500 501 502 |
# File 'app/models/droom/user.rb', line 500 def set_pref(key, value) preferences.where(key: key).first_or_create.set(value) end |
#thumbnail ⇒ Object
286 287 288 |
# File 'app/models/droom/user.rb', line 286 def thumbnail image.url(:thumb) if image? end |
#title ⇒ Object
391 392 393 394 395 396 397 |
# File 'app/models/droom/user.rb', line 391 def title title = read_attribute(:title) if title.blank? title = (gender == 'f') ? 'Ms' : 'Mr' end title end |
#title_if_it_matters ⇒ Object
387 388 389 |
# File 'app/models/droom/user.rb', line 387 def title_if_it_matters title unless title_ordinary? end |
#title_ordinary? ⇒ Boolean
### Formality
The family name is held separately becaose for most purposes we will address people using the relatively reliable ‘Dr Chan’ or ‘Mr Smith’.
383 384 385 |
# File 'app/models/droom/user.rb', line 383 def title_ordinary? ['Mr', 'Ms', 'Mrs', '', nil].include?(title) end |
#to_vcf ⇒ Object
430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 |
# File 'app/models/droom/user.rb', line 430 def to_vcf @vcard ||= Vcard::Vcard::Maker.make2 do |maker| maker.add_name do |n| n.given = name || "" end maker.add_addr {|a| a.location = 'home' # until we do this properly with multiple contact sets a.country = post_country || "" a.region = post_region || "" a.locality = post_city || "" a.street = "#{post_line1}, #{post_line2}" a.postalcode = post_code || "" } maker.add_tel phone { |t| t.location = 'home' } unless phone.blank? maker.add_tel mobile { |t| t.location = 'cell' } unless mobile.blank? maker.add_email email { |e| t.location = 'home' } end @vcard.to_s end |
#uninvite_from(event) ⇒ Object
212 213 214 |
# File 'app/models/droom/user.rb', line 212 def uninvite_from(event) invitations.to_event(event).destroy_all end |
#user_permissions ⇒ Object
Permissions
Permissions are usually assigned by way of group membership, but the effect of this is to create a user-permission object. Additional user-permission objects can be created: all we need to do here is return that set.
511 |
# File 'app/models/droom/user.rb', line 511 has_many :user_permissions |
#valid_password?(password) ⇒ Boolean
Our old user accounts store passwords as salted sha512 digests. Current best practice uses BCrypt so we migrate user accounts across in this rescue block whenever we hear BCrypt grumbling about the old hash.
117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 |
# File 'app/models/droom/user.rb', line 117 def valid_password?(password) begin super(password) rescue BCrypt::Errors::InvalidHash Rails.logger.warn "...trying sha512 on password input" stretches = 10 salt = self.password_salt pepper = nil old_digest = Devise::Encryptable::Encryptors::Sha512.digest(password, stretches, salt, pepper) if old_digest == self.encrypted_password self.password = password self.save return true else # Doesn't match the old format either: password is just wrong. return false end end end |