diff --git a/CRM/Civimoodle/Util.php b/CRM/Civimoodle/Util.php index 869af75521349dd596ac16fdfe0802853fbbdc8e..d1d5e2de705764a02449ae42d659abfe0e9e9324 100644 --- a/CRM/Civimoodle/Util.php +++ b/CRM/Civimoodle/Util.php @@ -22,25 +22,83 @@ class CRM_Civimoodle_Util { } } + /** + * Function used to fetch moodle user ID using contact's custom field moodle_credential.user_id OR moodle_credential.username OR primary email address + * + * @param int $contactID + * Array of course IDs + * @param bool $updateUserID + * If set to true then update the moodle user ID if not set and found on basis of username or primary email + * @return int|null $moodleUserID + */ + public static function getMoodleUserID($contactID, $updateUserID = FALSE) { + $moodleUserID = NULL; + $contact = \Civi\Api4\Contact::get(FALSE) + ->addSelect('moodle_credential.user_id', 'moodle_credential.username', 'email_primary.email') + ->addWhere('moodle_credential.username', 'IS NOT EMPTY') + ->addWhere('id', '=', $contactID) + ->execute() + ->first(); + $moodleUserID = $contact['moodle_credential.user_id']; + + if (!$moodleUserID && (!empty($contact['moodle_credential.username']) || !empty($contact['email_primary.email']))) { + $criterias = [ + 'email' => $contact['email_primary.email'], + 'username' => $contact['moodle_credential.username'], + ]; + + foreach ($criterias as $key => $value) { + if ($value) { + $criteria = [ + 'key' => $key, + 'value' => $value, + ]; + list($isError, $response) = CRM_Civimoodle_API::singleton($criteria, TRUE)->getUser(); + $response = json_decode($response, TRUE); + + // if user found on given 'username' value + if (!empty($response['users'])) { + $moodleUserID = $response['users'][0]['id']; + } + // break the loop means avoid next criteria search on basis of email if user ID is found + if (!empty($moodleUserID)) { + if ($updateUserID) { + \Civi\Api4\Contact::update(FALSE) + ->addValue('moodle_credential.user_id', $moodleUserID) + ->addWhere('id', '=', $contactID) + ->execute(); + } + break; + } + } + } + } + + return $moodleUserID; + } + + public static function getRandomString($n) { + $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^&*()_+=-}{[}]\|;:<>?/'; + $randomString = ''; + + for ($i = 0; $i < $n; $i++) { + $index = random_int(0, strlen($characters) - 1); + $randomString .= $characters[$index]; + } + + return $randomString; + } + /** * Function used to create/update moodle user * * @param int $contactID * CiviCRM contact ID - * @param bool $ignoreCMSCredential * - * @return int $userID - * Moodle user ID + * @return int Moodle user ID */ - public static function createUser($contactID, $ignoreCMSCredential = FALSE) { + public static function createUser($contactID) { $userIDKey = self::getCustomFieldKey('user_id'); - - if (Civi::settings()->get('moodle_cms_credential') && !$ignoreCMSCredential) { - return civicrm_api3('Contact', 'getvalue', array( - 'return' => $userIDKey, - 'id' => $contactID, - )); - } $usernameKey = self::getCustomFieldKey('username'); $passwordKey = self::getCustomFieldKey('password'); $result = civicrm_api3('Contact', 'getsingle', array( @@ -54,31 +112,21 @@ class CRM_Civimoodle_Util { ), 'id' => $contactID, )); + $defaultUserName = strtolower($result['first_name'] . $result['last_name'] . $contactID); $userParams = array( 'firstname' => $result['first_name'], 'lastname' => $result['last_name'], 'email' => $result['email'], - 'username' => CRM_Utils_Array::value($usernameKey, $result, $result['first_name']), - 'password' => CRM_Utils_Array::value($passwordKey, $result, 'changeme'), + 'username' => CRM_Utils_Array::value($usernameKey, $result, $defaultUserName), ); - if (Civi::settings()->get('moodle_cms_credential')) { - global $user; - if (!empty($user) && !empty($user->uid)) { - $userParams['username'] = !empty($user->name) ? $user->name : $user->mail; - $userParams['password'] = 'changeme'; - if (empty($result[$usernameKey])) { - $result[$usernameKey] = $userParams['username']; - } - } - } $userID = CRM_Utils_Array::value($userIDKey, $result); // If user ID not found, meaning if moodle user is not created or user ID not found in CiviCRM - $criterias = array( - 'username' => $usernameKey, - 'email' => 'email', - ); if (empty($userID)) { + $criterias = array( + 'username' => $usernameKey, + 'email' => 'email', + ); // fetch user ID on basis of username OR email foreach ($criterias as $key => $value) { $criteria = array( @@ -106,6 +154,8 @@ class CRM_Civimoodle_Util { } else { // create user by calling core_user_create_users + $userParams['username'] = $defaultUserName; + $userParams['password'] = self::getRandomString(8); list($isError, $response) = CRM_Civimoodle_API::singleton($userParams, TRUE)->createUser(); $response = json_decode($response, TRUE); if (!$isError && !empty($response[0])) { @@ -117,7 +167,8 @@ class CRM_Civimoodle_Util { civicrm_api3('Contact', 'create', array( 'id' => $contactID, $userIDKey => $userID, - $passwordKey => '', //clean password if user ID is stored + $usernameKey => $userParams['username'], + $passwordKey => $userParams['password'], )); return $userID; @@ -219,4 +270,89 @@ class CRM_Civimoodle_Util { $enrollmentCreate->execute(); } + //Changed _updateDrupalUserDetails function to the follow + /* + public static function updateCMSUserDetails($ufID, $contactParams, $create = FALSE){ + $cms = CRM_Core_Config::singleton()->userFramework; + + //using switch case for Drupal and WordPress + switch ($cms) { + case 'Drupal': + // Fetch user details + //Changed user_load to directory for Drupal 10 compatibility + $user = \Drupal\user\Entity\User::load($ufID); + if (!$user) { + // If the user doesn't exist, create a new user + $user = \Drupal\user\Entity\User::create([ + 'uid' => $ufID, + 'field_first_name' => NULL, + 'field_last_name' => NULL, + ]); + } + + $matchingParams = [ + 'field_first_name', + 'field_last_name', + ]; + + // Set the fields based on the contact parameters + foreach ($matchingParams as $paramName) { + if (!empty($contactParams[$paramName]) || $create) { + // Set the field values + $user->set($paramName, CRM_Utils_Array::value($paramName, $contactParams)); + } + } + + // Save the user + //Changed from user_save to user->save(); + $user->save(); + + return $user; + break; + + case 'WordPress': + // fetch user details + $user = get_userdata($ufID); + $userEditParams = (array) $user; + //wordpress doesn't have field_ prefix + $matchingParams = [ + 'first_name', + 'last_name', + ]; + //wordpress uses ID isntead of uif + if ($create) { + $userEditParams = [ + 'ID' => $ufID, + 'first_name' => NULL, + 'last_name' => NULL, + ]; + } + //I made this function using chatGPT as wordpress doesn't use nested arrays + foreach ($matchingParams as $paramName) { + if (!empty($user->$paramName) || $create) { + $paramValue = CRM_Utils_Array::value($paramName, $contactParams); + if ($create) { + $userEditParams[$paramName] = $paramValue; + } elseif (empty(get_user_meta($ufID, $paramName, true))) { + update_user_meta($ufID, $paramName, $paramValue); + } + } + } + + //using wp_udate_user instead of user -> save + if ($create) { + wp_update_user($userEditParams); + } + + return get_userdata($ufID); + break; + + default: + //default message if not Wordpress or Drupal + CRM_Core_Error::debug_log_message('Unsupported CMS'); + break; + } + } +*/ + } diff --git a/civimoodle.php b/civimoodle.php index 6ca14d8aaf1e44d5acdae0bab9a6049a907a39d8..0d36d35ea7c2927ba04e3264883d06461c92ed88 100644 --- a/civimoodle.php +++ b/civimoodle.php @@ -44,7 +44,7 @@ function civimoodle_civicrm_container(\Symfony\Component\DependencyInjection\Con 'type' => ['*memory*', 'SqlGroup', 'ArrayCache'], ], ] - ))->setFactory('CRM_Utils_Cache::create')->setPublic(true); + ))->setFactory('CRM_Utils_Cache::create')->setPublic(TRUE); } /** @@ -67,42 +67,15 @@ function civimoodle_civicrm_fieldOptions($entity, $field, &$options, $params) { } function civimoodle_civicrm_buildForm($formName, &$form) { - if ($formName == 'CRM_Event_Form_Registration_ThankYou' || $formName == 'CRM_Event_Form_Registration_ParticipantConfirm') { - //Changed 'user_load' to directory '\Drupal\user\Entity\User' for Drupal 10 compatibility - if (Civi::settings()->get('moodle_cms_credential')) { - $user = \Drupal\user\Entity\User::load($ufID); - global $user; - if (!empty($user->uid)) { - $ufID = $user->uid; - $contactID = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_UFMatch', $ufID, 'contact_id', 'uf_id'); - $result = civicrm_api3('Contact', 'getsingle', array( - 'return' => array( - 'email', - 'first_name', - 'last_name', - ), - 'id' => $contactID, - )); - //Changed to new function - updateCMSUserDetails($ufID, $result, TRUE); - - $courses = Civi::cache('civiMoodle')->get('moodle-courses'); - if (!empty($courses)) { - $contactID = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_UFMatch', $ufID, 'contact_id', 'uf_id'); - $userID = CRM_Civimoodle_Util::createUser($contactID, TRUE); - CRM_Civimoodle_Util::enrollUser($courses, $userID); - - $userIDKey = CRM_Civimoodle_Util::getCustomFieldKey('user_id'); - //update user id in contact - civicrm_api3('Contact', 'create', array( - 'id' => $contactID, - $userIDKey => $userID, - $passwordKey => '', //clean password if user ID is stored - )); - - Civi::cache('civiMoodle')->delete('moodle-courses'); - } - } + if ($formName == 'CRM_Event_Form_Registration_ThankYou') { + $courses = Civi::cache('civiMoodle')->get('moodle-courses'); + if (empty($courses)) { + $courses = CRM_Civimoodle_Util::getCoursesFromEvent($form->_eventId); + } + if (!empty($courses) && !empty($form->_values['participant']['contact_id'])) { + $userID = CRM_Civimoodle_Util::getMoodleUserID($form->_values['participant']['contact_id'], TRUE); + CRM_Civimoodle_Util::enrollUser($courses, $userID); + Civi::cache('civiMoodle')->delete('moodle-courses'); } } } @@ -120,265 +93,19 @@ function civimoodle_civicrm_post($op, $objectName, $objectId, &$objectRef) { \CRM_Civimoodle_Util::createLocalEnrollmentRecords($objectRef->contact_id, $courses, $objectId, $objectRef->register_date); - // now register the partipant at Moodle - - //Changed 'user_load' to directory '\Drupal\user\Entity\User' for Drupal 10 compatibility - if (Civi::settings()->get('moodle_cms_credential')) { - $user = \Drupal\user\Entity\User::load($ufID); - $userIDKey = CRM_Civimoodle_Util::getCustomFieldKey('user_id'); - $passwordKey = CRM_Civimoodle_Util::getCustomFieldKey('password'); - $result = civicrm_api3('Contact', 'getsingle', array( - 'return' => array( - 'email', - 'first_name', - 'last_name', - $userIDKey, - ), - 'id' => $objectRef->contact_id, - )); - - $ufID = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_UFMatch', $objectRef->contact_id, 'uf_id', 'contact_id'); - - if (!$ufID) { - global $user; - if (!empty($user) && !empty($user->uid)) { - civicrm_api3('UFMatch', 'create', array( - 'contact_id' => $objectRef->contact_id, - 'uf_id' => $user->uid, - 'uf_name' => $user->mail, - 'domain_id' => CRM_Core_BAO_Domain::getDomain(), - )); - $ufID = $user->uid; - } - } - - $userParams = [ - 'firstname' => $result['first_name'], - 'lastname' => $result['last_name'], - 'email' => CRM_Utils_Array::value('email', $result), - 'username' => $result['first_name'], - 'password' => 'changeme', - ]; - if ($ufID) { - $cms = CRM_Core_Config::singleton()->userFramework; - //Added switch case here to make compatible with wordpress aswell - switch ($cms) { - case 'Drupal': - // Update Drupal user details - $drupaluser = _updateDrupalUserDetails($ufID, $result); - $userParams = array_merge($userParams, [ - 'email' => $drupaluser->mail, - 'username' => $drupaluser->name, - ]); - break; - - case 'WordPress': - // Update WordPress user details - $wordpressUser = get_user_by('id', $ufID); - if ($wordpressUser) { - $userParams = array_merge($userParams, [ - 'email' => $wordpressUser->user_email, - 'username' => $wordpressUser->user_login, - ]); - } - break; - - /* For Joomla implementation when needed - case 'Joomla': - break; - */ - default: - //default message if not Wordpress or Drupal - CRM_Core_Error::debug_log_message('Unsupported CMS'); - break; - } - } - - $userID = CRM_Utils_Array::value($userIDKey, $result); - - if (!empty($userID)) { - // If user ID not found, meaning if moodle user is not created or user ID not found in CiviCRM - $criterias = array( - 'username' => $usernameKey, - 'email' => 'email', - ); - // fetch user ID on basis of username OR email - foreach ($criterias as $key => $value) { - $criteria = array( - 'key' => $key, - 'value' => $result[$value], - ); - list($isError, $response) = CRM_Civimoodle_API::singleton($criteria, TRUE)->getUser(); - $response = json_decode($response, TRUE); - - // if user found on given 'username' value - if (!empty($response['users'])) { - $userID = $response['users'][0]['id']; - } - // break the loop means avoid next criteria search on basis of email if user ID is found - if (!empty($userID)) { - break; - } - } - } - - if (!empty($userID)) { - // update user by calling core_user_update_users - $updateParams = array_merge($userParams, array('id' => $userID)); - list($isError, $response) = CRM_Civimoodle_API::singleton($updateParams, TRUE)->updateUser(); - } - - //update user id in contact - civicrm_api3('Contact', 'create', array( - 'id' => $objectRef->contact_id, - $userIDKey => $userID, - $passwordKey => '', //clean password if user ID is stored - )); - } - else { + // Fetch moodle user ID, second parameter is to update custom field moodle_credential.user_id if user_id is not set + $userID = CRM_Civimoodle_Util::getMoodleUserID($objectRef->contact_id, TRUE); + if (!$userID) { // create/update moodle user based on CiviCRM contact ID information $userID = CRM_Civimoodle_Util::createUser($objectRef->contact_id); } - // enroll user of given $userID to multiple courses $courses - if (!empty($userID)) { - CRM_Civimoodle_Util::enrollUser($courses, $userID); - } - else { - Civi::cache('civiMoodle')->delete('moodle-courses'); - Civi::cache('civiMoodle')->set('moodle-courses', $courses); - } + Civi::cache('civiMoodle')->delete('moodle-courses'); + Civi::cache('civiMoodle')->set('moodle-courses', $courses); } } } -//Changed _updateDrupalUserDetails function to the follow -function updateCMSUserDetails($ufID, $contactParams, $create = FALSE){ - $cms = CRM_Core_Config::singleton()->userFramework; - - //using switch case fo drupal, wordpress and joomla - switch ($cms) { - case 'Drupal': - // Fetch user details - //Changed user_load to directory for Drupal 10 compatibility - $user = \Drupal\user\Entity\User::load($ufID); - if (!$user) { - // If the user doesn't exist, create a new user - $user = \Drupal\user\Entity\User::create([ - 'uid' => $ufID, - 'field_first_name' => NULL, - 'field_last_name' => NULL, - ]); - } - - $matchingParams = [ - 'field_first_name', - 'field_last_name', - ]; - - // Set the fields based on the contact parameters - foreach ($matchingParams as $paramName) { - if (!empty($contactParams[$paramName]) || $create) { - // Set the field values - $user->set($paramName, CRM_Utils_Array::value($paramName, $contactParams)); - } - } - - // Save the user - //Changed from user_save to user->save(); - $user->save(); - - return $user; - break; - - //same concept and code as drupal case but changed to wordpress syntax - case 'WordPress': - // fetch user details - $user = get_userdata($ufID); - $userEditParams = (array) $user; - //wordpress doesn't have field_ prefix - $matchingParams = [ - 'first_name', - 'last_name', - ]; - //wordpress uses ID isntead of uif - if ($create) { - $userEditParams = [ - 'ID' => $ufID, - 'first_name' => NULL, - 'last_name' => NULL, - ]; - } - //I made this function using chatGPT as wordpress doesn't use nested arrays - foreach ($matchingParams as $paramName) { - if (!empty($user->$paramName) || $create) { - $paramValue = CRM_Utils_Array::value($paramName, $contactParams); - if ($create) { - $userEditParams[$paramName] = $paramValue; - } elseif (empty(get_user_meta($ufID, $paramName, true))) { - update_user_meta($ufID, $paramName, $paramValue); - } - } - } - - //using wp_udate_user instead of user -> save - if ($create) { - wp_update_user($userEditParams); - } - - return get_userdata($ufID); - break; - - /* For Joomla implementation when needed - case 'Joomla': - break; - */ - - default: - //default message if not Wordpress or Drupal - CRM_Core_Error::debug_log_message('Unsupported CMS'); - break; - } -} - -/* Original Drupal User Function -function _updateDrupalUserDetails($ufID, $contactParams, $create = FALSE) { - // fetch user details - $user = user_load($ufID); - $userEditParams = (array) $user; - $matchingParams = [ - 'field_first_name', - 'field_last_name', - ]; - if ($create) { - $userEditParams = [ - 'uid' => $ufID, - 'field_first_name' => NULL, - 'field_last_name' => NULL, - ]; - } - foreach($userEditParams as $attribute => $value) { - if (in_array($attribute, $matchingParams) && (!empty($user->$attribute) || $create)) { - $paramName = str_replace('field_', '', $attribute); - if ($create) { - $userEditParams[$attribute]['und'] = [ - 0 => [ - 'value' => CRM_Utils_Array::value($paramName, $contactParams), - ], - ]; - } - elseif (empty($userEditParams[$attribute]['und'][0]['value'])) { - $userEditParams[$attribute]['und'][0]['value'] = CRM_Utils_Array::value($paramName, $contactParams); - } - } - } - - user_save($user, $userEditParams); - - return $user; -} -*/ - /** * Implements hook_civicrm_validateForm(). * diff --git a/settings/civimoodle.setting.php b/settings/civimoodle.setting.php index 88b45840339553e326a73ec0c6604184ed5b4c20..e076eca9ce281e2c4744e6076cf98e44990ee2fa 100644 --- a/settings/civimoodle.setting.php +++ b/settings/civimoodle.setting.php @@ -35,21 +35,4 @@ return [ ] ], ], - 'moodle_cms_credential' => [ - 'name' => 'moodle_cms_credential', - 'type' => 'Boolean', - 'html_type' => 'text', - 'quick_form_type' => 'YesNo', - 'default' => '', - 'is_domain' => 1, - 'is_contact' => 0, - 'title' => E::ts('Do you want to use CMS account credentials for Moodle?'), - 'description' => '', - 'html_attributes' => [], - 'settings_pages' => [ - 'moodle' => [ - 'weight' => 20, - ] - ], - ], ];