Permalink
Browse files

MDL-34286 conditionlib: Fixed up loading of user profile data for cus…

…tom field conditions
  • Loading branch information...
Sam Hemelryk
Sam Hemelryk committed Jul 13, 2012
1 parent 3294034 commit 2ede466e5faa800511b2dc04995cad61b7cc88f7
Showing with 89 additions and 57 deletions.
  1. +89 −57 lib/conditionlib.php
View
@@ -374,7 +374,8 @@ public static function update_section_from_form($section, $fromform, $wipefirst=
protected $idfieldname;
/** @var array */
protected $usergroupings;
-
+ /** @var array An array of custom profile field ids => to their shortname */
+ protected $customprofilefields = null;
/**
* Constructs with item details.
*
@@ -1002,8 +1003,8 @@ public function is_available(&$information, $grabthelot=false, $userid=0, $modin
// Check if user field condition
if (count($this->item->conditionsfield) > 0) {
foreach ($this->item->conditionsfield as $field => $details) {
- $uservalue = $this->get_cached_user_profile_field($userid, $field, $grabthelot);
- if (!$fieldconditionmet = $this->is_field_condition_met($details->operator, $uservalue, $details->value)) {
+ $uservalue = $this->get_cached_user_profile_field($userid, $field);
+ if (!$this->is_field_condition_met($details->operator, $uservalue, $details->value)) {
// Set available to false
$available = false;
$a = new stdClass();
@@ -1185,7 +1186,15 @@ private function get_cached_grade_score($gradeitemid, $grabthelot=false, $userid
* @return boolean
*/
private function is_field_condition_met($operator, $uservalue, $value) {
+ if ($uservalue === false) {
+ // If the user value is false this is an instant fail.
+ // All user values come from the database as either data or the default.
+ // They will always be a string.
+ return false;
+ }
$fieldconditionmet = true;
+ // Just to be doubly sure it is a string.
+ $uservalue = (string)$uservalue;
switch($operator) {
case OP_CONTAINS: // contains
$pos = strpos($uservalue, $value);
@@ -1233,71 +1242,94 @@ private function is_field_condition_met($operator, $uservalue, $value) {
*
* @param int $userid set if requesting grade for a different user (does not use cache)
* @param int $fieldid the user profile field id
- * @param bool $grabthelot If true, grabs all the user profile fields for current user on this course, so that later ones come from cache
* @return string the user value, or false if user does not have a user field value yet
*/
- private function get_cached_user_profile_field($userid, $fieldid, $grabthelot) {
- global $USER, $DB, $SESSION;
- // Check if the field is a custom profile field
- $iscustomprofilefield = is_numeric($fieldid) ? true : false;
- if ($userid == 0 || $userid == $USER->id) {
- if ($iscustomprofilefield) {
- // For current user, go via cache in session
- if (empty($SESSION->userfieldcache) || $SESSION->userfieldcacheuserid != $USER->id) {
- $SESSION->userfieldcache = array();
- $SESSION->userfieldcacheuserid = $USER->id;
- }
- if (!array_key_exists($fieldid, $SESSION->userfieldcache)) {
- if ($grabthelot) {
- // Get all custom profile field values for user
- $sql = "SELECT uf.id, ud.data
- FROM {user_info_field} uf
- LEFT JOIN {user_info_data} ud ON uf.id = ud.fieldid
- WHERE ud.userid = :userid";
- if ($records = $DB->get_records_sql($sql, array('userid' => $USER->id))) {
- foreach ($records as $r) {
- $SESSION->userfieldcache[$r->id] = $r->data;
- }
- }
- } else {
- // Just get specified user field
- $sql = "SELECT ud.data
- FROM {user_info_data} ud
- INNER JOIN {user_info_field} uf ON ud.fieldid = uf.id
- WHERE uf.id = :fieldid
- AND ud.userid = :userid";
- if ($record = $DB->get_record_sql($sql, array('fieldid' => $fieldid, 'userid' => $USER->id))) {
- $field = $record->data;
- } else {
- $field = false;
- }
- $SESSION->userfieldcache[$fieldid] = $field;
- }
- }
- if (!empty($SESSION->userfieldcache[$fieldid])) {
- return $SESSION->userfieldcache[$fieldid];
+ private function get_cached_user_profile_field($userid, $fieldid) {
+ global $USER, $DB, $CFG;
+
+ if ($userid === 0) {
+ // Map out userid = 0 to the current user
+ $userid = $USER->id;
+ }
+ $iscurrentuser = $USER->id == $userid;
+
+ if (isguestuser($userid)) {
+ // Must be logged in and can't be the guest. (this should never happen anyway)
+ return false;
+ }
+
+ // Custom profile fields will be numeric, there are no numeric standard profile fields so this is not a problem.
+ $iscustomprofilefield = is_numeric($fieldid);
+ if ($iscustomprofilefield) {
+ // As its a custom profile field we need to map the id back to the actual field.
+ // We'll also preload all of the other custom profile fields just in case and ensure we have the
+ // default value available as well.
+ if ($this->customprofilefields === null) {
+ $this->customprofilefields = $DB->get_records('user_info_field', null, 'sortorder ASC, id ASC', 'id, shortname, defaultdata');
+ }
+ if (!array_key_exists($fieldid, $this->customprofilefields)) {
+ // No such field exists.
+ // This shouldn't normally happen but occur if things go wrong when deleting a custom profile field
+ // or when restoring a backup of a course with user profile field conditions.
+ return false;
+ }
+ $field = $this->customprofilefields[$fieldid]->shortname;
+ } else {
+ $field = $fieldid;
+ }
+
+ // If its the current user than most likely we will be able to get this information from $USER.
+ // If its a regular profile field then it should already be available, if not then we have a mega problem.
+ // If its a custom profile field then it should be available but may not be. If it is then we use the value
+ // available, otherwise we load all custom profile fields into a temp object and refer to that.
+ // Noting its not going be great for performance if we have to use the temp object as it involves loading the
+ // custom profile field API and classes.
+ if ($iscurrentuser) {
+ if (!$iscustomprofilefield) {
+ if (property_exists($USER, $field)) {
+ return $USER->{$field};
} else {
- return false;
+ // Unknown user field. This should not happen.
+ throw new coding_exception('Requested user profile field does not exist');
}
- } else {
- return $USER->$fieldid;
}
+ // Checking if the custom profile fields are already available.
+ if (!isset($USER->profile)) {
+ // Drat! they're not. We need to use a temp object and load them.
+ // We don't use $USER as the profile fields are loaded into the object.
+ $user = new stdClass;
+ $user->id = $USER->id;
+ // This should ALWAYS be set, but just in case we check.
+ require_once($CFG->dirroot.'/user/profile/lib.php');
+ profile_load_custom_fields($user);
+ if (array_key_exists($field, $user->profile)) {
+ return $user->profile[$field];
+ }
+ } else if (array_key_exists($field, $USER->profile)) {
+ // Hurrah they're available, this is easy.
+ return $USER->profile[$field];
+ }
+ // The profile field doesn't exist.
+ return false;
} else {
+ // Loading for another user.
if ($iscustomprofilefield) {
- $sql = "SELECT ud.data
- FROM {user_info_data} ud
- INNER JOIN {user_info_field} uf ON ud.fieldid = uf.id
- WHERE uf.id = :fieldid
- AND ud.userid = :userid";
- if ($record = $DB->get_record_sql($sql, array('fieldid' => $fieldid, 'userid' => $userid))) {
- return $record->data;
+ // Fetch the data for the field. Noting we keep this query simple so that Database caching takes care of performance
+ // for us (this will likely be hit again).
+ // We are able to do this because we've already pre-loaded the custom fields.
+ $data = $DB->get_field('user_info_data', 'data', array('userid' => $userid, 'fieldid' => $fieldid), IGNORE_MISSING);
+ // If we have data return that, otherwise return the default.
+ if ($data !== false) {
+ return $data;
+ } else {
+ return $this->customprofilefields[$field]->defaultdata;
}
} else {
- return $DB->get_field('user', $fieldid, array('id' => $userid), MUST_EXIST);
+ // Its a standard field, retrieve it from the user.
+ return $DB->get_field('user', $field, array('id' => $userid), MUST_EXIST);
}
- // If it reaches here, then no matches found
- return false;
}
+ return false;
}
/**

0 comments on commit 2ede466

Please sign in to comment.