[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

/user/ -> lib.php (source)

   1  <?php
   2  // This file is part of Moodle - http://moodle.org/
   3  //
   4  // Moodle is free software: you can redistribute it and/or modify
   5  // it under the terms of the GNU General Public License as published by
   6  // the Free Software Foundation, either version 3 of the License, or
   7  // (at your option) any later version.
   8  //
   9  // Moodle is distributed in the hope that it will be useful,
  10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12  // GNU General Public License for more details.
  13  //
  14  // You should have received a copy of the GNU General Public License
  15  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  16  
  17  /**
  18   * External user API
  19   *
  20   * @package   core_user
  21   * @copyright 2009 Moodle Pty Ltd (http://moodle.com)
  22   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  23   */
  24  
  25  /**
  26   * Creates a user
  27   *
  28   * @throws moodle_exception
  29   * @param stdClass $user user to create
  30   * @param bool $updatepassword if true, authentication plugin will update password.
  31   * @param bool $triggerevent set false if user_created event should not be triggred.
  32   *             This will not affect user_password_updated event triggering.
  33   * @return int id of the newly created user
  34   */
  35  function user_create_user($user, $updatepassword = true, $triggerevent = true) {
  36      global $DB;
  37  
  38      // Set the timecreate field to the current time.
  39      if (!is_object($user)) {
  40          $user = (object) $user;
  41      }
  42  
  43      // Check username.
  44      if ($user->username !== core_text::strtolower($user->username)) {
  45          throw new moodle_exception('usernamelowercase');
  46      } else {
  47          if ($user->username !== core_user::clean_field($user->username, 'username')) {
  48              throw new moodle_exception('invalidusername');
  49          }
  50      }
  51  
  52      // Save the password in a temp value for later.
  53      if ($updatepassword && isset($user->password)) {
  54  
  55          // Check password toward the password policy.
  56          if (!check_password_policy($user->password, $errmsg)) {
  57              throw new moodle_exception($errmsg);
  58          }
  59  
  60          $userpassword = $user->password;
  61          unset($user->password);
  62      }
  63  
  64      // Apply default values for user preferences that are stored in users table.
  65      if (!isset($user->calendartype)) {
  66          $user->calendartype = core_user::get_property_default('calendartype');
  67      }
  68      if (!isset($user->maildisplay)) {
  69          $user->maildisplay = core_user::get_property_default('maildisplay');
  70      }
  71      if (!isset($user->mailformat)) {
  72          $user->mailformat = core_user::get_property_default('mailformat');
  73      }
  74      if (!isset($user->maildigest)) {
  75          $user->maildigest = core_user::get_property_default('maildigest');
  76      }
  77      if (!isset($user->autosubscribe)) {
  78          $user->autosubscribe = core_user::get_property_default('autosubscribe');
  79      }
  80      if (!isset($user->trackforums)) {
  81          $user->trackforums = core_user::get_property_default('trackforums');
  82      }
  83      if (!isset($user->lang)) {
  84          $user->lang = core_user::get_property_default('lang');
  85      }
  86  
  87      $user->timecreated = time();
  88      $user->timemodified = $user->timecreated;
  89  
  90      // Validate user data object.
  91      $uservalidation = core_user::validate($user);
  92      if ($uservalidation !== true) {
  93          foreach ($uservalidation as $field => $message) {
  94              debugging("The property '$field' has invalid data and has been cleaned.", DEBUG_DEVELOPER);
  95              $user->$field = core_user::clean_field($user->$field, $field);
  96          }
  97      }
  98  
  99      // Insert the user into the database.
 100      $newuserid = $DB->insert_record('user', $user);
 101  
 102      // Create USER context for this user.
 103      $usercontext = context_user::instance($newuserid);
 104  
 105      // Update user password if necessary.
 106      if (isset($userpassword)) {
 107          // Get full database user row, in case auth is default.
 108          $newuser = $DB->get_record('user', array('id' => $newuserid));
 109          $authplugin = get_auth_plugin($newuser->auth);
 110          $authplugin->user_update_password($newuser, $userpassword);
 111      }
 112  
 113      // Trigger event If required.
 114      if ($triggerevent) {
 115          \core\event\user_created::create_from_userid($newuserid)->trigger();
 116      }
 117  
 118      return $newuserid;
 119  }
 120  
 121  /**
 122   * Update a user with a user object (will compare against the ID)
 123   *
 124   * @throws moodle_exception
 125   * @param stdClass $user the user to update
 126   * @param bool $updatepassword if true, authentication plugin will update password.
 127   * @param bool $triggerevent set false if user_updated event should not be triggred.
 128   *             This will not affect user_password_updated event triggering.
 129   */
 130  function user_update_user($user, $updatepassword = true, $triggerevent = true) {
 131      global $DB;
 132  
 133      // Set the timecreate field to the current time.
 134      if (!is_object($user)) {
 135          $user = (object) $user;
 136      }
 137  
 138      // Check username.
 139      if (isset($user->username)) {
 140          if ($user->username !== core_text::strtolower($user->username)) {
 141              throw new moodle_exception('usernamelowercase');
 142          } else {
 143              if ($user->username !== core_user::clean_field($user->username, 'username')) {
 144                  throw new moodle_exception('invalidusername');
 145              }
 146          }
 147      }
 148  
 149      // Unset password here, for updating later, if password update is required.
 150      if ($updatepassword && isset($user->password)) {
 151  
 152          // Check password toward the password policy.
 153          if (!check_password_policy($user->password, $errmsg)) {
 154              throw new moodle_exception($errmsg);
 155          }
 156  
 157          $passwd = $user->password;
 158          unset($user->password);
 159      }
 160  
 161      // Make sure calendartype, if set, is valid.
 162      if (empty($user->calendartype)) {
 163          // Unset this variable, must be an empty string, which we do not want to update the calendartype to.
 164          unset($user->calendartype);
 165      }
 166  
 167      $user->timemodified = time();
 168  
 169      // Validate user data object.
 170      $uservalidation = core_user::validate($user);
 171      if ($uservalidation !== true) {
 172          foreach ($uservalidation as $field => $message) {
 173              debugging("The property '$field' has invalid data and has been cleaned.", DEBUG_DEVELOPER);
 174              $user->$field = core_user::clean_field($user->$field, $field);
 175          }
 176      }
 177  
 178      $DB->update_record('user', $user);
 179  
 180      if ($updatepassword) {
 181          // Get full user record.
 182          $updateduser = $DB->get_record('user', array('id' => $user->id));
 183  
 184          // If password was set, then update its hash.
 185          if (isset($passwd)) {
 186              $authplugin = get_auth_plugin($updateduser->auth);
 187              if ($authplugin->can_change_password()) {
 188                  $authplugin->user_update_password($updateduser, $passwd);
 189              }
 190          }
 191      }
 192      // Trigger event if required.
 193      if ($triggerevent) {
 194          \core\event\user_updated::create_from_userid($user->id)->trigger();
 195      }
 196  }
 197  
 198  /**
 199   * Marks user deleted in internal user database and notifies the auth plugin.
 200   * Also unenrols user from all roles and does other cleanup.
 201   *
 202   * @todo Decide if this transaction is really needed (look for internal TODO:)
 203   * @param object $user Userobject before delete    (without system magic quotes)
 204   * @return boolean success
 205   */
 206  function user_delete_user($user) {
 207      return delete_user($user);
 208  }
 209  
 210  /**
 211   * Get users by id
 212   *
 213   * @param array $userids id of users to retrieve
 214   * @return array
 215   */
 216  function user_get_users_by_id($userids) {
 217      global $DB;
 218      return $DB->get_records_list('user', 'id', $userids);
 219  }
 220  
 221  /**
 222   * Returns the list of default 'displayable' fields
 223   *
 224   * Contains database field names but also names used to generate information, such as enrolledcourses
 225   *
 226   * @return array of user fields
 227   */
 228  function user_get_default_fields() {
 229      return array( 'id', 'username', 'fullname', 'firstname', 'lastname', 'email',
 230          'address', 'phone1', 'phone2', 'icq', 'skype', 'yahoo', 'aim', 'msn', 'department',
 231          'institution', 'interests', 'firstaccess', 'lastaccess', 'auth', 'confirmed',
 232          'idnumber', 'lang', 'theme', 'timezone', 'mailformat', 'description', 'descriptionformat',
 233          'city', 'url', 'country', 'profileimageurlsmall', 'profileimageurl', 'customfields',
 234          'groups', 'roles', 'preferences', 'enrolledcourses'
 235      );
 236  }
 237  
 238  /**
 239   *
 240   * Give user record from mdl_user, build an array contains all user details.
 241   *
 242   * Warning: description file urls are 'webservice/pluginfile.php' is use.
 243   *          it can be changed with $CFG->moodlewstextformatlinkstoimagesfile
 244   *
 245   * @throws moodle_exception
 246   * @param stdClass $user user record from mdl_user
 247   * @param stdClass $course moodle course
 248   * @param array $userfields required fields
 249   * @return array|null
 250   */
 251  function user_get_user_details($user, $course = null, array $userfields = array()) {
 252      global $USER, $DB, $CFG, $PAGE;
 253      require_once($CFG->dirroot . "/user/profile/lib.php"); // Custom field library.
 254      require_once($CFG->dirroot . "/lib/filelib.php");      // File handling on description and friends.
 255  
 256      $defaultfields = user_get_default_fields();
 257  
 258      if (empty($userfields)) {
 259          $userfields = $defaultfields;
 260      }
 261  
 262      foreach ($userfields as $thefield) {
 263          if (!in_array($thefield, $defaultfields)) {
 264              throw new moodle_exception('invaliduserfield', 'error', '', $thefield);
 265          }
 266      }
 267  
 268      // Make sure id and fullname are included.
 269      if (!in_array('id', $userfields)) {
 270          $userfields[] = 'id';
 271      }
 272  
 273      if (!in_array('fullname', $userfields)) {
 274          $userfields[] = 'fullname';
 275      }
 276  
 277      if (!empty($course)) {
 278          $context = context_course::instance($course->id);
 279          $usercontext = context_user::instance($user->id);
 280          $canviewdetailscap = (has_capability('moodle/user:viewdetails', $context) || has_capability('moodle/user:viewdetails', $usercontext));
 281      } else {
 282          $context = context_user::instance($user->id);
 283          $usercontext = $context;
 284          $canviewdetailscap = has_capability('moodle/user:viewdetails', $usercontext);
 285      }
 286  
 287      $currentuser = ($user->id == $USER->id);
 288      $isadmin = is_siteadmin($USER);
 289  
 290      $showuseridentityfields = get_extra_user_fields($context);
 291  
 292      if (!empty($course)) {
 293          $canviewhiddenuserfields = has_capability('moodle/course:viewhiddenuserfields', $context);
 294      } else {
 295          $canviewhiddenuserfields = has_capability('moodle/user:viewhiddendetails', $context);
 296      }
 297      $canviewfullnames = has_capability('moodle/site:viewfullnames', $context);
 298      if (!empty($course)) {
 299          $canviewuseremail = has_capability('moodle/course:useremail', $context);
 300      } else {
 301          $canviewuseremail = false;
 302      }
 303      $cannotviewdescription   = !empty($CFG->profilesforenrolledusersonly) && !$currentuser && !$DB->record_exists('role_assignments', array('userid' => $user->id));
 304      if (!empty($course)) {
 305          $canaccessallgroups = has_capability('moodle/site:accessallgroups', $context);
 306      } else {
 307          $canaccessallgroups = false;
 308      }
 309  
 310      if (!$currentuser && !$canviewdetailscap && !has_coursecontact_role($user->id)) {
 311          // Skip this user details.
 312          return null;
 313      }
 314  
 315      $userdetails = array();
 316      $userdetails['id'] = $user->id;
 317  
 318      if (in_array('username', $userfields)) {
 319          if ($currentuser or has_capability('moodle/user:viewalldetails', $context)) {
 320              $userdetails['username'] = $user->username;
 321          }
 322      }
 323      if ($isadmin or $canviewfullnames) {
 324          if (in_array('firstname', $userfields)) {
 325              $userdetails['firstname'] = $user->firstname;
 326          }
 327          if (in_array('lastname', $userfields)) {
 328              $userdetails['lastname'] = $user->lastname;
 329          }
 330      }
 331      $userdetails['fullname'] = fullname($user);
 332  
 333      if (in_array('customfields', $userfields)) {
 334          $fields = $DB->get_recordset_sql("SELECT f.*
 335                                              FROM {user_info_field} f
 336                                              JOIN {user_info_category} c
 337                                                   ON f.categoryid=c.id
 338                                          ORDER BY c.sortorder ASC, f.sortorder ASC");
 339          $userdetails['customfields'] = array();
 340          foreach ($fields as $field) {
 341              require_once($CFG->dirroot.'/user/profile/field/'.$field->datatype.'/field.class.php');
 342              $newfield = 'profile_field_'.$field->datatype;
 343              $formfield = new $newfield($field->id, $user->id);
 344              if ($formfield->is_visible() and !$formfield->is_empty()) {
 345  
 346                  // TODO: Part of MDL-50728, this conditional coding must be moved to
 347                  // proper profile fields API so they are self-contained.
 348                  // We only use display_data in fields that require text formatting.
 349                  if ($field->datatype == 'text' or $field->datatype == 'textarea') {
 350                      $fieldvalue = $formfield->display_data();
 351                  } else {
 352                      // Cases: datetime, checkbox and menu.
 353                      $fieldvalue = $formfield->data;
 354                  }
 355  
 356                  $userdetails['customfields'][] =
 357                      array('name' => $formfield->field->name, 'value' => $fieldvalue,
 358                          'type' => $field->datatype, 'shortname' => $formfield->field->shortname);
 359              }
 360          }
 361          $fields->close();
 362          // Unset customfields if it's empty.
 363          if (empty($userdetails['customfields'])) {
 364              unset($userdetails['customfields']);
 365          }
 366      }
 367  
 368      // Profile image.
 369      if (in_array('profileimageurl', $userfields)) {
 370          $userpicture = new user_picture($user);
 371          $userpicture->size = 1; // Size f1.
 372          $userdetails['profileimageurl'] = $userpicture->get_url($PAGE)->out(false);
 373      }
 374      if (in_array('profileimageurlsmall', $userfields)) {
 375          if (!isset($userpicture)) {
 376              $userpicture = new user_picture($user);
 377          }
 378          $userpicture->size = 0; // Size f2.
 379          $userdetails['profileimageurlsmall'] = $userpicture->get_url($PAGE)->out(false);
 380      }
 381  
 382      // Hidden user field.
 383      if ($canviewhiddenuserfields) {
 384          $hiddenfields = array();
 385          // Address, phone1 and phone2 not appears in hidden fields list but require viewhiddenfields capability
 386          // according to user/profile.php.
 387          if ($user->address && in_array('address', $userfields)) {
 388              $userdetails['address'] = $user->address;
 389          }
 390      } else {
 391          $hiddenfields = array_flip(explode(',', $CFG->hiddenuserfields));
 392      }
 393  
 394      if ($user->phone1 && in_array('phone1', $userfields) &&
 395              (in_array('phone1', $showuseridentityfields) or $canviewhiddenuserfields)) {
 396          $userdetails['phone1'] = $user->phone1;
 397      }
 398      if ($user->phone2 && in_array('phone2', $userfields) &&
 399              (in_array('phone2', $showuseridentityfields) or $canviewhiddenuserfields)) {
 400          $userdetails['phone2'] = $user->phone2;
 401      }
 402  
 403      if (isset($user->description) &&
 404          ((!isset($hiddenfields['description']) && !$cannotviewdescription) or $isadmin)) {
 405          if (in_array('description', $userfields)) {
 406              // Always return the descriptionformat if description is requested.
 407              list($userdetails['description'], $userdetails['descriptionformat']) =
 408                      external_format_text($user->description, $user->descriptionformat,
 409                              $usercontext->id, 'user', 'profile', null);
 410          }
 411      }
 412  
 413      if (in_array('country', $userfields) && (!isset($hiddenfields['country']) or $isadmin) && $user->country) {
 414          $userdetails['country'] = $user->country;
 415      }
 416  
 417      if (in_array('city', $userfields) && (!isset($hiddenfields['city']) or $isadmin) && $user->city) {
 418          $userdetails['city'] = $user->city;
 419      }
 420  
 421      if (in_array('url', $userfields) && $user->url && (!isset($hiddenfields['webpage']) or $isadmin)) {
 422          $url = $user->url;
 423          if (strpos($user->url, '://') === false) {
 424              $url = 'http://'. $url;
 425          }
 426          $user->url = clean_param($user->url, PARAM_URL);
 427          $userdetails['url'] = $user->url;
 428      }
 429  
 430      if (in_array('icq', $userfields) && $user->icq && (!isset($hiddenfields['icqnumber']) or $isadmin)) {
 431          $userdetails['icq'] = $user->icq;
 432      }
 433  
 434      if (in_array('skype', $userfields) && $user->skype && (!isset($hiddenfields['skypeid']) or $isadmin)) {
 435          $userdetails['skype'] = $user->skype;
 436      }
 437      if (in_array('yahoo', $userfields) && $user->yahoo && (!isset($hiddenfields['yahooid']) or $isadmin)) {
 438          $userdetails['yahoo'] = $user->yahoo;
 439      }
 440      if (in_array('aim', $userfields) && $user->aim && (!isset($hiddenfields['aimid']) or $isadmin)) {
 441          $userdetails['aim'] = $user->aim;
 442      }
 443      if (in_array('msn', $userfields) && $user->msn && (!isset($hiddenfields['msnid']) or $isadmin)) {
 444          $userdetails['msn'] = $user->msn;
 445      }
 446  
 447      if (in_array('firstaccess', $userfields) && (!isset($hiddenfields['firstaccess']) or $isadmin)) {
 448          if ($user->firstaccess) {
 449              $userdetails['firstaccess'] = $user->firstaccess;
 450          } else {
 451              $userdetails['firstaccess'] = 0;
 452          }
 453      }
 454      if (in_array('lastaccess', $userfields) && (!isset($hiddenfields['lastaccess']) or $isadmin)) {
 455          if ($user->lastaccess) {
 456              $userdetails['lastaccess'] = $user->lastaccess;
 457          } else {
 458              $userdetails['lastaccess'] = 0;
 459          }
 460      }
 461  
 462      if (in_array('email', $userfields) && ($isadmin // The admin is allowed the users email.
 463        or $currentuser // Of course the current user is as well.
 464        or $canviewuseremail  // This is a capability in course context, it will be false in usercontext.
 465        or in_array('email', $showuseridentityfields)
 466        or $user->maildisplay == 1
 467        or ($user->maildisplay == 2 and enrol_sharing_course($user, $USER)))) {
 468          $userdetails['email'] = $user->email;
 469      }
 470  
 471      if (in_array('interests', $userfields)) {
 472          $interests = core_tag_tag::get_item_tags_array('core', 'user', $user->id, core_tag_tag::BOTH_STANDARD_AND_NOT, 0, false);
 473          if ($interests) {
 474              $userdetails['interests'] = join(', ', $interests);
 475          }
 476      }
 477  
 478      // Departement/Institution/Idnumber are not displayed on any profile, however you can get them from editing profile.
 479      if (in_array('idnumber', $userfields) && $user->idnumber) {
 480          if (in_array('idnumber', $showuseridentityfields) or $currentuser or
 481                  has_capability('moodle/user:viewalldetails', $context)) {
 482              $userdetails['idnumber'] = $user->idnumber;
 483          }
 484      }
 485      if (in_array('institution', $userfields) && $user->institution) {
 486          if (in_array('institution', $showuseridentityfields) or $currentuser or
 487                  has_capability('moodle/user:viewalldetails', $context)) {
 488              $userdetails['institution'] = $user->institution;
 489          }
 490      }
 491      // Isset because it's ok to have department 0.
 492      if (in_array('department', $userfields) && isset($user->department)) {
 493          if (in_array('department', $showuseridentityfields) or $currentuser or
 494                  has_capability('moodle/user:viewalldetails', $context)) {
 495              $userdetails['department'] = $user->department;
 496          }
 497      }
 498  
 499      if (in_array('roles', $userfields) && !empty($course)) {
 500          // Not a big secret.
 501          $roles = get_user_roles($context, $user->id, false);
 502          $userdetails['roles'] = array();
 503          foreach ($roles as $role) {
 504              $userdetails['roles'][] = array(
 505                  'roleid'       => $role->roleid,
 506                  'name'         => $role->name,
 507                  'shortname'    => $role->shortname,
 508                  'sortorder'    => $role->sortorder
 509              );
 510          }
 511      }
 512  
 513      // If groups are in use and enforced throughout the course, then make sure we can meet in at least one course level group.
 514      if (in_array('groups', $userfields) && !empty($course) && $canaccessallgroups) {
 515          $usergroups = groups_get_all_groups($course->id, $user->id, $course->defaultgroupingid,
 516                  'g.id, g.name,g.description,g.descriptionformat');
 517          $userdetails['groups'] = array();
 518          foreach ($usergroups as $group) {
 519              list($group->description, $group->descriptionformat) =
 520                  external_format_text($group->description, $group->descriptionformat,
 521                          $context->id, 'group', 'description', $group->id);
 522              $userdetails['groups'][] = array('id' => $group->id, 'name' => $group->name,
 523                  'description' => $group->description, 'descriptionformat' => $group->descriptionformat);
 524          }
 525      }
 526      // List of courses where the user is enrolled.
 527      if (in_array('enrolledcourses', $userfields) && !isset($hiddenfields['mycourses'])) {
 528          $enrolledcourses = array();
 529          if ($mycourses = enrol_get_users_courses($user->id, true)) {
 530              foreach ($mycourses as $mycourse) {
 531                  if ($mycourse->category) {
 532                      $coursecontext = context_course::instance($mycourse->id);
 533                      $enrolledcourse = array();
 534                      $enrolledcourse['id'] = $mycourse->id;
 535                      $enrolledcourse['fullname'] = format_string($mycourse->fullname, true, array('context' => $coursecontext));
 536                      $enrolledcourse['shortname'] = format_string($mycourse->shortname, true, array('context' => $coursecontext));
 537                      $enrolledcourses[] = $enrolledcourse;
 538                  }
 539              }
 540              $userdetails['enrolledcourses'] = $enrolledcourses;
 541          }
 542      }
 543  
 544      // User preferences.
 545      if (in_array('preferences', $userfields) && $currentuser) {
 546          $preferences = array();
 547          $userpreferences = get_user_preferences();
 548          foreach ($userpreferences as $prefname => $prefvalue) {
 549              $preferences[] = array('name' => $prefname, 'value' => $prefvalue);
 550          }
 551          $userdetails['preferences'] = $preferences;
 552      }
 553  
 554      return $userdetails;
 555  }
 556  
 557  /**
 558   * Tries to obtain user details, either recurring directly to the user's system profile
 559   * or through one of the user's course enrollments (course profile).
 560   *
 561   * @param stdClass $user The user.
 562   * @return array if unsuccessful or the allowed user details.
 563   */
 564  function user_get_user_details_courses($user) {
 565      global $USER;
 566      $userdetails = null;
 567  
 568      // Get the courses that the user is enrolled in (only active).
 569      $courses = enrol_get_users_courses($user->id, true);
 570  
 571      $systemprofile = false;
 572      if (can_view_user_details_cap($user) || ($user->id == $USER->id) || has_coursecontact_role($user->id)) {
 573          $systemprofile = true;
 574      }
 575  
 576      // Try using system profile.
 577      if ($systemprofile) {
 578          $userdetails = user_get_user_details($user, null);
 579      } else {
 580          // Try through course profile.
 581          foreach ($courses as $course) {
 582              if (can_view_user_details_cap($user, $course) || ($user->id == $USER->id) || has_coursecontact_role($user->id)) {
 583                  $userdetails = user_get_user_details($user, $course);
 584              }
 585          }
 586      }
 587  
 588      return $userdetails;
 589  }
 590  
 591  /**
 592   * Check if $USER have the necessary capabilities to obtain user details.
 593   *
 594   * @param stdClass $user
 595   * @param stdClass $course if null then only consider system profile otherwise also consider the course's profile.
 596   * @return bool true if $USER can view user details.
 597   */
 598  function can_view_user_details_cap($user, $course = null) {
 599      // Check $USER has the capability to view the user details at user context.
 600      $usercontext = context_user::instance($user->id);
 601      $result = has_capability('moodle/user:viewdetails', $usercontext);
 602      // Otherwise can $USER see them at course context.
 603      if (!$result && !empty($course)) {
 604          $context = context_course::instance($course->id);
 605          $result = has_capability('moodle/user:viewdetails', $context);
 606      }
 607      return $result;
 608  }
 609  
 610  /**
 611   * Return a list of page types
 612   * @param string $pagetype current page type
 613   * @param stdClass $parentcontext Block's parent context
 614   * @param stdClass $currentcontext Current context of block
 615   * @return array
 616   */
 617  function user_page_type_list($pagetype, $parentcontext, $currentcontext) {
 618      return array('user-profile' => get_string('page-user-profile', 'pagetype'));
 619  }
 620  
 621  /**
 622   * Count the number of failed login attempts for the given user, since last successful login.
 623   *
 624   * @param int|stdclass $user user id or object.
 625   * @param bool $reset Resets failed login count, if set to true.
 626   *
 627   * @return int number of failed login attempts since the last successful login.
 628   */
 629  function user_count_login_failures($user, $reset = true) {
 630      global $DB;
 631  
 632      if (!is_object($user)) {
 633          $user = $DB->get_record('user', array('id' => $user), '*', MUST_EXIST);
 634      }
 635      if ($user->deleted) {
 636          // Deleted user, nothing to do.
 637          return 0;
 638      }
 639      $count = get_user_preferences('login_failed_count_since_success', 0, $user);
 640      if ($reset) {
 641          set_user_preference('login_failed_count_since_success', 0, $user);
 642      }
 643      return $count;
 644  }
 645  
 646  /**
 647   * Converts a string into a flat array of menu items, where each menu items is a
 648   * stdClass with fields type, url, title, pix, and imgsrc.
 649   *
 650   * @param string $text the menu items definition
 651   * @param moodle_page $page the current page
 652   * @return array
 653   */
 654  function user_convert_text_to_menu_items($text, $page) {
 655      global $OUTPUT, $CFG;
 656  
 657      $lines = explode("\n", $text);
 658      $items = array();
 659      $lastchild = null;
 660      $lastdepth = null;
 661      $lastsort = 0;
 662      $children = array();
 663      foreach ($lines as $line) {
 664          $line = trim($line);
 665          $bits = explode('|', $line, 3);
 666          $itemtype = 'link';
 667          if (preg_match("/^#+$/", $line)) {
 668              $itemtype = 'divider';
 669          } else if (!array_key_exists(0, $bits) or empty($bits[0])) {
 670              // Every item must have a name to be valid.
 671              continue;
 672          } else {
 673              $bits[0] = ltrim($bits[0], '-');
 674          }
 675  
 676          // Create the child.
 677          $child = new stdClass();
 678          $child->itemtype = $itemtype;
 679          if ($itemtype === 'divider') {
 680              // Add the divider to the list of children and skip link
 681              // processing.
 682              $children[] = $child;
 683              continue;
 684          }
 685  
 686          // Name processing.
 687          $namebits = explode(',', $bits[0], 2);
 688          if (count($namebits) == 2) {
 689              // Check the validity of the identifier part of the string.
 690              if (clean_param($namebits[0], PARAM_STRINGID) !== '') {
 691                  // Treat this as a language string.
 692                  $child->title = get_string($namebits[0], $namebits[1]);
 693                  $child->titleidentifier = implode(',', $namebits);
 694              }
 695          }
 696          if (empty($child->title)) {
 697              // Use it as is, don't even clean it.
 698              $child->title = $bits[0];
 699              $child->titleidentifier = str_replace(" ", "-", $bits[0]);
 700          }
 701  
 702          // URL processing.
 703          if (!array_key_exists(1, $bits) or empty($bits[1])) {
 704              // Set the url to null, and set the itemtype to invalid.
 705              $bits[1] = null;
 706              $child->itemtype = "invalid";
 707          } else {
 708              // Nasty hack to replace the grades with the direct url.
 709              if (strpos($bits[1], '/grade/report/mygrades.php') !== false) {
 710                  $bits[1] = user_mygrades_url();
 711              }
 712  
 713              // Make sure the url is a moodle url.
 714              $bits[1] = new moodle_url(trim($bits[1]));
 715          }
 716          $child->url = $bits[1];
 717  
 718          // PIX processing.
 719          $pixpath = "t/edit";
 720          if (!array_key_exists(2, $bits) or empty($bits[2])) {
 721              // Use the default.
 722              $child->pix = $pixpath;
 723          } else {
 724              // Check for the specified image existing.
 725              $pixpath = "t/" . $bits[2];
 726              if ($page->theme->resolve_image_location($pixpath, 'moodle', true)) {
 727                  // Use the image.
 728                  $child->pix = $pixpath;
 729              } else {
 730                  // Treat it like a URL.
 731                  $child->pix = null;
 732                  $child->imgsrc = $bits[2];
 733              }
 734          }
 735  
 736          // Add this child to the list of children.
 737          $children[] = $child;
 738      }
 739      return $children;
 740  }
 741  
 742  /**
 743   * Get a list of essential user navigation items.
 744   *
 745   * @param stdclass $user user object.
 746   * @param moodle_page $page page object.
 747   * @param array $options associative array.
 748   *     options are:
 749   *     - avatarsize=35 (size of avatar image)
 750   * @return stdClass $returnobj navigation information object, where:
 751   *
 752   *      $returnobj->navitems    array    array of links where each link is a
 753   *                                       stdClass with fields url, title, and
 754   *                                       pix
 755   *      $returnobj->metadata    array    array of useful user metadata to be
 756   *                                       used when constructing navigation;
 757   *                                       fields include:
 758   *
 759   *          ROLE FIELDS
 760   *          asotherrole    bool    whether viewing as another role
 761   *          rolename       string  name of the role
 762   *
 763   *          USER FIELDS
 764   *          These fields are for the currently-logged in user, or for
 765   *          the user that the real user is currently logged in as.
 766   *
 767   *          userid         int        the id of the user in question
 768   *          userfullname   string     the user's full name
 769   *          userprofileurl moodle_url the url of the user's profile
 770   *          useravatar     string     a HTML fragment - the rendered
 771   *                                    user_picture for this user
 772   *          userloginfail  string     an error string denoting the number
 773   *                                    of login failures since last login
 774   *
 775   *          "REAL USER" FIELDS
 776   *          These fields are for when asotheruser is true, and
 777   *          correspond to the underlying "real user".
 778   *
 779   *          asotheruser        bool    whether viewing as another user
 780   *          realuserid         int        the id of the user in question
 781   *          realuserfullname   string     the user's full name
 782   *          realuserprofileurl moodle_url the url of the user's profile
 783   *          realuseravatar     string     a HTML fragment - the rendered
 784   *                                        user_picture for this user
 785   *
 786   *          MNET PROVIDER FIELDS
 787   *          asmnetuser            bool   whether viewing as a user from an
 788   *                                       MNet provider
 789   *          mnetidprovidername    string name of the MNet provider
 790   *          mnetidproviderwwwroot string URL of the MNet provider
 791   */
 792  function user_get_user_navigation_info($user, $page, $options = array()) {
 793      global $OUTPUT, $DB, $SESSION, $CFG;
 794  
 795      $returnobject = new stdClass();
 796      $returnobject->navitems = array();
 797      $returnobject->metadata = array();
 798  
 799      $course = $page->course;
 800  
 801      // Query the environment.
 802      $context = context_course::instance($course->id);
 803  
 804      // Get basic user metadata.
 805      $returnobject->metadata['userid'] = $user->id;
 806      $returnobject->metadata['userfullname'] = fullname($user, true);
 807      $returnobject->metadata['userprofileurl'] = new moodle_url('/user/profile.php', array(
 808          'id' => $user->id
 809      ));
 810  
 811      $avataroptions = array('link' => false, 'visibletoscreenreaders' => false);
 812      if (!empty($options['avatarsize'])) {
 813          $avataroptions['size'] = $options['avatarsize'];
 814      }
 815      $returnobject->metadata['useravatar'] = $OUTPUT->user_picture (
 816          $user, $avataroptions
 817      );
 818      // Build a list of items for a regular user.
 819  
 820      // Query MNet status.
 821      if ($returnobject->metadata['asmnetuser'] = is_mnet_remote_user($user)) {
 822          $mnetidprovider = $DB->get_record('mnet_host', array('id' => $user->mnethostid));
 823          $returnobject->metadata['mnetidprovidername'] = $mnetidprovider->name;
 824          $returnobject->metadata['mnetidproviderwwwroot'] = $mnetidprovider->wwwroot;
 825      }
 826  
 827      // Did the user just log in?
 828      if (isset($SESSION->justloggedin)) {
 829          // Don't unset this flag as login_info still needs it.
 830          if (!empty($CFG->displayloginfailures)) {
 831              // We're already in /user/lib.php, so we don't need to include.
 832              if ($count = user_count_login_failures($user)) {
 833  
 834                  // Get login failures string.
 835                  $a = new stdClass();
 836                  $a->attempts = html_writer::tag('span', $count, array('class' => 'value'));
 837                  $returnobject->metadata['userloginfail'] =
 838                      get_string('failedloginattempts', '', $a);
 839  
 840              }
 841          }
 842      }
 843  
 844      // Links: Dashboard.
 845      $myhome = new stdClass();
 846      $myhome->itemtype = 'link';
 847      $myhome->url = new moodle_url('/my/');
 848      $myhome->title = get_string('mymoodle', 'admin');
 849      $myhome->titleidentifier = 'mymoodle,admin';
 850      $myhome->pix = "i/course";
 851      $returnobject->navitems[] = $myhome;
 852  
 853      // Links: My Profile.
 854      $myprofile = new stdClass();
 855      $myprofile->itemtype = 'link';
 856      $myprofile->url = new moodle_url('/user/profile.php', array('id' => $user->id));
 857      $myprofile->title = get_string('profile');
 858      $myprofile->titleidentifier = 'profile,moodle';
 859      $myprofile->pix = "i/user";
 860      $returnobject->navitems[] = $myprofile;
 861  
 862      // Links: Role-return or logout link.
 863      $lastobj = null;
 864      $buildlogout = true;
 865      $returnobject->metadata['asotherrole'] = false;
 866      if (is_role_switched($course->id)) {
 867          if ($role = $DB->get_record('role', array('id' => $user->access['rsw'][$context->path]))) {
 868              // Build role-return link instead of logout link.
 869              $rolereturn = new stdClass();
 870              $rolereturn->itemtype = 'link';
 871              $rolereturn->url = new moodle_url('/course/switchrole.php', array(
 872                  'id' => $course->id,
 873                  'sesskey' => sesskey(),
 874                  'switchrole' => 0,
 875                  'returnurl' => $page->url->out_as_local_url(false)
 876              ));
 877              $rolereturn->pix = "a/logout";
 878              $rolereturn->title = get_string('switchrolereturn');
 879              $rolereturn->titleidentifier = 'switchrolereturn,moodle';
 880              $lastobj = $rolereturn;
 881  
 882              $returnobject->metadata['asotherrole'] = true;
 883              $returnobject->metadata['rolename'] = role_get_name($role, $context);
 884  
 885              $buildlogout = false;
 886          }
 887      }
 888  
 889      if ($returnobject->metadata['asotheruser'] = \core\session\manager::is_loggedinas()) {
 890          $realuser = \core\session\manager::get_realuser();
 891  
 892          // Save values for the real user, as $user will be full of data for the
 893          // user the user is disguised as.
 894          $returnobject->metadata['realuserid'] = $realuser->id;
 895          $returnobject->metadata['realuserfullname'] = fullname($realuser, true);
 896          $returnobject->metadata['realuserprofileurl'] = new moodle_url('/user/profile.php', array(
 897              'id' => $realuser->id
 898          ));
 899          $returnobject->metadata['realuseravatar'] = $OUTPUT->user_picture($realuser, $avataroptions);
 900  
 901          // Build a user-revert link.
 902          $userrevert = new stdClass();
 903          $userrevert->itemtype = 'link';
 904          $userrevert->url = new moodle_url('/course/loginas.php', array(
 905              'id' => $course->id,
 906              'sesskey' => sesskey()
 907          ));
 908          $userrevert->pix = "a/logout";
 909          $userrevert->title = get_string('logout');
 910          $userrevert->titleidentifier = 'logout,moodle';
 911          $lastobj = $userrevert;
 912  
 913          $buildlogout = false;
 914      }
 915  
 916      if ($buildlogout) {
 917          // Build a logout link.
 918          $logout = new stdClass();
 919          $logout->itemtype = 'link';
 920          $logout->url = new moodle_url('/login/logout.php', array('sesskey' => sesskey()));
 921          $logout->pix = "a/logout";
 922          $logout->title = get_string('logout');
 923          $logout->titleidentifier = 'logout,moodle';
 924          $lastobj = $logout;
 925      }
 926  
 927      // Before we add the last item (usually a logout link), add any
 928      // custom-defined items.
 929      $customitems = user_convert_text_to_menu_items($CFG->customusermenuitems, $page);
 930      foreach ($customitems as $item) {
 931          $returnobject->navitems[] = $item;
 932      }
 933  
 934      // Add the last item to the list.
 935      if (!is_null($lastobj)) {
 936          $returnobject->navitems[] = $lastobj;
 937      }
 938  
 939      return $returnobject;
 940  }
 941  
 942  /**
 943   * Add password to the list of used hashes for this user.
 944   *
 945   * This is supposed to be used from:
 946   *  1/ change own password form
 947   *  2/ password reset process
 948   *  3/ user signup in auth plugins if password changing supported
 949   *
 950   * @param int $userid user id
 951   * @param string $password plaintext password
 952   * @return void
 953   */
 954  function user_add_password_history($userid, $password) {
 955      global $CFG, $DB;
 956  
 957      if (empty($CFG->passwordreuselimit) or $CFG->passwordreuselimit < 0) {
 958          return;
 959      }
 960  
 961      // Note: this is using separate code form normal password hashing because
 962      //       we need to have this under control in the future. Also the auth
 963      //       plugin might not store the passwords locally at all.
 964  
 965      $record = new stdClass();
 966      $record->userid = $userid;
 967      $record->hash = password_hash($password, PASSWORD_DEFAULT);
 968      $record->timecreated = time();
 969      $DB->insert_record('user_password_history', $record);
 970  
 971      $i = 0;
 972      $records = $DB->get_records('user_password_history', array('userid' => $userid), 'timecreated DESC, id DESC');
 973      foreach ($records as $record) {
 974          $i++;
 975          if ($i > $CFG->passwordreuselimit) {
 976              $DB->delete_records('user_password_history', array('id' => $record->id));
 977          }
 978      }
 979  }
 980  
 981  /**
 982   * Was this password used before on change or reset password page?
 983   *
 984   * The $CFG->passwordreuselimit setting determines
 985   * how many times different password needs to be used
 986   * before allowing previously used password again.
 987   *
 988   * @param int $userid user id
 989   * @param string $password plaintext password
 990   * @return bool true if password reused
 991   */
 992  function user_is_previously_used_password($userid, $password) {
 993      global $CFG, $DB;
 994  
 995      if (empty($CFG->passwordreuselimit) or $CFG->passwordreuselimit < 0) {
 996          return false;
 997      }
 998  
 999      $reused = false;
1000  
1001      $i = 0;
1002      $records = $DB->get_records('user_password_history', array('userid' => $userid), 'timecreated DESC, id DESC');
1003      foreach ($records as $record) {
1004          $i++;
1005          if ($i > $CFG->passwordreuselimit) {
1006              $DB->delete_records('user_password_history', array('id' => $record->id));
1007              continue;
1008          }
1009          // NOTE: this is slow but we cannot compare the hashes directly any more.
1010          if (password_verify($password, $record->hash)) {
1011              $reused = true;
1012          }
1013      }
1014  
1015      return $reused;
1016  }
1017  
1018  /**
1019   * Remove a user device from the Moodle database (for PUSH notifications usually).
1020   *
1021   * @param string $uuid The device UUID.
1022   * @param string $appid The app id. If empty all the devices matching the UUID for the user will be removed.
1023   * @return bool true if removed, false if the device didn't exists in the database
1024   * @since Moodle 2.9
1025   */
1026  function user_remove_user_device($uuid, $appid = "") {
1027      global $DB, $USER;
1028  
1029      $conditions = array('uuid' => $uuid, 'userid' => $USER->id);
1030      if (!empty($appid)) {
1031          $conditions['appid'] = $appid;
1032      }
1033  
1034      if (!$DB->count_records('user_devices', $conditions)) {
1035          return false;
1036      }
1037  
1038      $DB->delete_records('user_devices', $conditions);
1039  
1040      return true;
1041  }
1042  
1043  /**
1044   * Trigger user_list_viewed event.
1045   *
1046   * @param stdClass  $course course  object
1047   * @param stdClass  $context course context object
1048   * @since Moodle 2.9
1049   */
1050  function user_list_view($course, $context) {
1051  
1052      $event = \core\event\user_list_viewed::create(array(
1053          'objectid' => $course->id,
1054          'courseid' => $course->id,
1055          'context' => $context,
1056          'other' => array(
1057              'courseshortname' => $course->shortname,
1058              'coursefullname' => $course->fullname
1059          )
1060      ));
1061      $event->trigger();
1062  }
1063  
1064  /**
1065   * Returns the url to use for the "Grades" link in the user navigation.
1066   *
1067   * @param int $userid The user's ID.
1068   * @param int $courseid The course ID if available.
1069   * @return mixed A URL to be directed to for "Grades".
1070   */
1071  function user_mygrades_url($userid = null, $courseid = SITEID) {
1072      global $CFG, $USER;
1073      $url = null;
1074      if (isset($CFG->grade_mygrades_report) && $CFG->grade_mygrades_report != 'external') {
1075          if (isset($userid) && $USER->id != $userid) {
1076              // Send to the gradebook report.
1077              $url = new moodle_url('/grade/report/' . $CFG->grade_mygrades_report . '/index.php',
1078                      array('id' => $courseid, 'userid' => $userid));
1079          } else {
1080              $url = new moodle_url('/grade/report/' . $CFG->grade_mygrades_report . '/index.php');
1081          }
1082      } else if (isset($CFG->grade_mygrades_report) && $CFG->grade_mygrades_report == 'external'
1083              && !empty($CFG->gradereport_mygradeurl)) {
1084          $url = $CFG->gradereport_mygradeurl;
1085      } else {
1086          $url = $CFG->wwwroot;
1087      }
1088      return $url;
1089  }
1090  
1091  /**
1092   * Check if a user has the permission to viewdetails in a shared course's context.
1093   *
1094   * @param object $user The other user's details.
1095   * @param object $course Use this course to see if we have permission to see this user's profile.
1096   * @param context $usercontext The user context if available.
1097   * @return bool true for ability to view this user, else false.
1098   */
1099  function user_can_view_profile($user, $course = null, $usercontext = null) {
1100      global $USER, $CFG;
1101  
1102      if ($user->deleted) {
1103          return false;
1104      }
1105  
1106      // If any of these four things, return true.
1107      // Number 1.
1108      if ($USER->id == $user->id) {
1109          return true;
1110      }
1111  
1112      // Number 2.
1113      if (empty($CFG->forceloginforprofiles)) {
1114          return true;
1115      }
1116  
1117      if (empty($usercontext)) {
1118          $usercontext = context_user::instance($user->id);
1119      }
1120      // Number 3.
1121      if (has_capability('moodle/user:viewdetails', $usercontext)) {
1122          return true;
1123      }
1124  
1125      // Number 4.
1126      if (has_coursecontact_role($user->id)) {
1127          return true;
1128      }
1129  
1130      if (isset($course)) {
1131          $sharedcourses = array($course);
1132      } else {
1133          $sharedcourses = enrol_get_shared_courses($USER->id, $user->id, true);
1134      }
1135      foreach ($sharedcourses as $sharedcourse) {
1136          $coursecontext = context_course::instance($sharedcourse->id);
1137          if (has_capability('moodle/user:viewdetails', $coursecontext)) {
1138              if (!groups_user_groups_visible($sharedcourse, $user->id)) {
1139                  // Not a member of the same group.
1140                  continue;
1141              }
1142              return true;
1143          }
1144      }
1145      return false;
1146  }
1147  
1148  /**
1149   * Returns users tagged with a specified tag.
1150   *
1151   * @param core_tag_tag $tag
1152   * @param bool $exclusivemode if set to true it means that no other entities tagged with this tag
1153   *             are displayed on the page and the per-page limit may be bigger
1154   * @param int $fromctx context id where the link was displayed, may be used by callbacks
1155   *            to display items in the same context first
1156   * @param int $ctx context id where to search for records
1157   * @param bool $rec search in subcontexts as well
1158   * @param int $page 0-based number of page being displayed
1159   * @return \core_tag\output\tagindex
1160   */
1161  function user_get_tagged_users($tag, $exclusivemode = false, $fromctx = 0, $ctx = 0, $rec = 1, $page = 0) {
1162      global $PAGE;
1163  
1164      if ($ctx && $ctx != context_system::instance()->id) {
1165          $usercount = 0;
1166      } else {
1167          // Users can only be displayed in system context.
1168          $usercount = $tag->count_tagged_items('core', 'user',
1169                  'it.deleted=:notdeleted', array('notdeleted' => 0));
1170      }
1171      $perpage = $exclusivemode ? 24 : 5;
1172      $content = '';
1173      $totalpages = ceil($usercount / $perpage);
1174  
1175      if ($usercount) {
1176          $userlist = $tag->get_tagged_items('core', 'user', $page * $perpage, $perpage,
1177                  'it.deleted=:notdeleted', array('notdeleted' => 0));
1178          $renderer = $PAGE->get_renderer('core', 'user');
1179          $content .= $renderer->user_list($userlist, $exclusivemode);
1180      }
1181  
1182      return new core_tag\output\tagindex($tag, 'core', 'user', $content,
1183              $exclusivemode, $fromctx, $ctx, $rec, $page, $totalpages);
1184  }


Generated: Thu Aug 11 10:00:09 2016 Cross-referenced by PHPXref 0.7.1