Package base :: Package plugins :: Package user
[hide private]

Source Code for Package base.plugins.user

   1  #!/usr/bin/env python 
   2  # $Id: user.module,v 1.914 2008/08/08 19:48:43 dries Exp $ 
   3   
   4  """ 
   5    Enables the user registration and login system. 
   6   
   7    @package user 
   8    @see <a href='http://drupy.net'>Drupy Homepage</a> 
   9    @see <a href='http://drupal.org'>Drupal Homepage</a> 
  10    @note Drupy is a port of the Drupal project. 
  11    @note This file was ported from Drupal's modules/user/user.module 
  12    @author Brendon Crawford 
  13    @copyright 2008 Brendon Crawford 
  14    @contact message144 at users dot sourceforge dot net 
  15    @created 2008-01-10 
  16    @version 0.1 
  17    @note License: 
  18   
  19      This program is free software; you can redistribute it and/or 
  20      modify it under the terms of the GNU General Public License 
  21      as published by the Free Software Foundation; either version 2 
  22      of the License, or (at your option) any later version. 
  23   
  24      This program is distributed in the hope that it will be useful, 
  25      but WITHOUT ANY WARRANTY; without even the implied warranty of 
  26      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
  27      GNU General Public License for more details. 
  28   
  29      You should have received a copy of the GNU General Public License 
  30      along with this program; if not, write to: 
  31       
  32      The Free Software Foundation, Inc., 
  33      51 Franklin Street, Fifth Floor, 
  34      Boston, MA  02110-1301, 
  35      USA 
  36  """ 
  37   
  38  __version__ = "$Revision: 1 $" 
  39   
  40  from lib.drupy import DrupyPHP as php 
  41  from lib.drupy import DrupyImport 
  42  #from includes import password as lib_password 
  43  from includes import common as lib_common 
  44  from includes import path as lib_path 
  45  from includes import database as lib_database 
  46  from includes import bootstrap as lib_bootstrap 
  47  from includes import plugin as lib_plugin 
  48   
  49  # 
  50  # Maximum length of username text field. 
  51  # 
  52  USERNAME_MAX_LENGTH = 60 
  53   
  54  # 
  55  # Maximum length of user e-mail text field. 
  56  # 
  57  EMAIL_MAX_LENGTH = 64 
  58   
  59   
60 -def plugin_invoke(type_, array_, user_, category = None):
61 """ 62 Invokes hook_user() in every module. 63 64 We cannot use plugin_invoke() for this, because the arguments need to 65 be passed by reference. 66 """ 67 php.Reference.check(array_) 68 php.Reference.check(user_) 69 for plugin_ in lib_plugin.list_(): 70 function_name = 'hook_user' 71 if (php.function_exists(function_name, lib_plugin.plugins[plugin_])): 72 function = DrupyImport.getFunction(lib_plugin.plugins[plugin_], function_name) 73 php.call_user_func(function, type_, array_, user_, category)
74 75 76 77 78 # 79 # Implementation of hook_theme(). 80 #
81 -def hook_theme():
82 return { 83 'user_picture' : { 84 'arguments' : {'account' : None}, 85 'template' : 'user-picture' 86 }, 87 'user_profile' : { 88 'arguments' : {'account' : None}, 89 'template' : 'user-profile', 90 'file' : 'user.pages.inc' 91 }, 92 'user_profile_category' : { 93 'arguments' : {'element' : None}, 94 'template' : 'user-profile-category', 95 'file' : 'user.pages.inc' 96 }, 97 'user_profile_item' : { 98 'arguments' : {'element' : None}, 99 'template' : 'user-profile-item', 100 'file' : 'user.pages.inc' 101 }, 102 'user_list' : { 103 'arguments' : {'users' : None, 'title' : None} 104 }, 105 'user_admin_perm' : { 106 'arguments' : {'form' : None}, 107 'file' : 'user.admin.inc' 108 }, 109 'user_admin_new_role' : { 110 'arguments' : {'form' : None}, 111 'file' : 'user.admin.inc' 112 }, 113 'user_admin_account' : { 114 'arguments' : {'form' : None}, 115 'file' : 'user.admin.inc' 116 }, 117 'user_filter_form' : { 118 'arguments' : {'form' : None}, 119 'file' : 'user.admin.inc' 120 }, 121 'user_filters' : { 122 'arguments' : {'form' : None}, 123 'file' : 'user.admin.inc' 124 }, 125 'user_signature' : { 126 'arguments' : {'signature' : None}, 127 }, 128 }
129 130 131 132
133 -def external_load(authname):
134 result = lib_database.query(\ 135 "SELECT uid FROM {authmap} WHERE authname = '%s'", authname) 136 this_user = lib_database.fetch_array(result) 137 if (this_user): 138 return lib_plugin.plugins['user'].load(this_user) 139 else: 140 return False
141 142 143
144 -def external_login(account, edit=[]):
145 """ 146 Perform standard Drupal login operations for a user object. 147 148 The user object must already be authenticated. This function verifies 149 that the user account is not blocked and then performs the login, 150 updates the login timestamp in the database, invokes hook_user('login'), 151 and regenerates the session. 152 153 @param account 154 An authenticated user object to be set as the currently logged 155 in user. 156 @param edit 157 The array of form values submitted by the user, if any. 158 This array is passed to hook_user op login. 159 @return boolean 160 True if the login succeeds, False otherwise. 161 """ 162 form = lib_common.drupal_get_form('user_login') 163 state['values'] = edit 164 if (php.empty(state['values']['name'])): 165 state['values']['name'] = account.name 166 # Check if user is blocked. 167 lib_plugin.plugins['user'].login_name_validate( 168 form, state, php.array_(account)) 169 if (lib_form.get_errors()): 170 # Invalid login. 171 return False 172 # Valid login. 173 lib_bootstrap.user = account 174 lib_plugin.plugins['user'].authenticate_finalize(state['values']) 175 return True
176 177 178
179 -def load(array_={}):
180 """ 181 Fetch a user object. 182 183 @param array 184 An associative array of attributes to search for in selecting the 185 user, such as user name or e-mail address. 186 187 @return 188 A fully-loaded user object upon successful user load or False if user 189 cannot be loaded. 190 """ 191 # Dynamically compose a SQL query: 192 query = [] 193 params = [] 194 if (php.is_numeric(array_)): 195 array_ = {'uid' : array} 196 elif (not php.is_array(array_)): 197 return False 198 for key,value in array_.items(): 199 if (key == 'uid' or key == 'status'): 200 query.append( key + " = %d" ) 201 params.append( value ) 202 elif (key == 'pass'): 203 query.append( "pass = '%s'" ) 204 params.append( value ) 205 else: 206 query.append( "LOWER(key) = LOWER('%s')" ) 207 params.append( value ) 208 result = lib_database.query('SELECT * FROM {users} u WHERE ' + \ 209 php.implode(' AND ', query), params) 210 this_user = lib_database.fetch_object(result) 211 if (this_user): 212 this_user = lib_bootstrap.drupal_unpack(this_user) 213 this_user.roles = {} 214 if (this_user.uid): 215 this_user.roles[lib_bootstrap.DRUPAL_AUTHENTICATED_RID] = \ 216 'authenticated user' 217 else: 218 this_user.roles[DRUPAL_ANONYMOUS_RID] = 'anonymous user' 219 result = lib_database.query(\ 220 'SELECT r.rid, r.name FROM {role} r ' + \ 221 'INNER JOIN {users_roles} ur ON ur.rid = r.rid ' + \ 222 'WHERE ur.uid = %d', this_user.uid) 223 while True: 224 role = lib_database.fetch_object(result) 225 if not role: 226 break 227 this_user.roles[role.rid] = role.name 228 plugin_invoke('load', array_, this_user) 229 else: 230 this_user = False 231 # In python, 'pass' is a reserved word, so we should add an extra field 232 if hasattr(this_user, 'pass'): 233 setattr(this_user, 'pass_', getattr(this_user, 'pass')) 234 return this_user
235 236
237 -def save(account, array_={}, category = 'account'):
238 """ 239 Save changes to a user account or add a new user. 240 241 @param account 242 The user object for the user to modify or add. If user.uid is 243 omitted, a new user will be added. 244 245 @param array 246 An array of fields and values to save. For example array('name' 247 : 'My name'). Keys that do not belong to columns in the user-related 248 tables are added to the a serialized array in the 'data' column 249 and will be loaded in the user.data array by user_load(). 250 Setting a field to None deletes it from the data column. 251 252 @param category 253 (optional) The category for storing profile information in. 254 255 @return 256 A fully-loaded user object upon successful save or False 257 if the save failed. 258 """ 259 table = lib_common.get_schema('users') 260 user_fields = table['fields'] 261 if (not php.empty(array_['pass'])): 262 # Allow alternate password hashing schemes. 263 array_['pass'] = hash_password(php.trim(array_['pass'])) 264 # Abort if the hashing failed and returned False. 265 if (not array_['pass']): 266 return False 267 else: 268 # Avoid overwriting an existing password with a blank password. 269 del(array_['pass']) 270 if (php.is_object(account) and account.uid > 0): 271 plugin_invoke('update', array_, account, category) 272 data = php.unserialize(lib_database.result(lib_database.query(\ 273 'SELECT data FROM {users} WHERE uid = %d', account.uid))) 274 # Consider users edited by an administrator as logged in, if they haven't 275 # already, so anonymous users can view the profile (if allowed). 276 if (php.empty(array_['access']) and php.empty(account.access) and \ 277 lib_plugin.plugins['user'].access('administer users')): 278 array_['access'] = php.time_() 279 for key,value in array_.items(): 280 # Fields that don't pertain to the users or user_roles 281 # automatically serialized into the users.data column. 282 if (key != 'roles' and ph.empty(user_fields[key])): 283 if (value is None): 284 del(data[key]) 285 else: 286 data[key] = value 287 array_['data'] = data 288 array_['uid'] = account.uid 289 # Save changes to the users table. 290 success = lib_common.drupal_write_record('users', array_, 'uid') 291 if (not success): 292 # The query failed - better to abort the save than risk further 293 # data loss. 294 return False 295 # Reload user roles if provided. 296 if (php.isset(php.array_['roles']) and php.is_array(array_['roles'])): 297 lib_database.db_query('DELETE FROM {users_roles} WHERE uid = %d', \ 298 account.uid) 299 for rid in php.array_keys(array_['roles']): 300 if (not php.in_array(rid, (DRUPAL_ANONYMOUS_RID, \ 301 DRUPAL_AUTHENTICATED_RID))): 302 lin_database.db_query(\ 303 'INSERT INTO {users_roles} (uid, rid) VALUES (%d, %d)', \ 304 account.uid, rid) 305 # Delete a blocked user's sessions to kick them if they are online. 306 if (php.isset(array_['status']) and array_['status'] == 0): 307 lib_session.destroy_uid(account.uid) 308 # If the password changed, delete all open sessions and recreate 309 # the current one. 310 if (not empty(array_['pass'])): 311 lib_session.destroy_uid(account.uid) 312 lib_session.regenerate() 313 # Refresh user object. 314 this_user = load({'uid' : account.uid}) 315 # Send emails after we have the new user object. 316 if (php.isset(array_['status']) and array_['status'] != account.status): 317 # The user's status is changing; conditionally send notification email. 318 op = ('status_activated' if (array_['status'] == 1) else \ 319 'status_blocked') 320 _mail_notify(op, this_user) 321 plugin_invoke('after_update', array_, this_user, category) 322 else: 323 # Allow 'created' to be set by the caller. 324 if (not php.isset(array_['created'])): 325 array_['created'] = php.time_() 326 # Consider users created by an administrator as already logged in, so 327 # anonymous users can view the profile (if allowed). 328 if (php.empty(array_['access']) and access('administer users')): 329 array_['access'] = php.time_() 330 success = lib_common.drupal_write_record('users', array_) 331 if (not success): 332 # On a failed INSERT some other existing user's uid may be returned. 333 # We must abort to avoid overwriting their account. 334 return False 335 # Build the initial user object. 336 this_user = load({'uid' : array_['uid']}) 337 plugin_invoke('insert', array_, this_user, category) 338 # Note, we wait with saving the data column to prevent module-handled 339 # fields from being saved there. 340 data = [] 341 for key,value in array_.items(): 342 if ((key != 'roles') and (php.empty(user_fields[key])) and \ 343 (value != None)): 344 data[key] = value 345 if (not empty(data)): 346 data_array = {'uid' : user.uid, 'data' : data} 347 lib_common.drupal_write_record('users', data_array, 'uid') 348 # Save user roles (delete just to be safe). 349 if (php.isset(array_['roles']) and php.is_array(array_['roles'])): 350 lib_database.db_query('DELETE FROM {users_roles} WHERE uid = %d', \ 351 array_['uid']) 352 for rid in php.array_keys(array_['roles']): 353 if (not php.in_array(rid, (DRUPAL_ANONYMOUS_RID, \ 354 DRUPAL_AUTHENTICATED_RID))): 355 db_query('INSERT INTO {users_roles} (uid, rid) VALUES (%d, %d)', \ 356 array_['uid'], rid) 357 # Build the finished user object. 358 this_user = load({'uid' : array_['uid']}) 359 return this_user
360 361 362
363 -def validate_name(name):
364 """ 365 Verify the syntax of the given name. 366 """ 367 if (not name): 368 return lib_common.t('You must enter a username.') 369 if (php.substr(name, 0, 1) == ' '): 370 return lib_common.t('The username cannot begin with a space.') 371 if (php.substr(name, -1) == ' '): 372 return lib_common.t('The username cannot end with a space.') 373 if (php.strpos(name, ' ') != False): 374 return lib_common.t(\ 375 'The username cannot contain multiple spaces in a row.') 376 if (php.preg_match('/[^\x80-\xF7 a-z0-9@_.\'-]/i', name)): 377 return lib_common.t('The username contains an illegal character.') 378 if (php.preg_match(\ 379 # Non-printable ISO-8859-1 + NBSP 380 '/[\x80-\xA0' + \ 381 # Soft-hyphen 382 '\xAD' + \ 383 # Various space characters 384 '\u2000-\u200F' + \ 385 # Bidirectional text overrides 386 '\u2028-\u202F' + \ 387 # Various text hinting characters 388 '\u205F-\u206F' + \ 389 # Byte order mark 390 '\uFEFF' + \ 391 # Full-width latin 392 '\uFF01-\uFF60' + \ 393 # Replacement characters 394 '\xFFF9-\xFFFD' + \ 395 # None byte and control characters 396 '\x00-\x1F]/u', \ 397 name)): 398 return lib_common.t('The username contains an illegal character.') 399 if (drupal_strlen(name) > USERNAME_MAX_LENGTH): 400 return lib_common.t(\ 401 'The username %name is too long: it must be %max characters or less.', \ 402 {'%name' : name, '%max' : USERNAME_MAX_LENGTH})
403 404 405
406 -def validate_mail(mail):
407 if (not mail): 408 return lib_common.t('You must enter an e-mail address.') 409 if (not valid_email_address(mail)): 410 return lib_common.t('The e-mail address %mail is not valid.', \ 411 {'%mail' : mail})
412 413 414
415 -def validate_picture(form, form_state):
416 php.Reference.check(form) 417 php.Reference.check(form_state) 418 # If required, validate the uploaded picture. 419 validators = { 420 'file_validate_is_image' : [], 421 'file_validate_image_resolution' : \ 422 (lib_bootstrap.variable_get('user_picture_dimensions', '85x85')), 423 'file_validate_size' : \ 424 (lib_bootstrap.variable_get('user_picture_file_size', '30') * 1024) 425 } 426 file_ = lib_file.save_upload('picture_upload', validators) 427 if (file_): 428 # Remove the old picture. 429 if (php.isset(form_state['values']['_account'].picture) and \ 430 lib_file.exists(form_state['values']['_account'].picture)): 431 lib_file.delete(form_state['values']['_account'].picture) 432 # The image was saved using file_save_upload() and was added to the 433 # files table as a temporary file. We'll make a copy and let the garbage 434 # collector delete the original upload. 435 info = image_get_info(file_.filepath) 436 destination = lib_bootstrap.variable_get('user_picture_path', \ 437 'pictures') + '/picture-' + form['#uid'] + '.' + info['extension'] 438 if (lib_file.copy(file_, destination, FILE_EXISTS_REPLACE)): 439 form_state['values']['picture'] = file_.filepath 440 else: 441 lib_form.set_error('picture_upload', \ 442 lib_common.t("Failed to upload the picture image; " + \ 443 "the %directory directory doesn't exist or is not writable.", \ 444 {'%directory' : \ 445 lib_common.variable_get('user_picture_path', 'pictures')}))
446 447
448 -def user_password(length=10):
449 """ 450 Generate a random alphanumeric password. 451 """ 452 # This variable contains the list of allowable characters for the 453 # password. Note that the number 0 and the letter 'O' have been 454 # removed to avoid confusion between the two. The same is True 455 # of 'I', 1, and 'l'. 456 allowable_characters = \ 457 'abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789' 458 # Zero-based count of characters in the allowable list: 459 len_ = php.strlen(allowable_characters) - 1 460 # Declare the password as a blank string. 461 pass_ = '' 462 # Loop the number of times specified by length. 463 for i in range(length): 464 # Each iteration, pick a random character from the 465 # allowable string and append it to the password: 466 pass_ += allowable_characters[php.mt_rand(0, len_)] 467 return pass_
468 469
470 -def role_permissions(roles=[], reset=False):
471 """ 472 Determine the permissions for one or more roles. 473 474 @param roles 475 An array whose keys are the role IDs of interest, such as user.roles. 476 @param reset 477 Optional parameter - if True data in the static variable is rebuilt. 478 479 @return 480 An array indexed by role ID. Each value is an array whose keys are the 481 permission strings for the given role ID. 482 """ 483 php.static(role_permissions, 'stored_permissions', {}) 484 if (reset): 485 # Clear the data cached in the static variable. 486 role_permissions.stored_permissions = {} 487 role_permissions_ = fetch = [] 488 if (roles): 489 for rid,name in roles.items(): 490 if (php.isset(role_permissions.stored_permissions[rid])): 491 role_permissions_[rid] = role_permissions.stored_permissions[rid] 492 else: 493 # Add this rid to the list of those needing to be fetched. 494 fetch.append( rid ) 495 # Prepare in case no permissions are returned. 496 role_permissions.stored_permissions[rid] = {} 497 if (fetch): 498 # Get from the database permissions that were not in the static variable. 499 # Only role IDs with at least one permission assigned will return rows. 500 result = lib_database.query(\ 501 "SELECT r.rid, p.permission FROM {role} r " + \ 502 "INNER JOIN {role_permission} p ON p.rid = r.rid " + \ 503 "WHERE r.rid IN (" + lib_database.placeholders(fetch) + ")", fetch) 504 while True: 505 row = lib_database.fetch_array(result) 506 if not row: 507 break 508 role_permissions.stored_permissions[row['rid']][row['permission']] = \ 509 True 510 for rid in fetch: 511 # For every rid, we know we at least assigned an empty array. 512 role_permissions_[rid] = role_permissions.stored_permissions[rid] 513 return role_permissions_
514 515
516 -def access(string_, account=None, reset=False):
517 """ 518 Determine whether the user has a given privilege. 519 520 @param string 521 The permission, such as "administer nodes", being checked for. 522 @param account 523 (optional) The account to check, 524 if not given use currently logged in user. 525 @param reset 526 (optional) Resets the user's permissions cache, which will result in a 527 recalculation of the user's permissions. This is necessary to support 528 dynamically added user roles. 529 530 @return 531 Boolean True if the current user has the requested permission. 532 533 All permission checks in Drupal should go through this function. This 534 way, we guarantee consistent behavior, and ensure that the superuser 535 can perform all actions. 536 """ 537 php.static(access, 'perm', {}) 538 if (reset): 539 access.perm = {} 540 if (account is None): 541 account = lib_bootstrap.user 542 # User #1 has all privileges: 543 if (account.uid == 1): 544 return True 545 # To reduce the number of SQL queries, we cache the user's permissions 546 # in a static variable. 547 if (not php.isset(access.perm[account.uid])): 548 role_permissions = role_permissions(account.roles, reset) 549 access.perms = {} 550 for one_role in role_permissions: 551 access.perms += one_role 552 access.perm[account.uid] = access.perms 553 return php.isset(access.perm[account.uid][string])
554 555
556 -def is_blocked(name):
557 """ 558 Checks for usernames blocked by user administration. 559 560 @return boolean True for blocked users, False for active. 561 """ 562 deny = lib_database.fetch_object(\ 563 lib_database.query(\ 564 "SELECT name FROM {users} WHERE status = 0 AND name = LOWER('%s')", \ 565 name)) 566 return deny
567 568
569 -def hook_perm():
570 """ 571 Implementation of hook_perm(). 572 """ 573 return { 574 'administer permissions' : lib_common.t(\ 575 'Manage the permissions assigned to user roles + %warning', \ 576 {'%warning' : lib_common.t(\ 577 'Warning: Give to trusted roles only; ' + \ 578 'this permission has security implications.')}), 579 'administer users' : lib_common.t(\ 580 'Manage or block users, and manage their role assignments.'), 581 'access user profiles' : lib_common.t(\ 582 'View profiles of users on the site, ' + \ 583 'which may contain personal information.'), 584 'change own username' : t('Select a different username.'), 585 }
586 587
588 -def hook_file_download(file):
589 """ 590 Implementation of hook_file_download(). 591 592 Ensure that user pictures (avatars) are always downloadable. 593 """ 594 if (php.strpos(file, lib_bootstrap.variable_get(\ 595 'user_picture_path', 'pictures') + '/picture-') == 0): 596 info = lib_plugin.plugins['image'].get_info(\ 597 lib_plugin.plugins['file'].create_path(file)) 598 return ('Content-type: ' + info['mime_type'],)
599 600
601 -def hook_search(op = 'search', keys = None, skip_access_check = False):
602 """ 603 Implementation of hook_search(). 604 """ 605 if op == 'name': 606 if (skip_access_check or access('access user profiles')): 607 return lib_common.t('Users') 608 elif op == 'search': 609 if (access('access user profiles')): 610 find = [] 611 # Replace wildcards with MySQL/PostgreSQL wildcards. 612 keys = php.preg_replace('not \*+not ', '%', keys) 613 if (access('administer users')): 614 # Administrators can also search in the otherwise private email field. 615 result = lib_database.pager_query(\ 616 "SELECT name, uid, mail FROM {users} " + \ 617 "WHERE LOWER(name) LIKE LOWER('%%%s%%') OR " + \ 618 "LOWER(mail) LIKE LOWER('%%%s%%')", 15, 0, None, keys, keys) 619 while True: 620 account = db_fetch_object(result) 621 if not account: 622 break 623 find.append({ 624 'title' : account.name + ' (' + account.mail + ')', \ 625 'link' : \ 626 lib_common.url('user/' + account.uid, {'absolute' : True}) 627 }) 628 else: 629 result = lib_database.pager_query(\ 630 "SELECT name, uid FROM {users} " + \ 631 "WHERE LOWER(name) LIKE LOWER('%%%s%%')", 15, 0, None, keys) 632 while True: 633 account = db_fetch_object(result) 634 if not account: 635 break; 636 find.append({ 637 'title' : account.name, 638 'link' : lib_common.url('user/' + account.uid, \ 639 {'absolute' : True}) 640 }) 641 return find
642 643 644
645 -def hook_elements():
646 """ 647 Implementation of hook_elements(). 648 """ 649 return { 650 'user_profile_category' : [], 651 'user_profile_item' : [] 652 }
653 654 655
656 -def hook_user(type, edit, account, category=None):
657 """ 658 Implementation of hook_user(). 659 """ 660 php.Reference.check(edit) 661 php.Reference.check(account) 662 if (type == 'view'): 663 account.content['user_picture'] = { 664 '#value' : lib_theme.theme('user_picture', account), 665 '#weight' : -10 666 } 667 if (not php.isset(account.content, 'summary')): 668 account.content['summary'] = {} 669 account.content['summary'] += { 670 '#type' : 'user_profile_category', 671 '#attributes' : {'class' : 'user-member'}, 672 '#weight' : 5, 673 '#title' : lib_common.t('History') 674 } 675 account.content['summary']['member_for'] = { 676 '#type' : 'user_profile_item', 677 '#title' : lib_theme.t('Member for'), 678 '#markup' : format_interval(php.time_() - account.created) 679 } 680 if (type == 'form' and category == 'account'): 681 form_state = {} 682 return edit_form(form_state, lib_common.arg(1), edit) 683 if (type == 'validate' and category == 'account'): 684 return _user_edit_validate(arg(1), edit) 685 if (type == 'submit' and category == 'account'): 686 return _user_edit_submit(arg(1), edit) 687 if (type == 'categories'): 688 return ({ 689 'name' : 'account', 690 'title' : lib_common.t('Account settings'), 691 'weight' : 1 692 },)
693 694 695 696 697
698 -def login_block():
699 form = { 700 '#action' : lib_common.url(php.GET['q'], \ 701 {'query' : drupal_get_destination()}), 702 '#id' : 'user-login-form', 703 '#validate' : login_default_validators(), 704 '#submit' : ('user_login_submit',), 705 } 706 form['name'] = { 707 '#type' : 'textfield', 708 '#title' : t('Username'), 709 '#maxlength' : USERNAME_MAX_LENGTH, 710 '#size' : 15, 711 '#required' : True, 712 } 713 form['pass'] = { 714 '#type' : 'password', 715 '#title' : lib_common.t('Password'), 716 '#maxlength' : 60, 717 '#size' : 15, 718 '#required' : True 719 } 720 form['submit'] = { 721 '#type' : 'submit', 722 '#value' : lib_common.t('Log in') 723 } 724 items = {} 725 if (lib_bootstrap.variable_get('user_register', 1)): 726 items.append( lib_common.l(\ 727 lib_common.t('Create new account'), 'user/register', \ 728 {'attributes' : {'title' : t('Create a new user account.')}}) ) 729 items.append( lib_common.l(\ 730 t('Request new password'), \ 731 'user/password', {'attributes' : \ 732 {'title' : lib_common.t('Request new password via e-mail.')}}) ) 733 form['links'] = {'#markup' : lib_theme.theme('item_list', items)} 734 return form
735 736 737 738
739 -def hook_block(op='list', delta='', edit=[]):
740 """ 741 Implementation of hook_block(). 742 """ 743 if (op == 'list'): 744 blocks = { 745 'login' : {}, 746 'navigation' : {}, 747 'new' : {}, 748 'online' : {} 749 } 750 blocks['login']['info'] = lib_common.t('User login') 751 # Not worth caching. 752 blocks['login']['cache'] = BLOCK_NO_CACHE 753 blocks['navigation']['info'] = lib_common.t('Navigation') 754 # Menu blocks can't be cached because each menu item can have 755 # a custom access callback. menu.inc manages its own caching. 756 blocks['navigation']['cache'] = BLOCK_NO_CACHE 757 blocks['new']['info'] = lib_common.t('Who\'s new') 758 # Too dynamic to cache. 759 blocks['online']['info'] = lib_common.t('Who\'s online') 760 blocks['online']['cache'] = BLOCK_NO_CACHE 761 return blocks 762 elif (op == 'configure' and delta == 'new'): 763 form['user_block_whois_new_count'] = { 764 '#type' : 'select', 765 '#title' : lib_common.t('Number of users to display'), 766 '#default_value' : \ 767 lib_bootstrap.variable_get('user_block_whois_new_count', 5), 768 '#options' : drupal_map_assoc((1, 2, 3, 4, 5, 6, 7, 8, 9, 10)) 769 } 770 return form 771 elif (op == 'configure' and delta == 'online'): 772 period = drupal_map_assoc( 773 (30, 60, 120, 180, 300, 600, 900, 1800, 2700, 3600, 5400, 7200, \ 774 10800, 21600, 43200, 86400), 'format_interval') 775 form['user_block_seconds_online'] = { 776 '#type' : 'select', 777 '#title' : lib_common.t('User activity'), 778 '#default_value' : \ 779 lib_bootstrap.variable_get('user_block_seconds_online', 900), 780 '#options' : period, 781 '#description' : \ 782 lib.common.t('A user is considered online for ' + \ 783 'this long after they have last viewed a page.') 784 } 785 form['user_block_max_list_count'] = {'#type' : 'select', \ 786 '#title' : lib_common.t('User list length'), \ 787 '#default_value' : \ 788 lib_bootstrap.variable_get('user_block_max_list_count', 10), \ 789 '#options' : drupal_map_assoc(\ 790 (0, 5, 10, 15, 20, 25, 30, 40, 50, 75, 100)), \ 791 '#description' : lib_common.t('Maximum number of ' + \ 792 'currently online users to display.')} 793 return form 794 elif (op == 'save' and delta == 'new'): 795 lib_bootstrap.variable_set('user_block_whois_new_count', \ 796 edit['user_block_whois_new_count']) 797 elif (op == 'save' and delta == 'online'): 798 lib_bootstrap.variable_set('user_block_seconds_online', \ 799 edit['user_block_seconds_online']) 800 lib_bootstrap.variable_set('user_block_max_list_count', \ 801 edit['user_block_max_list_count']) 802 elif (op == 'view'): 803 block = {} 804 if delta == 'login': 805 # For usability's sake, avoid showing two login forms on one page. 806 if (lib_bootstrap.user.uid < 1 and not \ 807 (lib_path.arg(0) == 'user' and not php.is_numeric(lib_path.arg(1)))): 808 block['subject'] = lib_common.t('User login') 809 block['content'] = drupal_get_form('user_login_block') 810 return block 811 elif delta == 'navigation': 812 menu = lib_menu.tree() 813 if (menu): 814 block['subject'] = (check_plain(lib_bootstrap.user.name) if \ 815 (user.uid > 0) else \ 816 lib_common.t('Navigation')) 817 block['content'] = menu 818 return block 819 elif delta == 'new': 820 if (access('access content')): 821 # Retrieve a list of new users who have 822 # subsequently accessed the site successfully. 823 result = lib_database.query_range(\ 824 'SELECT uid, name FROM {users} ' + \ 825 'WHERE status != 0 AND access != 0 ' + \ 826 'ORDER BY created DESC', 0, \ 827 lib_common.variable_get('user_block_whois_new_count', 5)) 828 while True: 829 account = lib_database.fetch_object(result) 830 if not account: 831 break 832 items.append( account ) 833 output = lib_theme.theme('user_list', items) 834 block['subject'] = lib_common.t('Who\'s new') 835 block['content'] = output 836 return block 837 elif delta == 'online': 838 if (access('access content')): 839 # Count users active within the defined period. 840 interval = time() - variable_get('user_block_seconds_online', 900) 841 # Perform database queries to gather online user lists. 842 # We use s.timestamp 843 # rather than u.access because it is much faster. 844 anonymous_count = sess_count(interval) 845 authenticated_users = lib_database.query(\ 846 'SELECT DISTINCT u.uid, u.name, s.timestamp ' + \ 847 'FROM {users} u ' + \ 848 'INNER JOIN {sessions} s ON u.uid = s.uid ' + \ 849 'WHERE s.timestamp >= %d AND s.uid > 0 ' + \ 850 'ORDER BY s.timestamp DESC', interval) 851 authenticated_count = 0 852 max_users = lib_bootstrap.variable_get('user_block_max_list_count', 10) 853 items = [] 854 while True: 855 account = lib_database.fetch_object(authenticated_users) 856 if (max_users > 0): 857 items.append( account ) 858 max_users -= 1 859 authenticated_count += 1 860 # Format the output with proper grammar. 861 if (anonymous_count == 1 and authenticated_count == 1): 862 output = lib_common.t(\ 863 'There is currently %members and %visitors online.', \ 864 {'%members' : \ 865 format_plural(authenticated_count, '1 user', '@count users'), \ 866 '%visitors' : format_plural(anonymous_count, '1 guest', \ 867 '@count guests')}) 868 else: 869 output = lib_common.t(\ 870 'There are currently %members and %visitors online.', \ 871 {'%members' : format_plural(authenticated_count, '1 user', \ 872 '@count users'), '%visitors' : \ 873 format_plural(anonymous_count, '1 guest', '@count guests')}) 874 # Display a list of currently online users. 875 max_users = lib_bootstrap.variable_get('user_block_max_list_count', 10) 876 if (authenticated_count and max_users): 877 output += lib_theme.theme('user_list', items, \ 878 lib_common.t('Online users')) 879 block['subject'] = lib_common.t('Who\'s online') 880 block['content'] = output 881 return block
882 883 884
885 -def template_preprocess_user_picture(variables):
886 """ 887 Process variables for user-picture.tpl.php. 888 889 The variables array contains the following arguments: 890 - account 891 892 @see user-picture.tpl.php 893 """ 894 php.Reference.check(variables) 895 variables['picture'] = '' 896 if (lib_bootstrap.variable_get('user_pictures', 0)): 897 account = variables['account'] 898 if (not php.empty(account.picture) and php.file_exists(account.picture)): 899 picture = lib_file.create_url(account.picture) 900 elif (lib_bootstrap.variable_get('user_picture_default', '')): 901 picture = variable_get('user_picture_default', '') 902 if (php.isset(picture)): 903 alt = lib_common.t("@user's picture", {'@user' : \ 904 (account.name if account.name else \ 905 lib_bootstrap.variable_get('anonymous', t('Anonymous')))}) 906 variables['picture'] = theme('image', picture, alt, alt, '', False) 907 if (not empty(account.uid) and access('access user profiles')): 908 attributes = {'attributes' : {'title' : t('View user profile.')}, \ 909 'html' : True} 910 variables['picture'] = lib_common.l(variables['picture'], \ 911 "user/account.uid", attributes)
912 913 914 915
916 -def theme_list(users, title=None):
917 """ 918 Make a list of users. 919 920 @param users 921 An array with user objects. Should contain at least the name and uid. 922 @param title 923 (optional) Title to pass on to theme_item_list(). 924 925 @ingroup themeable 926 """ 927 if (not php.empty(users)): 928 for user in users: 929 items.append( lib_theme.theme('username', user) ) 930 return lib_theme.theme('item_list', items, title)
931 932 933 934
935 -def is_anonymous():
936 # Menu administrators can see items for anonymous when administering. 937 return not lib_bootstrap.user.uid or not php.empty(lib_bootstrap.menu_admin)
938 939
940 -def is_logged_in():
941 return bool(lib_bootstrap.user.uid)
942 943
944 -def register_access():
945 return is_anonymous() and lib_bootstrap.variable_get('user_register', 1)
946 947 948
949 -def view_access(account):
950 return account and account.uid and \ 951 (\ 952 # Always let users view their own profile. 953 (lib_bootstrap.user.uid == account.uid) or \ 954 # Administrators can view all accounts. 955 access('administer users') or \ 956 # The user is not blocked and logged in at least once. 957 (account.access and account.status and access('access user profiles')) \ 958 )
959 960 961 """ 962 Access callback for user account editing. 963 """
964 -def edit_access(account):
965 return ((lib_bootstrap.user.uid == account.uid) or \ 966 access('administer users')) and account.uid > 0
967 968
969 -def load_self(arg_):
970 arg_[1] = load(lib_bootstrap.user.uid) 971 return arg_
972 973
974 -def hook_menu():
975 """ 976 Implementation of hook_menu(). 977 """ 978 items = {} 979 items['user/autocomplete'] = { 980 'title' : 'User autocomplete', 981 'page callback' : 'user_autocomplete', 982 'access callback' : 'user_access', 983 'access arguments' : ('access user profiles',), 984 'type' : MENU_CALLBACK 985 } 986 # Registration and login pages. 987 items['user'] = { 988 'title' : 'User account', 989 'page callback' : 'user_page', 990 'access callback' : True, 991 'type' : MENU_CALLBACK 992 } 993 items['user/login'] = { 994 'title' : 'Log in', 995 'access callback' : 'user_is_anonymous', 996 'type' : MENU_DEFAULT_LOCAL_TASK 997 } 998 items['user/register'] = { 999 'title' : 'Create new account', 1000 'page callback' : 'drupal_get_form', 1001 'page arguments' : ('user_register',), 1002 'access callback' : 'user_register_access', 1003 'type' : MENU_LOCAL_TASK 1004 } 1005 items['user/password'] = { 1006 'title' : 'Request new password', 1007 'page callback' : 'drupal_get_form', 1008 'page arguments' : ('user_pass',), 1009 'access callback' : 'user_is_anonymous', 1010 'type' : MENU_LOCAL_TASK 1011 } 1012 items['user/reset/%/%/%'] = { 1013 'title' : 'Reset password', 1014 'page callback' : 'drupal_get_form', 1015 'page arguments' : ('user_pass_reset', 2, 3, 4), 1016 'access callback' : True, 1017 'type' : MENU_CALLBACK 1018 } 1019 # User administration pages. 1020 items['admin/user'] = { 1021 'title' : 'User management', 1022 'description' : \ 1023 "Manage your site's users, groups and access to site features.", 1024 'position' : 'left', 1025 'page callback' : 'system_admin_menu_block_page', 1026 'access arguments' : ('access administration pages',) 1027 } 1028 items['admin/user/user'] = { 1029 'title' : 'Users', 1030 'description' : 'List, add, and edit users.', 1031 'page callback' : 'user_admin', 1032 'page arguments' : ('list',), 1033 'access arguments' : ('administer users',), 1034 } 1035 items['admin/user/user/list'] = { 1036 'title' : 'List', 1037 'type' : MENU_DEFAULT_LOCAL_TASK, 1038 'weight' : -10 1039 } 1040 items['admin/user/user/create'] = { 1041 'title' : 'Add user', 1042 'page arguments' : ('create',), 1043 'access arguments' : ('administer users',), 1044 'type' : MENU_LOCAL_TASK 1045 } 1046 items['admin/user/settings'] = { 1047 'title' : 'User settings', 1048 'description' : \ 1049 'Configure default behavior of users, including registration ' + \ 1050 'requirements, e-mails, and user pictures.', 1051 'page callback' : 'drupal_get_form', 1052 'page arguments' : ('user_admin_settings',), 1053 'access arguments' : ('administer users',) 1054 } 1055 # Permission administration pages. 1056 items['admin/user/permissions'] = { 1057 'title' : 'Permissions', 1058 'description' : 'Determine access to features by ' + \ 1059 'selecting permissions for roles.', 1060 'page callback' : 'drupal_get_form', 1061 'page arguments' : ('user_admin_perm',), 1062 'access arguments' : ('administer permissions',) 1063 } 1064 items['admin/user/roles'] = { 1065 'title' : 'Roles', 1066 'description' : 'List, edit, or add user roles.', 1067 'page callback' : 'drupal_get_form', 1068 'page arguments' : ('user_admin_new_role',), 1069 'access arguments' : ('administer permissions',) 1070 } 1071 items['admin/user/roles/edit'] = { 1072 'title' : 'Edit role', 1073 'page arguments' : ('user_admin_role',), 1074 'access arguments' : ('administer permissions',), 1075 'type' : MENU_CALLBACK 1076 } 1077 items['logout'] = { 1078 'title' : 'Log out', 1079 'access callback' : 'user_is_logged_in', 1080 'page callback' : 'user_logout', 1081 'weight' : 10 1082 } 1083 items['user/%user_uid_optional'] = { 1084 'title' : 'My account', 1085 'title callback' : 'user_page_title', 1086 'title arguments' : (1,), 1087 'page callback' : 'user_view', 1088 'page arguments' : (1,), 1089 'access callback' : 'user_view_access', 1090 'access arguments' : (1,), 1091 'parent' : '' 1092 } 1093 items['user/%user/view'] = { 1094 'title' : 'View', 1095 'type' : MENU_DEFAULT_LOCAL_TASK, 1096 'weight' : -10 1097 } 1098 items['user/%user/delete'] = { 1099 'title' : 'Delete', 1100 'page callback' : 'drupal_get_form', 1101 'page arguments' : ('user_confirm_delete', 1), 1102 'access callback' : 'user_access', 1103 'access arguments' : ('administer users',), 1104 'type' : MENU_CALLBACK 1105 } 1106 items['user/%user_category/edit'] = { 1107 'title' : 'Edit', 1108 'page callback' : 'user_edit', 1109 'page arguments' : (1,), 1110 'access callback' : 'user_edit_access', 1111 'access arguments' : (1,), 1112 'type' : MENU_LOCAL_TASK, 1113 'load arguments' : ('%map', '%index') 1114 } 1115 items['user/%user_category/edit/account'] = { 1116 'title' : 'Account', 1117 'type' : MENU_DEFAULT_LOCAL_TASK, 1118 'load arguments' : ('%map', '%index') 1119 } 1120 empty_account = php.stdClass() 1121 categories = _user_categories(empty_account) 1122 if (categories and (php.count(categories) > 1)): 1123 for key,category in categories.items(): 1124 # 'account' is already handled by the MENU_DEFAULT_LOCAL_TASK. 1125 if (category['name'] != 'account'): 1126 items['user/%user_category/edit/' + category['name']] = { 1127 'title callback' : 'check_plain', 1128 'title arguments' : (category['title'],), 1129 'page callback' : 'user_edit', 1130 'page arguments' : (1, 3), 1131 'access callback' : (category['access callback'] if \ 1132 php.isset(category['access callback']) else 'user_edit_access'), 1133 'access arguments' : (category['access arguments'] if \ 1134 php.isset(category['access arguments']) else (1,)), 1135 'type' : MENU_LOCAL_TASK, 1136 'weight' : category['weight'], 1137 'load arguments' : ('%map', '%index'), 1138 'tab_parent' : 'user/%/edit' 1139 } 1140 return items
1141 1142 1143
1144 -def hook_init():
1145 lib_common.drupal_add_css(lib_common.drupal_get_path('plugin', 'user') + \ 1146 '/user.css', 'plugin')
1147 1148 1149
1150 -def uid_optional_load(arg):
1151 return load((arg_ if php.isset(arg_) else lib_bootstrap.user.uid))
1152 1153 1154
1155 -def category_load(uid, map_, index):
1156 """ 1157 Return a user object after checking if any profile category in 1158 the path exists. 1159 """ 1160 php.static(category_load, 'user_categories') 1161 php.static(category_load, 'accounts') 1162 php.Reference.check(map_) 1163 # Cache account - this load function will get called for each profile tab. 1164 if (not php.isset(category_load.accounts[uid])): 1165 category_load.accounts[uid] = load(uid) 1166 valid = True 1167 account = category_load.accounts[uid] 1168 if account: 1169 # Since the path is like user/%/edit/category_name, the category name will 1170 # be at a position 2 beyond the index corresponding to the % wildcard. 1171 category_index = index + 2 1172 # Valid categories may contain slashes, and hence need to be imploded. 1173 category_path = php.implode('/', php.array_slice(map_, category_index)) 1174 if (category_path): 1175 # Check that the requested category exists. 1176 valid = False 1177 if (not php.isset(category_load.user_categories)): 1178 empty_account = php.stdClass() 1179 category_load.user_categories = _categories(empty_account) 1180 for category in category_load.user_categories: 1181 if (category['name'] == category_path): 1182 valid = True 1183 # Truncate the map array in case the category name had slashes. 1184 map_ = php.array_slice(map_, 0, category_index) 1185 # Assign the imploded category name to the last map element. 1186 map_[category_index] = category_path 1187 break 1188 return (account if valid else False)
1189 1190 1191
1192 -def uid_optional_to_arg(arg_):
1193 """ 1194 Returns the user id of the currently logged in user. 1195 """ 1196 # Give back the current user uid when called from eg. tracker, aka. 1197 # with an empty arg. Also use the current user uid when called from 1198 # the menu with a % for the current account link. 1199 return (lib_bootstrap.user.uid if (php.empty(arg_) or arg_ == '%') else arg_)
1200 1201 1202
1203 -def page_title(account):
1204 """ 1205 Menu item title callback - use the user name if it's not the current user. 1206 """ 1207 if (account.uid == lib_bootstrap.user.uid): 1208 return lib_common.t('My account') 1209 return account.name
1210 1211 1212
1213 -def get_authmaps(authname=None):
1214 """ 1215 Discover which external authentication module(s) authenticated a username. 1216 1217 @param authname 1218 A username used by an external authentication module. 1219 @return 1220 An associative array with module as key and username as value. 1221 """ 1222 result = lib_database.query(\ 1223 "SELECT authname, module FROM {authmap} WHERE authname = '%s'", authname) 1224 authmaps = [] 1225 has_rows = False 1226 while True: 1227 authmap = lib_database.fetch_object(result) 1228 if not authmap: 1229 break 1230 authmaps[authmap.module] = authmap.authname 1231 has_rows = True 1232 return (authmaps if has_rows else 0)
1233 1234 1235
1236 -def set_authmaps(account, authmaps):
1237 """ 1238 Save mappings of which external authentication module(s) authenticated 1239 a user. Maps external usernames to user ids in the users table. 1240 1241 @param account 1242 A user object. 1243 @param authmaps 1244 An associative array with a compound key and the username as the value. 1245 The key is made up of 'authname_' plus the name of the 1246 external authentication 1247 module. 1248 @see user_external_login_register() 1249 """ 1250 for key,value in authmaps.items(): 1251 module = explode('_', key, 2) 1252 if (value): 1253 lib_database.query(\ 1254 "UPDATE {authmap} SET authname = '%s' " + \ 1255 "WHERE uid = %d AND module = '%s'", value, account.uid, module[1]) 1256 if (not lib_database.affected_rows()): 1257 lib_database.query(\ 1258 "INSERT INTO {authmap} (authname, uid, module) " + \ 1259 "VALUES ('%s', %d, '%s')", value, account.uid, module[1]) 1260 else: 1261 lib_database.query(\ 1262 "DELETE FROM {authmap} WHERE uid = %d AND module = '%s'", \ 1263 account.uid, module[1])
1264 1265 1266
1267 -def login(form_state):
1268 """ 1269 Form builder; the main user login form. 1270 1271 @ingroup forms 1272 """ 1273 php.Reference.check(form_state) 1274 # If we are already logged on, go to the user page instead. 1275 if (lib_bootstrap.user.uid): 1276 drupal_goto('user/' + lib_bootstrap.user.uid) 1277 # Display login form: 1278 form['name'] = { 1279 '#type' : 'textfield', 1280 '#title' : lib_common.t('Username'), 1281 '#size' : 60, 1282 '#maxlength' : USERNAME_MAX_LENGTH, 1283 '#required' : True, 1284 '#attributes' : {'tabindex' : '1'} 1285 } 1286 form['name']['#description'] = lib_common.t('Enter your @s username.', \ 1287 {'@s' : lib_bootstrap.variable_get('site_name', 'Drupal')}) 1288 form['pass'] = { 1289 '#type' : 'password', 1290 '#title' : lib_common.t('Password'), 1291 '#description' : lib_common.t(\ 1292 'Enter the password that accompanies your username.'), 1293 '#required' : True, 1294 '#attributes' : {'tabindex' : '2'} 1295 } 1296 form['#validate'] = login_default_validators() 1297 form['submit'] = {'#type' : 'submit', '#value' : lib_common.t('Log in'), \ 1298 '#weight' : 2, '#attributes' : {'tabindex' : '3'}} 1299 return form
1300 1301 1302
1303 -def login_default_validators():
1304 """ 1305 Set up a series for validators which check for blocked users, 1306 then authenticate against local database, then return an error if 1307 authentication fails. Distributed authentication modules are welcome 1308 to use hook_form_alter() to change this series in order to 1309 authenticate against their user database instead of the local users 1310 table. 1311 1312 We use three validators instead of one since external authentication 1313 modules usually only need to alter the second validator. 1314 1315 @see user_login_name_validate() 1316 @see user_login_authenticate_validate() 1317 @see user_login_final_validate() 1318 @return array 1319 A simple list of validate functions. 1320 """ 1321 return ('user_login_name_validate', \ 1322 'user_login_authenticate_validate', 'user_login_final_validate')
1323 1324 1325
1326 -def login_name_validate(form, form_state):
1327 """ 1328 A FAPI validate handler. Sets an error if supplied username 1329 has been blocked. 1330 """ 1331 if (php.isset(form_state['values']['name']) and \ 1332 is_blocked(form_state['values']['name'])): 1333 # Blocked in user administration. 1334 form_set_error('name', lib_common.t(\ 1335 'The username %name has not been activated or is blocked.', \ 1336 {'%name' : form_state['values']['name']}))
1337 1338
1339 -def login_authenticate_validate(form, form_state):
1340 """ 1341 A validate handler on the login form. Check supplied username/password 1342 against local users table. If successful, sets the global user object. 1343 """ 1344 authenticate(form_state['values'])
1345 1346 1347
1348 -def login_final_validate(form, form_state):
1349 """ 1350 A validate handler on the login form. Should be the last validator. Sets an 1351 error if user has not been authenticated yet. 1352 """ 1353 php.Reference.check(form_state) 1354 if (not lib_bootstrap.user.uid): 1355 form_set_error('name', \ 1356 lib_common.t(\ 1357 'Sorry, unrecognized username or password. ' + \ 1358 '<a href="@password">Have you forgotten your password?</a>', \ 1359 {'@password' : url('user/password')})) 1360 watchdog('user', 'Login attempt failed for %user.', \ 1361 {'%user' : form_state['values']['name']})
1362 1363 1364
1365 -def authenticate(form_values=[]):
1366 """ 1367 Try to log in the user locally. 1368 1369 @param form_values 1370 Form values with at least 'name' and 'pass' keys, as well as anything else 1371 which should be passed along to hook_user op 'login'. 1372 1373 @return 1374 A user object, if successful. 1375 """ 1376 password = php.trim(form_values['pass']) 1377 # Name and pass keys are required. 1378 if (not php.empty(form_values['name']) and not php.empty(password)): 1379 account = lib_database.fetch_object(\ 1380 lib_database.query(\ 1381 "SELECT * FROM {users} WHERE name = '%s' AND status = 1", \ 1382 form_values['name'])) 1383 if (account): 1384 # Allow alternate password hashing schemes. 1385 if (check_password(password, account)): 1386 if (needs_new_hash(account)): 1387 new_hash = hash_password(password) 1388 if (new_hash): 1389 lib_database.query(\ 1390 "UPDATE {users} SET pass = '%s' WHERE uid = %d", \ 1391 new_hash, account.uid) 1392 account = load({'uid' : account.uid, 'status' : 1}) 1393 lib_bootstrap.user = account 1394 authenticate_finalize(form_values) 1395 return lib_bootstrap.user
1396 1397 1398
1399 -def authenticate_finalize(edit):
1400 """ 1401 Finalize the login process. Must be called when logging in a user. 1402 1403 The function records a watchdog message about the new session, saves the 1404 login timestamp, calls hook_user op 'login' and generates a new session. 1405 1406 param edit 1407 This array is passed to hook_user op login. 1408 """ 1409 php.Reference.check(edit) 1410 watchdog('user', 'Session opened for %name.', \ 1411 {'%name' : lib_bootstrap.user.name}) 1412 # Update the user table timestamp noting user has logged in. 1413 # This is also used to invalidate one-time login links. 1414 lib_bootstrap.user.login = php.time_() 1415 lib_database.query("UPDATE {users} SET login = %d WHERE uid = %d", \ 1416 lib_bootstrap.user.login, lib_bootstrap.user.uid) 1417 plugin_invoke('login', edit, lib_bootstrap.user) 1418 lib_session.regenerate()
1419 1420 1421 1422
1423 -def form_login_submit(form, form_state):
1424 """ 1425 Submit handler for the login form. Redirects the user to a page. 1426 1427 The user is redirected to the My Account page. Setting the destination in 1428 the query string (as done by the user login block) overrides the redirect. 1429 """ 1430 php.Reference.check(form_state) 1431 if (lib_bootstrap.user.uid): 1432 form_state['redirect'] = 'user/' + lib_bootstrap.user.uid 1433 return
1434 1435 1436
1437 -def external_login_register(name, module):
1438 """ 1439 Helper function for authentication modules. Either login in or registers 1440 the current user, based on username. Either way, the global user object is 1441 populated based on name. 1442 """ 1443 existing_user = load({'name' : name}); 1444 if (php.isset(existing_user.uid)): 1445 lib_bootstrap.user = existing_user; 1446 else: 1447 # Register this new user. 1448 userinfo = { 1449 'name' : name, 1450 'pass' : password(), 1451 'init' : name, 1452 'status' : 1, 1453 'access' : php.time_() 1454 } 1455 account = save('', userinfo) 1456 # Terminate if an error occured during user_save(). 1457 if (not account): 1458 drupal_set_message(lib_common.t("Error saving user account."), 'error') 1459 return 1460 user_set_authmaps(account, {"authname_module" : name}) 1461 user = account 1462 watchdog('user', 'New external user: %name using module %module.', \ 1463 {'%name' : name, '%module' : module}, \ 1464 WATCHDOG_NOTICE, lib_common.l(lib_common.t('edit'), \ 1465 'user/' + lib_bootstrap.user.uid + '/edit'))
1466 1467 1468 1469
1470 -def pass_reset_url(account):
1471 timestamp = php.time_() 1472 return url("user/reset/account.uid/timestamp/" + \ 1473 pass_rehash(account.pass_, timestamp, account.login), {'absolute' : True})
1474 1475 1476
1477 -def pass_rehash(password, timestamp, login):
1478 return php.md5(timestamp + password + login)
1479 1480
1481 -def edit_form(form_state, uid, edit, register=False):
1482 php.Reference.check(form_state) 1483 _password_dynamic_validation() 1484 admin = access('administer users') 1485 # Account information: 1486 form['account'] = { 1487 '#type' : 'fieldset', 1488 '#title' : lib_common.t('Account information'), 1489 '#weight' : -10 1490 } 1491 if (access('change own username') or admin or register): 1492 form['account']['name'] = { 1493 '#type' : 'textfield', 1494 '#title' : t('Username'), 1495 '#default_value' : edit['name'], 1496 '#maxlength' : USERNAME_MAX_LENGTH, 1497 '#description' : lib_common.t(\ 1498 'Spaces are allowed; punctuation is not allowed except ' + \ 1499 'for periods, hyphens, apostrophes, and underscores.'), 1500 '#required' : True 1501 } 1502 form['account']['mail'] = { 1503 '#type' : 'textfield', 1504 '#title' : lib_common.t('E-mail address'), 1505 '#default_value' : edit['mail'], 1506 '#maxlength' : EMAIL_MAX_LENGTH, 1507 '#description' : lib_common.t(\ 1508 'A valid e-mail address. All e-mails from the system ' + \ 1509 'will be sent to this address. The e-mail address is not ' + \ 1510 'made public and will only be used if you wish to receive a ' + \ 1511 'new password or wish to receive certain news ' + \ 1512 'or notifications by e-mail.'), 1513 '#required' : True 1514 } 1515 if (not register): 1516 form['account']['pass'] = { 1517 '#type' : 'password_confirm', 1518 '#description' : t('To change the current user password, ' + \ 1519 'enter the new password in both fields.'), 1520 '#size' : 25 1521 } 1522 elif (not lib_bootstrap.variable_get('user_email_verification', True) or \ 1523 admin): 1524 form['account']['pass'] = { 1525 '#type' : 'password_confirm', 1526 '#description' : lib_common.t(\ 1527 'Provide a password for the new account in both fields.'), 1528 '#required' : True, 1529 '#size' : 25 1530 } 1531 if (admin): 1532 form['account']['status'] = { 1533 '#type' : 'radios', 1534 '#title' : lib_common.t('Status'), 1535 '#default_value' : (edit['status'] if php.isset(edit['status']) else 1), 1536 '#options' : (lib_common.t('Blocked'), lib_common.t('Active')) 1537 } 1538 if (access('administer permissions')): 1539 roles_ = roles(True) 1540 # The disabled checkbox subelement for the 'authenticated user' role 1541 # must be generated separately and added to the checkboxes element, 1542 # because of a limitation in D6 FormAPI not supporting a single disabled 1543 # checkbox within a set of checkboxes. 1544 # TODO: This should be solved more elegantly. See issue #119038. 1545 checkbox_authenticated = { 1546 '#type' : 'checkbox', 1547 '#title' : roles_[DRUPAL_AUTHENTICATED_RID], 1548 '#default_value' : True, 1549 '#disabled' : True 1550 } 1551 del(roles_[DRUPAL_AUTHENTICATED_RID]) 1552 if (roles_): 1553 default = ([] if php.empty(edit['roles']) else \ 1554 php.array_keys(edit['roles'])) 1555 form['account']['roles'] = { 1556 '#type' : 'checkboxes', 1557 '#title' : lib_common.t('Roles'), 1558 '#default_value' : default, 1559 '#options' : roles_, 1560 DRUPAL_AUTHENTICATED_RID : checkbox_authenticated, 1561 } 1562 # Signature: 1563 if (lib_bootstrap.variable_get('user_signatures', 0) and \ 1564 lib_plugin.exists('comment') and not register): 1565 form['signature_settings'] = { 1566 '#type' : 'fieldset', 1567 '#title' : lib_common.t('Signature settings'), 1568 '#weight' : 1 1569 } 1570 form['signature_settings']['signature'] = { 1571 '#type' : 'textarea', 1572 '#title' : lib_common.t('Signature'), 1573 '#default_value' : edit['signature'], 1574 '#description' : lib_common.t('Your signature will be ' + \ 1575 'publicly displayed at the end of your comments.'), 1576 } 1577 # Picture/avatar: 1578 if (lib_bootstrap.variable_get('user_pictures', 0) and not register): 1579 form['picture'] = {'#type' : 'fieldset', '#title' : \ 1580 lib_common.t('Picture'), '#weight' : 1} 1581 picture = lib_theme.theme('user_picture', php.object_(edit)) 1582 if (edit['picture']): 1583 form['picture']['current_picture'] = {'#markup' : picture} 1584 form['picture']['picture_delete'] = {'#type' : 'checkbox', '#title' : \ 1585 lib_common.t('Delete picture'), '#description' : \ 1586 lib_common.t('Check this box to delete your current picture.')} 1587 else: 1588 form['picture']['picture_delete'] = {'#type' : 'hidden'} 1589 form['picture']['picture_upload'] = {'#type' : 'file', '#title' : \ 1590 lib_common.t('Upload picture'), '#size' : 48, '#description' : \ 1591 lib_common.t('Your virtual face or picture. Maximum dimensions ' + \ 1592 'are %dimensions and the maximum size is %size kB.', \ 1593 {'%dimensions' : \ 1594 lib_bootstrap.variable_get('user_picture_dimensions', '85x85'), \ 1595 '%size' : lib_bootstrap.variable_get('user_picture_file_size', \ 1596 '30')}) + ' ' + lib_bootstrap.variable_get('user_picture_guidelines', \ 1597 '')} 1598 form['#validate'].append('user_validate_picture') 1599 form['#uid'] = uid 1600 return form
1601 1602 1603
1604 -def _edit_validate(uid, edit):
1605 php.Reference.check(edit) 1606 user_ = load({'uid' : uid}) 1607 # Validate the username: 1608 if (access('change own username') or access('administer users') or \ 1609 not user_.uid): 1610 error = validate_name(edit['name']) 1611 if error: 1612 lib_form.set_error('name', error) 1613 elif (lib_database.result(\ 1614 lib_database.query(\ 1615 "SELECT COUNT(*) FROM {users} " + \ 1616 "WHERE uid != %d AND LOWER(name) = LOWER('%s')", uid, \ 1617 edit['name'])) > 0): 1618 lib_form.set_error('name', \ 1619 lib_common.t('The name %name is already taken.', \ 1620 {'%name' : edit['name']})) 1621 # Validate the e-mail address: 1622 error = validate_mail(edit['mail']) 1623 if error: 1624 lib_form.set_error('mail', error) 1625 elif (lib_database.result(lib_database.query(\ 1626 "SELECT COUNT(*) FROM {users} " + \ 1627 "WHERE uid != %d AND LOWER(mail) = LOWER('%s')", uid, \ 1628 edit['mail'])) > 0): 1629 lib_form.set_error('mail', \ 1630 lib_common.t('The e-mail address %email is already registered. ' + \ 1631 '<a href="@password">Have you forgotten your password?</a>', \ 1632 {'%email' : edit['mail'], '@password' : url('user/password')}))
1633 1634 1635 1636
1637 -def _edit_submit(uid, edit):
1638 php.Reference.check(edit) 1639 user_ = load({'uid' : uid}) 1640 # Delete picture if requested, and if no replacement picture was given. 1641 if (not php.empty(edit['picture_delete'])): 1642 if (user_.picture and lib_file.exists(user_.picture)): 1643 lib_file.delete(user_.picture) 1644 edit['picture'] = '' 1645 if (php.isset(edit['roles'])): 1646 edit['roles'] = php.array_filter(edit['roles'])
1647 1648 1649
1650 -def delete(edit, uid):
1651 """ 1652 Delete a user. 1653 1654 @param edit An array of submitted form values. 1655 @param uid The user ID of the user to delete. 1656 """ 1657 account = load({'uid' : uid}) 1658 lib_session.destroy_uid(uid) 1659 _mail_notify('status_deleted', account) 1660 lib_plugin.invoke_all('user', 'delete', edit, account) 1661 lib_database.query('DELETE FROM {users} WHERE uid = %d', uid) 1662 lib_database.query('DELETE FROM {users_roles} WHERE uid = %d', uid) 1663 lib_database.query('DELETE FROM {authmap} WHERE uid = %d', uid) 1664 variables = {'%name' : account.name, '%email' : '<' + account.mail + '>'} 1665 watchdog('user', 'Deleted user: %name %email.', variables, WATCHDOG_NOTICE) 1666 lib_plugin.invoke_all('user', 'delete', edit, account)
1667 1668 1669
1670 -def build_content(account):
1671 """ 1672 Builds a structured array representing the profile content. 1673 1674 @param account 1675 A user object. 1676 1677 @return 1678 A structured array containing the individual elements of the profile. 1679 """ 1680 php.Reference.check(account) 1681 edit = None 1682 plugin_invoke('view', edit, account) 1683 # Allow modules to modify the fully-built profile. 1684 drupal_alter('profile', account) 1685 return account.content
1686 1687 1688
1689 -def hook_mail(key, message, params):
1690 """ 1691 Implementation of hook_mail(). 1692 """ 1693 php.Reference.check(message) 1694 language = message['language'] 1695 variables = mail_tokens(params['account'], language) 1696 message['subject'] += _mail_text(key + '_subject', language, variables) 1697 message['body'].append( _mail_text(key + '_body', language, variables) )
1698 1699
1700 -def _mail_text(key, language=None, variables=[]):
1701 """ 1702 Returns a mail string for a variable name. 1703 1704 Used by user_mail() and the settings forms to retrieve strings. 1705 """ 1706 langcode = (language.language if php.isset(language) else None) 1707 admin_setting = lib_bootstrap.variable_get('user_mail_' + key, False) 1708 if admin_setting: 1709 # An admin setting overrides the default string. 1710 return php.strtr(admin_setting, variables) 1711 else: 1712 # No override, return default string. 1713 if key == 'register_no_approval_required_subject': 1714 return t('Account details for not username at not site', \ 1715 variables, langcode) 1716 elif key == 'register_no_approval_required_body': 1717 return lib_common.t("not username,\n\nThank you for " + \ 1718 "registering at not site. " + \ 1719 "You may now log in to not login_uri using the following " + \ 1720 "username and password:\n\nusername: not username\npassword: " + \ 1721 "not password\n\nYou may also log in by clicking on " + \ 1722 "this link or copying and pasting it in your browser:\n\nnot " + \ 1723 "login_url\n\nThis is a one-time login, so it can be used " + \ 1724 "only once.\n\nAfter logging in, you will be redirected to " + \ 1725 "not edit_uri so you can change your password.\n\n\n-- " + \ 1726 "not site team", variables, langcode) 1727 elif key == 'register_admin_created_subject': 1728 return lib_common.t('An administrator created an account ' + \ 1729 'for you at not site', variables, langcode) 1730 elif key == 'register_admin_created_body': 1731 return lib_common.t("not username,\n\nA site administrator at " + \ 1732 "not site has created an account for you. You may now log " + \ 1733 "in to not login_uri using the following username " + \ 1734 "and password:\n\nusername: not username\npassword: " + \ 1735 "not password\n\nYou may also log in by clicking on this " + \ 1736 "link or copying and pasting it in your browser:\n\nnot " + \ 1737 "login_url\n\nThis is a one-time login, so it can be " + \ 1738 "used only once.\n\nAfter logging in, you will be redirected to " + \ 1739 "not edit_uri so you can change your password.\n\n\n-- " + \ 1740 "not site team", variables, langcode) 1741 elif \ 1742 key == 'register_pending_approval_subject' or \ 1743 key == 'register_pending_approval_admin_subject': 1744 return lib_common.t('Account details for not username at not ' + \ 1745 'site (pending admin approval)', variables, langcode) 1746 elif key == 'register_pending_approval_body': 1747 return lib_common.t("not username,\n\nThank you for registering at " + \ 1748 "!site + Your application for an account is currently pending " + \ 1749 "approval. Once it has been approved, you will receive " + \ 1750 "another e-mail containing information about how to log in, " + \ 1751 "set your password, and other " + \ 1752 "details.\n\n\n-- not site team", variables, langcode) 1753 elif key == 'register_pending_approval_admin_body': 1754 return lib_common.t("not username has applied for an " + \ 1755 "account.\n\nnot edit_uri", variables, langcode) 1756 elif key == 'password_reset_subject': 1757 return lib_common.t('Replacement login information for ' + \ 1758 'not username at not site', variables, langcode) 1759 elif key == 'password_reset_body': 1760 return lib_common.t("not username,\n\nA request to reset the " + \ 1761 "password for your account has been made at not site.\n\nYou may " + \ 1762 "now log in to not uri_brief by clicking on this link or copying " + \ 1763 "and pasting it in your browser:\n\nnot login_url\n\nThis " + \ 1764 "is a one-time login, so it can be used only once. " + \ 1765 "It expires after one day and nothing will happen if it's not " + \ 1766 "used.\n\nAfter logging in, you will be redirected to !edit_uri " + \ 1767 "so you can change your password.", variables, langcode) 1768 elif key == 'status_activated_subject': 1769 return lib_common.t('Account details for not username at !site ' + \ 1770 '(approved)', variables, langcode) 1771 elif key == 'status_activated_body': 1772 return lib_common.t("not username,\n\nYour account at !site " + \ 1773 "has been activated.\n\nYou may now log in by clicking on this " + \ 1774 "link or copying and pasting it in your browser:\n\n" + \ 1775 "!login_url\n\nThis is a one-time login, so it can be used only " + \ 1776 "once.\n\nAfter logging in, you will be redirected to " + \ 1777 "!edit_uri so you can change your password.\n\nOnce you " + \ 1778 "have set your own password, you will be able to log in to " + \ 1779 "!login_uri in the future using:\n\nusername: not username\n", \ 1780 variables, langcode) 1781 elif key == 'status_blocked_subject': 1782 return lib_common.t('Account details for not username at ' + \ 1783 '!site (blocked)', variables, langcode) 1784 elif key == 'status_blocked_body': 1785 return lib_common.t("not username,\n\nYour account on !site has " + \ 1786 "been blocked.", variables, langcode) 1787 elif key == 'status_deleted_subject': 1788 return lib_common.t('Account details for not username at ' + \ 1789 '!site (deleted)', variables, langcode) 1790 elif key == 'status_deleted_body': 1791 return lib_common.t("not username,\n\nYour account on not " + \ 1792 "site has been deleted.", variables, langcode)
1793 1794 1795
1796 -def roles(membersonly=False, permission=None):
1797 """ 1798 Retrieve an array of roles matching specified conditions. 1799 1800 @param membersonly 1801 Set this to True to exclude the 'anonymous' role. 1802 @param permission 1803 A string containing a permission. If set, only roles containing that 1804 permission are returned. 1805 1806 @return 1807 An associative array with the role id as the key and the role name as 1808 value. 1809 """ 1810 # System roles take the first two positions. 1811 roles_ = { 1812 DRUPAL_ANONYMOUS_RID : None, 1813 DRUPAL_AUTHENTICATED_RID : None 1814 } 1815 if (not php.empty(permission)): 1816 result = lib_database.query(\ 1817 "SELECT r.* FROM {role} r " + \ 1818 "INNER JOIN {role_permission} p ON r.rid = p.rid " + \ 1819 "WHERE p.permission = '%s' ORDER BY r.name", permission) 1820 else: 1821 result = lib_database.query('SELECT * FROM {role} ORDER BY name') 1822 while True: 1823 role = lib_database.fetch_object(result) 1824 # We only translate the built in role names 1825 if role.rid == DRUPAL_ANONYMOUS_RID: 1826 if (not membersonly): 1827 roles_[role.rid] = lib_common.t(role.name) 1828 elif role.rid == DRUPAL_AUTHENTICATED_RID: 1829 roles_[role.rid] = lib_common.t(role.name) 1830 else: 1831 roles_[role.rid] = role.name 1832 # Filter to remove unmatched system roles. 1833 return php.array_filter(roles_)
1834 1835 1836
1837 -def hook_user_operations(form_state=[]):
1838 """ 1839 Implementation of hook_user_operations(). 1840 """ 1841 operations = { 1842 'unblock' : { 1843 'label' : lib_common.t('Unblock the selected users'), 1844 'callback' : 'user_user_operations_unblock' 1845 }, 1846 'block' : { 1847 'label' : lib_common.t('Block the selected users'), 1848 'callback' : 'user_user_operations_block' 1849 }, 1850 'delete' : { 1851 'label' : lib_common.t('Delete the selected users') 1852 }, 1853 } 1854 if (access('administer permissions')): 1855 roles_ = roles(True) 1856 del(roles_[DRUPAL_AUTHENTICATED_RID]); # Can't edit authenticated role. 1857 add_roles = [] 1858 for key,value in roles_.items(): 1859 add_roles['add_role-' + key] = value 1860 remove_roles = [] 1861 for key,value in roles_.items(): 1862 remove_roles['remove_role-' + key] = value 1863 if (php.count(roles_) > 0): 1864 role_operations = { 1865 lib_common.t('Add a role to the selected users') : { 1866 'label' : add_roles 1867 }, 1868 lib_common.t('Remove a role from the selected users') : { 1869 'label' : remove_roles 1870 }, 1871 } 1872 operations += role_operations 1873 # If the form has been posted, we need to insert the proper data for 1874 # role editing if necessary. 1875 if (not php.empty(form_state['submitted'])): 1876 operation_rid = php.explode('-', form_state['values']['operation']) 1877 operation = operation_rid[0] 1878 if (operation == 'add_role' or operation == 'remove_role'): 1879 rid = operation_rid[1] 1880 if (access('administer permissions')): 1881 operations[form_state['values']['operation']] = { 1882 'callback' : 'user_multiple_role_edit', 1883 'callback arguments' : (operation, rid), 1884 } 1885 else: 1886 watchdog('security', 'Detected malicious attempt to ' + \ 1887 'alter protected user fields.', tuple(), WATCHDOG_WARNING) 1888 return 1889 return operations
1890 1891 1892
1893 -def user_operations_unblock(accounts):
1894 """ 1895 Callback function for admin mass unblocking users. 1896 """ 1897 for uid in accounts: 1898 account = load({'uid' : int(uid)}) 1899 # Skip unblocking user if they are already unblocked. 1900 if (account != False and account.status == 0): 1901 save(account, {'status' : 1})
1902 1903 1904
1905 -def user_operations_block(accounts):
1906 """ 1907 Callback function for admin mass blocking users. 1908 """ 1909 for uid in accounts: 1910 account = load({'uid' : int(uid)}) 1911 # Skip blocking user if they are already blocked. 1912 if (account != False and account.status == 1): 1913 save(account, {'status' : 0})
1914 1915 1916
1917 -def multiple_role_edit(accounts, operation, rid):
1918 """ 1919 Callback function for admin mass adding/deleting a user role. 1920 """ 1921 # The role name is not necessary as user_save() will reload the user 1922 # object, but some modules' hook_user() may look at this first. 1923 role_name = lib_database.result( 1924 lib_database.query('SELECT name FROM {role} WHERE rid = %d', rid)) 1925 if operation == 'add_role': 1926 for uid in accounts: 1927 account = load({'uid' : int(uid)}) 1928 # Skip adding the role to the user if they already have it. 1929 if (account != False and not php.isset(account.roles[rid])): 1930 roles_ = account.roles + {rid : role_name} 1931 save(account, {'roles' : roles_}) 1932 elif operation == 'remove_role': 1933 for uid in accounts: 1934 account = load({'uid' : int(uid)}) 1935 # Skip removing the role from the user if they already don't have it. 1936 if (account != False and php.isset(account.roles[rid])): 1937 roles_ = php.array_diff(account.roles, {rid : role_name}) 1938 save(account, {'roles' : roles})
1939 1940 1941
1942 -def multiple_delete_confirm(form_state):
1943 php.Reference.check(form_state) 1944 edit = form_state['post'] 1945 form['accounts'] = {'#prefix' : '<ul>', '#suffix' : '</ul>', '#tree' : True} 1946 # array_filter() returns only elements with True values. 1947 for uid,value in array_filter(edit['accounts']).items(): 1948 user_ = lib_database.result(\ 1949 lib_database.query('SELECT name FROM {users} WHERE uid = %d', uid)) 1950 form['accounts'][uid] = {'#type' : 'hidden', '#value' : uid, \ 1951 '#prefix' : '<li>', '#suffix' : check_plain(user_) + "</li>\n"} 1952 form['operation'] = {'#type' : 'hidden', '#value' : 'delete'} 1953 return confirm_form(form, \ 1954 lib_common.t('Are you sure you want to delete these users?'), \ 1955 'admin/user/user', lib_common.t('This action cannot be undone.'), \ 1956 lib_common.t('Delete all'), lib_common.t('Cancel'))
1957 1958 1959 1960
1961 -def multiple_delete_confirm_submit(form, form_state):
1962 php.Reference.check(form_state) 1963 if (form_state['values']['confirm']): 1964 for uid, value in form_state['values']['accounts'].items(): 1965 delete(form_state['values'], uid) 1966 drupal_set_message(lib_common.t('The users have been deleted.')) 1967 form_state['redirect'] = 'admin/user/user' 1968 return
1969 1970
1971 -def hook_help(path, arg):
1972 """ 1973 Implementation of hook_help(). 1974 """ 1975 if path == 'admin/help#user': 1976 output = '<p>' + lib_common.t('The user module allows users to ' + \ 1977 'register, login, and log out + Users benefit from being able ' + \ 1978 'to sign on because it associates content they create with their ' + \ 1979 'account and allows various permissions to be set for their roles. ' + \ 1980 'The user module supports user roles which establish fine ' + \ 1981 'grained permissions allowing each role to do only what ' + \ 1982 'the administrator wants them to. Each user is assigned to ' + \ 1983 'one or more roles. By default there are two roles ' + \ 1984 '<em>anonymous</em> - a user who has not logged in, and ' + \ 1985 '<em>authenticated</em> a user who has signed up and who ' + \ 1986 'has been authorized.') + '</p>' 1987 output += '<p>' + lib_common.t("Users can use their own name or " + \ 1988 "handle and can specify personal configuration settings through " + \ 1989 "their individual <em>My account</em> page + Users must " + \ 1990 "authenticate by supplying a local username and password or " + \ 1991 "through their OpenID, an optional and secure method for " + \ 1992 "logging into many websites with a single username and password. " + \ 1993 "In some configurations, users may authenticate using a username " + \ 1994 "and password from another Drupal site, or through some " + \ 1995 "other site-specific mechanism.") + '</p>' 1996 output += '<p>' + lib_common.t('A visitor accessing your website ' + \ 1997 'is assigned a unique ID, or session ID, which is stored in a ' + \ 1998 'cookie + The cookie does not contain personal information, but ' + \ 1999 'acts as a key to retrieve information from your site. ' + \ 2000 'Users should have cookies enabled in their web browser ' + \ 2001 'when using your site.') + '</p>' 2002 output += '<p>' + lib_common.t('For more information, see the online ' + \ 2003 'handbook entry for <a href="@user">User module</a>.', \ 2004 {'@user' : 'http://drupal.org/handbook/modules/user/'}) + '</p>' 2005 return output 2006 elif path == 'admin/user/user': 2007 return '<p>' + lib_common.t('Drupal allows users to register, login, ' + \ 2008 'log out, maintain user profiles, etc + Users of the site ' + \ 2009 'may not use their own names to post content until they have ' + \ 2010 'signed up for a user account.') + '</p>' 2011 elif \ 2012 path == 'admin/user/user/create' or \ 2013 path == 'admin/user/user/account/create': 2014 return '<p>' + lib_common.t("This web page allows administrators " + \ 2015 "to register new users. Users' e-mail addresses and " + \ 2016 "usernames must be unique.") + '</p>' 2017 elif path == 'admin/user/permissions': 2018 return '<p>' + lib_common.t('Permissions let you control what users ' + \ 2019 'can do on your site + Each user role (defined on the ' + \ 2020 '<a href="@role">user roles page</a>) has its own set of ' + \ 2021 'permissions. For example, you could give users classified ' + \ 2022 'as "Administrators" permission to "administer nodes" but deny ' + \ 2023 'this power to ordinary, "authenticated" users. You can use ' + \ 2024 'permissions to reveal new features to privileged users ' + \ 2025 '(those with subscriptions, for example). Permissions also allow '+ \ 2026 'trusted users to share the administrative burden of ' + \ 2027 'running a busy site.', {'@role' : url('admin/user/roles')}) + '</p>' 2028 elif path == 'admin/user/roles': 2029 return lib_common.t('<p>Roles allow you to fine tune the security and ' + \ 2030 'administration of Drupal. A role defines a group of users ' + \ 2031 'that have certain privileges as defined in ' + \ 2032 '<a href="@permissions">user permissions</a>. Examples of ' + \ 2033 'roles include: anonymous user, authenticated user, moderator, ' + \ 2034 'administrator and so on. In this area you will define the ' + \ 2035 '<em>role names</em> of the various roles. To delete a role ' + \ 2036 'choose "edit".</p><p>By default, Drupal comes with two user ' + \ 2037 'roles:</p><ul>' + \ 2038 '<li>Anonymous user: this role is used for users that don\'t have ' + \ 2039 'a user account or that are not authenticated.</li>' + \ 2040 '<li>Authenticated user: this role is automatically granted ' + \ 2041 'to all logged in users.</li></ul>', \ 2042 {'@permissions' : url('admin/user/permissions')}) 2043 elif path == 'admin/user/search': 2044 return '<p>' + lib_common.t('Enter a simple pattern ("*" may be ' + \ 2045 'used as a wildcard match) to search for a username or ' + \ 2046 'e-mail address + For example, one may search for "br" ' + \ 2047 'and Drupal might return "brian", "brad", and ' + \ 2048 '"brenda@example.com".') + '</p>'
2049 2050 2051
2052 -def _categories(account):
2053 """ 2054 Retrieve a list of all user setting/information categories 2055 and sort them by weight. 2056 """ 2057 categories = [] 2058 for plugin in lib_plugin.list_(): 2059 data = lib_plugin.invoke(plugin, 'user', 'categories', None, account, '') 2060 if data: 2061 categories = php.array_merge(data, categories) 2062 php.usort(categories, _sort) 2063 return categories
2064 2065 2066
2067 -def _sort(a, b):
2068 a = php.array_(a) + {'weight' : 0, 'title' : ''} 2069 b = php.array(b) + {'weight' : 0, 'title' : ''} 2070 return (-1 if (a['weight'] < b['weight']) else \ 2071 (1 if (a['weight'] > b['weight']) else \ 2072 (-1 if (a['title'] < b['title']) else 1)))
2073 2074
2075 -def filters():
2076 """ 2077 List user administration filters that can be applied. 2078 """ 2079 # Regular filters 2080 filters_ = [] 2081 roles_ = roles(True) 2082 del(roles_[DRUPAL_AUTHENTICATED_RID]); # Don't list authorized role. 2083 if (php.count(roles_)): 2084 filters_['role'] = { 2085 'title' : lib_common.t('role'), 2086 'where' : "ur.rid = %d", 2087 'options' : roles_, 2088 'join' : '' 2089 } 2090 options = [] 2091 for plugin in lib_plugin.list_(): 2092 permissions = lib_plugin.invoke(plugin, 'perm') 2093 if permissions: 2094 php.asort(permissions) 2095 for permission,description in permissions.items(): 2096 options[t('@module module', {'@module' : module})][permission] = \ 2097 lib_common.t(permission) 2098 php.ksort(options) 2099 filters_['permission'] = { 2100 'title' : lib_common.t('permission'), 2101 'join' : 'LEFT JOIN {role_permission} p ON ur.rid = p.rid', 2102 'where' : " (p.permission = '%s' OR u.uid = 1) ", 2103 'options' : options 2104 } 2105 filters_['status'] = { 2106 'title' : lib_common.t('status'), 2107 'where' : 'u.status = %d', 2108 'join' : '', 2109 'options' : {1 : lib_common.t('active'), 0 : lib_common.t('blocked')} 2110 } 2111 return filters_
2112 2113 2114
2115 -def build_filter_query():
2116 """ 2117 Build query for user administration filters based on session. 2118 """ 2119 filters_ = filters() 2120 # Build query 2121 where = args = join_ = [] 2122 for filter_ in php.SESSION['user_overview_filter']: 2123 key,value = filter_ 2124 # This checks to see if this permission filter is an enabled permission for 2125 # the authenticated role. If so, then all users would be listed, and we can 2126 # skip adding it to the filter query. 2127 if (key == 'permission'): 2128 account = php.stdClass() 2129 account.uid = 'user_filter' 2130 account.roles = {DRUPAL_AUTHENTICATED_RID : 1} 2131 if (access(value, account)): 2132 continue 2133 where.append( filters[key]['where'] ) 2134 args.append( value ) 2135 join_.append( filters[key]['join'] ) 2136 where = ('AND ' + php.implode(' AND ', where) if \ 2137 (not php.empty(where)) else '' ) 2138 join_ = (' ' + php.implode(' ', php.array_unique(join_)) if \ 2139 (not php.empty(join_)) else '') 2140 return { 2141 'where' : where, 2142 'join' : join_, 2143 'args' : args 2144 }
2145 2146
2147 -def hook_forms():
2148 """ 2149 Implementation of hook_forms(). 2150 """ 2151 forms['user_admin_access_add_form']['callback'] = 'user_admin_access_form' 2152 forms['user_admin_access_edit_form']['callback'] = 'user_admin_access_form' 2153 forms['user_admin_new_role']['callback'] = 'user_admin_role' 2154 return forms
2155 2156 2157
2158 -def comment(comment_, op):
2159 """ 2160 Implementation of hook_comment(). 2161 """ 2162 php.Reference.check(comment_) 2163 # Validate signature. 2164 if (op == 'view'): 2165 if (lib_bootstrap.variable_get('user_signatures', 0) and \ 2166 not php.empty(comment_.signature)): 2167 comment_.signature = check_markup(comment_.signature, comment_.format) 2168 else: 2169 comment_.signature = ''
2170 2171 2172
2173 -def theme_signature(signature):
2174 """ 2175 Theme output of user signature. 2176 2177 @ingroup themeable 2178 """ 2179 output = '' 2180 if (signature): 2181 output += '<div class="clear">' 2182 output +='<div>--</div>' 2183 output += signature 2184 output += '</div>' 2185 return output
2186 2187 2188
2189 -def mail_tokens(account, language):
2190 """ 2191 Return an array of token to value mappings for user e-mail messages. 2192 2193 @param account 2194 The user object of the account being notified. Must contain at 2195 least the fields 'uid', 'name', and 'mail'. 2196 @param language 2197 Language object to generate the tokens with. 2198 @return 2199 Array of mappings from token names to values (for use with strtr()). 2200 """ 2201 tokens = { 2202 'not username' : account.name, 2203 'not site' : lib_bootstrap.variable_get('site_name', 'Drupal'), 2204 'not login_url' : pass_reset_url(account), 2205 'not uri' : settings.base_url, 2206 'not uri_brief' : php.substr(settings.base_url, php.strlen('http://')), 2207 'not mailto' : account.mail, 2208 'not date' : format_date(php.time_(), 'medium', '', None, \ 2209 language.language), 2210 'not login_uri' : url('user', {'absolute' : True, 'language' : language}), 2211 'not edit_uri' : url('user/' + account.uid + '/edit', \ 2212 {'absolute' : True, 'language' : language}) 2213 } 2214 if (not php.empty(account.password)): 2215 tokens['not password'] = account.password 2216 return tokens
2217 2218 2219
2220 -def preferred_language(account, default=None):
2221 """ 2222 Get the language object preferred by the user. This user preference can 2223 be set on the user account editing page, and is only available if there 2224 are more than one languages enabled on the site. If the user did not 2225 choose a preferred language, or is the anonymous user, the default 2226 value, or if it is not set, the site default language will be returned. 2227 2228 @param account 2229 User account to look up language for. 2230 @param default 2231 Optional default language object to return if the account 2232 has no valid language. 2233 """ 2234 language_list = lib_language.list_() 2235 if (account.language and php.isset(language_list[account.language])): 2236 return language_list[account.language] 2237 else: 2238 return (default if default else language_default())
2239 2240
2241 -def _mail_notify(op, account, language=None):
2242 """ 2243 Conditionally create and send a notification email when a certain 2244 operation happens on the given user account. 2245 2246 @see user_mail_tokens() 2247 @see drupal_mail() 2248 2249 @param op 2250 The operation being performed on the account. Possible values: 2251 'register_admin_created': Welcome message for user created by the admin 2252 'register_no_approval_required': Welcome message when user self-registers 2253 'register_pending_approval': Welcome message, user pending admin approval 2254 'password_reset': Password recovery request 2255 'status_activated': Account activated 2256 'status_blocked': Account blocked 2257 'status_deleted': Account deleted 2258 2259 @param account 2260 The user object of the account being notified. Must contain at 2261 least the fields 'uid', 'name', and 'mail'. 2262 @param language 2263 Optional language to use for the notification, overriding account language. 2264 @return 2265 The return value from drupal_mail_send(), if ends up being called. 2266 """ 2267 # By default, we always notify except for deleted and blocked. 2268 default_notify = (op != 'status_deleted' and op != 'status_blocked') 2269 notify = variable_get('user_mail_' + op + '_notify', default_notify) 2270 if (notify): 2271 params['account'] = account 2272 language = (language if language else user_preferred_language(account)) 2273 mail = drupal_mail('user', op, account.mail, language, params) 2274 if (op == 'register_pending_approval'): 2275 # If a user registered requiring admin approval, notify the admin, too. 2276 # We use the site default language for this. 2277 drupal_mail('user', 'register_pending_approval_admin', \ 2278 lib_bootstrap.variable_get('site_mail', ini_get('sendmail_from')), \ 2279 lib_language.default(), params) 2280 return (None if php.empty(mail) else mail['result'])
2281 2282 2283
2284 -def _password_dynamic_validation():
2285 """ 2286 Add javascript and string translations for dynamic password validation 2287 (strength and confirmation checking). 2288 2289 This is an internal function that makes it easier to manage the translation 2290 strings that need to be passed to the javascript code. 2291 """ 2292 php.static(_password_dynamic_validation, 'complete', False) 2293 # Only need to do once per page. 2294 if (not complete): 2295 drupal_add_js(drupal_get_path('module', 'user') + '/user.js', 'module') 2296 drupal_add_js({ 2297 'password' : { 2298 'strengthTitle' : lib_common.t('Password strength:'), \ 2299 'lowStrength' : lib_common.t('Low'), \ 2300 'mediumStrength' : lib_common.t('Medium'), \ 2301 'highStrength' : lib_common.t('High'), \ 2302 'tooShort' : lib_common.t('It is recommended to choose a ' + \ 2303 'password that contains at least six characters. ' + \ 2304 'It should include numbers, punctuation, and both ' + \ 2305 'upper and lowercase letters.'), \ 2306 'needsMoreVariation' : lib_common.t('The password does not ' + \ 2307 'include enough variation to be secure + Try:'), \ 2308 'addLetters' : lib_common.t('Adding both upper and ' + \ 2309 'lowercase letters.'), \ 2310 'addNumbers' : lib_common.t('Adding numbers.'), \ 2311 'addPunctuation' : lib_common.t('Adding punctuation.'), \ 2312 'sameAsUsername' : lib_common.t('It is recommended to choose ' + \ 2313 'a password different from the username.'), \ 2314 'confirmSuccess' : lib_common.t('Yes'), \ 2315 'confirmFailure' : lib_common.t('No'), \ 2316 'confirmTitle' : lib_common.t('Passwords match:'), \ 2317 'username' : (user.name if php.isset(user.name) else '')}}, \ 2318 'setting') 2319 complete = True
2320 2321
2322 -def hook_hook_info():
2323 """ 2324 Implementation of hook_hook_info(). 2325 """ 2326 return { 2327 'user' : { 2328 'user' : { 2329 'insert' : { 2330 'runs when' : lib_common.t('After a user account has been created'), 2331 }, 2332 'update' : { 2333 'runs when' : \ 2334 lib_common.t("After a user's profile has been updated"), 2335 }, 2336 'delete' : { 2337 'runs when' : lib_common.t('After a user has been deleted') 2338 }, 2339 'login' : { 2340 'runs when' : lib_common.t('After a user has logged in') 2341 }, 2342 'logout' : { 2343 'runs when' : lib_common.t('After a user has logged out') 2344 }, 2345 'view' : { 2346 'runs when' : lib_common.t("When a user's profile is being viewed") 2347 } 2348 } 2349 } 2350 }
2351 2352 2353
2354 -def hook_action_info():
2355 """ 2356 Implementation of hook_action_info(). 2357 """ 2358 return { 2359 'user_block_user_action' : { 2360 'description' : lib_common.t('Block current user'), 2361 'type' : 'user', 2362 'configurable' : False, 2363 'hooks' : tuple() 2364 }, 2365 }
2366 2367 2368
2369 -def block_user_action(object_, context=[]):
2370 """ 2371 Implementation of a Drupal action. 2372 Blocks the current user. 2373 """ 2374 php.Reference.check(object_) 2375 if (php.isset(object_.uid)): 2376 uid = object_.uid 2377 elif (isset(context['uid'])): 2378 uid = context['uid'] 2379 else: 2380 uid = user.uid 2381 lib_database.query("UPDATE {users} SET status = 0 WHERE uid = %d", uid) 2382 lib_session.destroy_uid(uid) 2383 watchdog('action', 'Blocked user %name.', \ 2384 {'%name' : check_plain(user.name)})
2385 2386 2387
2388 -def form_register_submit(form, form_state):
2389 """ 2390 Submit handler for the user registration form. 2391 2392 This function is shared by the installation form and 2393 the normal registration form, 2394 which is why it can't be in the user.pages.inc file. 2395 """ 2396 php.Reference.check(form_state) 2397 admin = access('administer users') 2398 mail = form_state['values']['mail'] 2399 name = form_state['values']['name'] 2400 if (not lib_bootstrap.variable_get('user_email_verification', True) or \ 2401 admin): 2402 pass_ = form_state['values']['pass'] 2403 else: 2404 pass_ = password() 2405 notify = (form_state['values']['notify'] if \ 2406 php.isset(form_state['values']['notify']) else None) 2407 from_ = lib_bootstrap.variable_get('site_mail', ini_get('sendmail_from')) 2408 if (php.isset(form_state['values']['roles'])): 2409 # Remove unset roles. 2410 roles_ = php.array_filter(form_state['values']['roles']) 2411 else: 2412 roles_ = [] 2413 if (not admin and php.array_intersect(\ 2414 php.array_keys(form_state['values']), \ 2415 ('uid', 'roles', 'init', 'session', 'status'))): 2416 watchdog('security', 'Detected malicious attempt to alter ' + \ 2417 'protected user fields.', tuple(), WATCHDOG_WARNING) 2418 form_state['redirect'] = 'user/register' 2419 return 2420 # The unset below is needed to prevent these form values from being saved as 2421 # user data. 2422 del(form_state['values']['form_token'], \ 2423 form_state['values']['submit'], form_state['values']['op'], \ 2424 form_state['values']['notify'], form_state['values']['form_id'], \ 2425 form_state['values']['affiliates'], form_state['values']['destination'], \ 2426 form_state['values']['form_build_id']) 2427 merge_data = {'pass' : pass_, 'init' : mail, 'roles' : roles} 2428 if (not admin): 2429 # Set the user's status because it was not displayed in the form. 2430 merge_data['status'] = \ 2431 (lib_bootstrap.variable_get('user_register', 1) == 1) 2432 account = save('', php.array_merge(form_state['values'], merge_data)) 2433 # Terminate if an error occured during user_save(). 2434 if (not account): 2435 drupal_set_message(t("Error saving user account."), 'error') 2436 form_state['redirect'] = '' 2437 return 2438 form_state['user'] = account 2439 watchdog('user', 'New user: %name (%email).', \ 2440 {'%name' : name, '%email' : mail}, WATCHDOG_NOTICE, \ 2441 lib_common.l(lib_common.t('edit'), 'user/' + account.uid + '/edit')) 2442 # The first user may login immediately, and receives 2443 # a customized welcome e-mail. 2444 if (account.uid == 1): 2445 drupal_set_message(lib_common.t('Welcome to Drupal. ' + \ 2446 'You are now logged in as user #1, ' + \ 2447 'which gives you full control over your website.')) 2448 if (lib_bootstrap.variable_get('user_email_verification', True)): 2449 drupal_set_message(lib_common.t('</p><p> Your password is ' + \ 2450 '<strong>%pass</strong> + You may change your password ' + \ 2451 'below.</p>', {'%pass' : pass_})) 2452 user_authenticate(php.array_merge(form_state['values'], merge_data)) 2453 form_state['redirect'] = 'user/1/edit' 2454 return 2455 else: 2456 # Add plain text password into user account to generate mail tokens. 2457 account.password = pass_ 2458 if (admin and not notify): 2459 drupal_set_message(lib_common.t('Created a new user account ' + \ 2460 'for <a href="@url">%name</a> + No e-mail has been sent.', \ 2461 {'@url' : url("user/account.uid"), '%name' : account.name})) 2462 elif (not variable_get('user_email_verification', True) and \ 2463 account.status and not admin): 2464 # No e-mail verification is required, create new user account, and login 2465 # user immediately. 2466 _mail_notify('register_no_approval_required', account) 2467 if (authenticate(php.array_merge(form_state['values'], merge_data))): 2468 drupal_set_message(lib_common.t(\ 2469 'Registration successful + You are now logged in.')) 2470 form_state['redirect'] = '' 2471 return 2472 elif (account.status or notify): 2473 # Create new user account, no administrator approval required. 2474 op = ('register_admin_created' if \ 2475 notify else 'register_no_approval_required') 2476 _mail_notify(op, account) 2477 if (notify): 2478 drupal_set_message(lib_common.t('Password and further ' + \ 2479 'instructions have been e-mailed to the ' + \ 2480 'new user <a href="@url">%name</a>.', \ 2481 {'@url' : url("user/account.uid"), '%name' : account.name})) 2482 else: 2483 drupal_set_message(lib_common.t('Your password and further ' + \ 2484 'instructions have been sent to your e-mail address.')) 2485 form_state['redirect'] = '' 2486 return 2487 else: 2488 # Create new user account, administrator approval required. 2489 _mail_notify('register_pending_approval', account) 2490 drupal_set_message(lib_common.t('Thank you for applying ' + \ 2491 'for an account + Your account is currently pending ' + \ 2492 'approval by the site administrator.<br />In the ' + \ 2493 'meantime, a welcome message with further instructions ' + \ 2494 'has been sent to your e-mail address.')) 2495 form_state['redirect'] = '' 2496 return
2497 2498
2499 -def register():
2500 """ 2501 Form builder; The user registration form. 2502 2503 @ingroup forms 2504 @see user_register_validate() 2505 @see user_register_submit() 2506 """ 2507 admin = access('administer users') 2508 # If we aren't admin but already logged on, go to the user page instead. 2509 if (not admin and user.uid): 2510 drupal_goto('user/' + user.uid) 2511 form = [] 2512 # Display the registration form. 2513 if (not admin): 2514 form['user_registration_help'] = \ 2515 {'#markup' : filter_xss_admin(\ 2516 lib_bootstrap.variable_get('user_registration_help', ''))} 2517 # Merge in the default user edit fields. 2518 form = php.array_merge(form, user_edit_form(form_state, None, None, True)) 2519 if (admin): 2520 form['account']['notify'] = { 2521 '#type' : 'checkbox', 2522 '#title' : lib_common.t('Notify user of new account') 2523 } 2524 # Redirect back to page which initiated the create request 2525 # usually admin/user/user/create. 2526 form['destination'] = {'#type' : 'hidden', '#value' : php.GET['q']} 2527 # Create a dummy variable for pass-by-reference parameters. 2528 null_ = php.Reference() 2529 extra = _forms(null_, None, None, 'register') 2530 # Remove form_group around default fields if there are no other groups. 2531 if (not extra): 2532 for key in ('name', 'mail', 'pass', 'status', 'roles', 'notify'): 2533 if (php.isset(form['account'][key])): 2534 form[key] = form['account'][key] 2535 del(form['account']) 2536 else: 2537 form = php.array_merge(form, extra) 2538 if (lib_bootstrap.variable_get('configurable_timezones', 1)): 2539 # Override field ID, so we only change timezone on user registration, 2540 # and never touch it on user edit pages. 2541 form['timezone'] = { 2542 '#type' : 'hidden', 2543 '#default_value' : \ 2544 lib_bootstrap.variable_get('date_default_timezone', None), 2545 '#id' : 'edit-user-register-timezone' 2546 } 2547 # Add the JavaScript callback to automatically set the timezone. 2548 drupal_add_js(\ 2549 'if (Drupal.jsEnabled) { ' + \ 2550 ' $(document).ready(function() { ' + \ 2551 ' Drupal.setDefaultTimezone(); ' + \ 2552 ' }) ' + \ 2553 '}', \ 2554 'inline') 2555 form['submit'] = {'#type' : 'submit', '#value' : \ 2556 lib_common.t('Create new account'), '#weight' : 30} 2557 form['#validate'].append( 'user_register_validate' ) 2558 return form
2559 2560 2561
2562 -def register_validate(form, form_state):
2563 php.Reference.check(form_state) 2564 plugin_invoke('validate', form_state['values'], form_state['values'], \ 2565 'account')
2566 2567
2568 -def _forms(edit, account, category, hook = 'form'):
2569 """ 2570 Retrieve a list of all form elements for the specified category. 2571 """ 2572 php.Reference.check(edit) 2573 groups = [] 2574 for plugin in lib_plugin.list_(): 2575 data = plugin_invoke(plugin, 'user', hook, edit, account, category) 2576 if data: 2577 groups = php.array_merge_recursive(data, groups) 2578 php.uasort(groups, '_user_sort') 2579 return (False if php.empty(groups) else groups)
2580