| 1 | efrain | 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 |  * Database enrolment plugin.
 | 
        
           |  |  | 19 |  *
 | 
        
           |  |  | 20 |  * This plugin synchronises enrolment and roles with external database table.
 | 
        
           |  |  | 21 |  *
 | 
        
           |  |  | 22 |  * @package    enrol_database
 | 
        
           |  |  | 23 |  * @copyright  2010 Petr Skoda {@link http://skodak.org}
 | 
        
           |  |  | 24 |  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 | 
        
           |  |  | 25 |  */
 | 
        
           |  |  | 26 |   | 
        
           |  |  | 27 | defined('MOODLE_INTERNAL') || die();
 | 
        
           |  |  | 28 |   | 
        
           |  |  | 29 | /**
 | 
        
           |  |  | 30 |  * Database enrolment plugin implementation.
 | 
        
           |  |  | 31 |  * @author  Petr Skoda - based on code by Martin Dougiamas, Martin Langhoff and others
 | 
        
           |  |  | 32 |  * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 | 
        
           |  |  | 33 |  */
 | 
        
           |  |  | 34 | class enrol_database_plugin extends enrol_plugin {
 | 
        
           |  |  | 35 |     /**
 | 
        
           |  |  | 36 |      * Is it possible to delete enrol instance via standard UI?
 | 
        
           |  |  | 37 |      *
 | 
        
           |  |  | 38 |      * @param stdClass $instance
 | 
        
           |  |  | 39 |      * @return bool
 | 
        
           |  |  | 40 |      */
 | 
        
           |  |  | 41 |     public function can_delete_instance($instance) {
 | 
        
           |  |  | 42 |         $context = context_course::instance($instance->courseid);
 | 
        
           |  |  | 43 |         if (!has_capability('enrol/database:config', $context)) {
 | 
        
           |  |  | 44 |             return false;
 | 
        
           |  |  | 45 |         }
 | 
        
           |  |  | 46 |         if (!enrol_is_enabled('database')) {
 | 
        
           |  |  | 47 |             return true;
 | 
        
           |  |  | 48 |         }
 | 
        
           |  |  | 49 |         if (!$this->get_config('dbtype') or !$this->get_config('remoteenroltable') or !$this->get_config('remotecoursefield') or !$this->get_config('remoteuserfield')) {
 | 
        
           |  |  | 50 |             return true;
 | 
        
           |  |  | 51 |         }
 | 
        
           |  |  | 52 |   | 
        
           |  |  | 53 |         //TODO: connect to external system and make sure no users are to be enrolled in this course
 | 
        
           |  |  | 54 |         return false;
 | 
        
           |  |  | 55 |     }
 | 
        
           |  |  | 56 |   | 
        
           |  |  | 57 |     /**
 | 
        
           |  |  | 58 |      * Is it possible to hide/show enrol instance via standard UI?
 | 
        
           |  |  | 59 |      *
 | 
        
           |  |  | 60 |      * @param stdClass $instance
 | 
        
           |  |  | 61 |      * @return bool
 | 
        
           |  |  | 62 |      */
 | 
        
           |  |  | 63 |     public function can_hide_show_instance($instance) {
 | 
        
           |  |  | 64 |         $context = context_course::instance($instance->courseid);
 | 
        
           |  |  | 65 |         return has_capability('enrol/database:config', $context);
 | 
        
           |  |  | 66 |     }
 | 
        
           |  |  | 67 |   | 
        
           |  |  | 68 |     /**
 | 
        
           |  |  | 69 |      * Does this plugin allow manual unenrolment of a specific user?
 | 
        
           |  |  | 70 |      * Yes, but only if user suspended...
 | 
        
           |  |  | 71 |      *
 | 
        
           |  |  | 72 |      * @param stdClass $instance course enrol instance
 | 
        
           |  |  | 73 |      * @param stdClass $ue record from user_enrolments table
 | 
        
           |  |  | 74 |      *
 | 
        
           |  |  | 75 |      * @return bool - true means user with 'enrol/xxx:unenrol' may unenrol this user, false means nobody may touch this user enrolment
 | 
        
           |  |  | 76 |      */
 | 
        
           |  |  | 77 |     public function allow_unenrol_user(stdClass $instance, stdClass $ue) {
 | 
        
           |  |  | 78 |         if ($ue->status == ENROL_USER_SUSPENDED) {
 | 
        
           |  |  | 79 |             return true;
 | 
        
           |  |  | 80 |         }
 | 
        
           |  |  | 81 |   | 
        
           |  |  | 82 |         return false;
 | 
        
           |  |  | 83 |     }
 | 
        
           |  |  | 84 |   | 
        
           |  |  | 85 |     /**
 | 
        
           |  |  | 86 |      * Forces synchronisation of user enrolments with external database,
 | 
        
           |  |  | 87 |      * does not create new courses.
 | 
        
           |  |  | 88 |      *
 | 
        
           |  |  | 89 |      * @param stdClass $user user record
 | 
        
           |  |  | 90 |      * @return void
 | 
        
           |  |  | 91 |      */
 | 
        
           |  |  | 92 |     public function sync_user_enrolments($user) {
 | 
        
           |  |  | 93 |         global $CFG, $DB;
 | 
        
           |  |  | 94 |   | 
        
           |  |  | 95 |         // We do not create courses here intentionally because it requires full sync and is slow.
 | 
        
           |  |  | 96 |         if (!$this->get_config('dbtype') or !$this->get_config('remoteenroltable') or !$this->get_config('remotecoursefield') or !$this->get_config('remoteuserfield')) {
 | 
        
           |  |  | 97 |             return;
 | 
        
           |  |  | 98 |         }
 | 
        
           |  |  | 99 |   | 
        
           |  |  | 100 |         $table            = $this->get_config('remoteenroltable');
 | 
        
           |  |  | 101 |         $coursefield      = trim($this->get_config('remotecoursefield'));
 | 
        
           |  |  | 102 |         $userfield        = trim($this->get_config('remoteuserfield'));
 | 
        
           |  |  | 103 |         $rolefield        = trim($this->get_config('remoterolefield'));
 | 
        
           |  |  | 104 |         $otheruserfield   = trim($this->get_config('remoteotheruserfield'));
 | 
        
           |  |  | 105 |   | 
        
           |  |  | 106 |         // Lowercased versions - necessary because we normalise the resultset with array_change_key_case().
 | 
        
           |  |  | 107 |         $coursefield_l    = strtolower($coursefield);
 | 
        
           |  |  | 108 |         $userfield_l      = strtolower($userfield);
 | 
        
           |  |  | 109 |         $rolefield_l      = strtolower($rolefield);
 | 
        
           |  |  | 110 |         $otheruserfieldlower = strtolower($otheruserfield);
 | 
        
           |  |  | 111 |   | 
        
           |  |  | 112 |         $localrolefield   = $this->get_config('localrolefield');
 | 
        
           |  |  | 113 |         $localuserfield   = $this->get_config('localuserfield');
 | 
        
           |  |  | 114 |         $localcoursefield = $this->get_config('localcoursefield');
 | 
        
           |  |  | 115 |   | 
        
           |  |  | 116 |         $unenrolaction    = $this->get_config('unenrolaction');
 | 
        
           |  |  | 117 |         $defaultrole      = $this->get_config('defaultrole');
 | 
        
           |  |  | 118 |   | 
        
           |  |  | 119 |         $ignorehidden     = $this->get_config('ignorehiddencourses');
 | 
        
           |  |  | 120 |   | 
        
           |  |  | 121 |         if (!is_object($user) or !property_exists($user, 'id')) {
 | 
        
           |  |  | 122 |             throw new coding_exception('Invalid $user parameter in sync_user_enrolments()');
 | 
        
           |  |  | 123 |         }
 | 
        
           |  |  | 124 |   | 
        
           |  |  | 125 |         if (!property_exists($user, $localuserfield)) {
 | 
        
           |  |  | 126 |             debugging('Invalid $user parameter in sync_user_enrolments(), missing '.$localuserfield);
 | 
        
           |  |  | 127 |             $user = $DB->get_record('user', array('id'=>$user->id));
 | 
        
           |  |  | 128 |         }
 | 
        
           |  |  | 129 |   | 
        
           |  |  | 130 |         // Create roles mapping.
 | 
        
           |  |  | 131 |         $allroles = get_all_roles();
 | 
        
           |  |  | 132 |         if (!isset($allroles[$defaultrole])) {
 | 
        
           |  |  | 133 |             $defaultrole = 0;
 | 
        
           |  |  | 134 |         }
 | 
        
           |  |  | 135 |         $roles = array();
 | 
        
           |  |  | 136 |         foreach ($allroles as $role) {
 | 
        
           |  |  | 137 |             $roles[$role->$localrolefield] = $role->id;
 | 
        
           |  |  | 138 |         }
 | 
        
           |  |  | 139 |   | 
        
           |  |  | 140 |         $roleassigns = array();
 | 
        
           |  |  | 141 |         $enrols = array();
 | 
        
           |  |  | 142 |         $instances = array();
 | 
        
           |  |  | 143 |   | 
        
           |  |  | 144 |         if (!$extdb = $this->db_init()) {
 | 
        
           |  |  | 145 |             // Can not connect to database, sorry.
 | 
        
           |  |  | 146 |             return;
 | 
        
           |  |  | 147 |         }
 | 
        
           |  |  | 148 |   | 
        
           |  |  | 149 |         // Read remote enrols and create instances.
 | 
        
           |  |  | 150 |         $sql = $this->db_get_sql($table, array($userfield=>$user->$localuserfield), array(), false);
 | 
        
           |  |  | 151 |   | 
        
           |  |  | 152 |         if ($rs = $extdb->Execute($sql)) {
 | 
        
           |  |  | 153 |             if (!$rs->EOF) {
 | 
        
           |  |  | 154 |                 while ($fields = $rs->FetchRow()) {
 | 
        
           |  |  | 155 |                     $fields = array_change_key_case($fields, CASE_LOWER);
 | 
        
           |  |  | 156 |                     $fields = $this->db_decode($fields);
 | 
        
           |  |  | 157 |   | 
        
           |  |  | 158 |                     if (empty($fields[$coursefield_l])) {
 | 
        
           |  |  | 159 |                         // Missing course info.
 | 
        
           |  |  | 160 |                         continue;
 | 
        
           |  |  | 161 |                     }
 | 
        
           |  |  | 162 |                     if (!$course = $DB->get_record('course', array($localcoursefield=>$fields[$coursefield_l]), 'id,visible')) {
 | 
        
           |  |  | 163 |                         continue;
 | 
        
           |  |  | 164 |                     }
 | 
        
           |  |  | 165 |                     if (!$course->visible and $ignorehidden) {
 | 
        
           |  |  | 166 |                         continue;
 | 
        
           |  |  | 167 |                     }
 | 
        
           |  |  | 168 |   | 
        
           |  |  | 169 |                     if (empty($fields[$rolefield_l]) or !isset($roles[$fields[$rolefield_l]])) {
 | 
        
           |  |  | 170 |                         if (!$defaultrole) {
 | 
        
           |  |  | 171 |                             // Role is mandatory.
 | 
        
           |  |  | 172 |                             continue;
 | 
        
           |  |  | 173 |                         }
 | 
        
           |  |  | 174 |                         $roleid = $defaultrole;
 | 
        
           |  |  | 175 |                     } else {
 | 
        
           |  |  | 176 |                         $roleid = $roles[$fields[$rolefield_l]];
 | 
        
           |  |  | 177 |                     }
 | 
        
           |  |  | 178 |   | 
        
           |  |  | 179 |                     $roleassigns[$course->id][$roleid] = $roleid;
 | 
        
           |  |  | 180 |                     if (empty($fields[$otheruserfieldlower])) {
 | 
        
           |  |  | 181 |                         $enrols[$course->id][$roleid] = $roleid;
 | 
        
           |  |  | 182 |                     }
 | 
        
           |  |  | 183 |   | 
        
           |  |  | 184 |                     if ($instance = $DB->get_record('enrol', array('courseid'=>$course->id, 'enrol'=>'database'), '*', IGNORE_MULTIPLE)) {
 | 
        
           |  |  | 185 |                         $instances[$course->id] = $instance;
 | 
        
           |  |  | 186 |                         continue;
 | 
        
           |  |  | 187 |                     }
 | 
        
           |  |  | 188 |   | 
        
           | 1441 | ariadna | 189 |                     $timeout = 5;
 | 
        
           |  |  | 190 |                     $locktype = 'enrol_database_user_enrolments';
 | 
        
           |  |  | 191 |                     $resource = 'course:' . $course->id;
 | 
        
           |  |  | 192 |                     $lockfactory = \core\lock\lock_config::get_lock_factory($locktype);
 | 
        
           |  |  | 193 |                     if ($lock = $lockfactory->get_lock($resource, $timeout)) {
 | 
        
           |  |  | 194 |                         try {
 | 
        
           |  |  | 195 |                             $instance = $DB->get_record('enrol', ['enrol' => 'database', 'courseid' => $course->id]);
 | 
        
           |  |  | 196 |                             if (!$instance) {
 | 
        
           |  |  | 197 |                                 $enrolid = $this->add_instance($course);
 | 
        
           |  |  | 198 |                                 $instance = $DB->get_record('enrol', ['id' => $enrolid]);
 | 
        
           |  |  | 199 |                             }
 | 
        
           |  |  | 200 |                         } finally {
 | 
        
           |  |  | 201 |                             $lock->release();
 | 
        
           |  |  | 202 |                         }
 | 
        
           |  |  | 203 |                     } else {
 | 
        
           |  |  | 204 |                         // Attempt to reuse an existing record added by another process during race condition.
 | 
        
           |  |  | 205 |                         if ($instance = $DB->get_record('enrol', ['enrol' => 'database', 'courseid' => $course->id])) {
 | 
        
           |  |  | 206 |                             $instances[$course->id] = $instance;
 | 
        
           |  |  | 207 |                             continue;
 | 
        
           |  |  | 208 |                         } else {
 | 
        
           |  |  | 209 |                             // Give up.
 | 
        
           |  |  | 210 |                             throw new moodle_exception(
 | 
        
           |  |  | 211 |                                 'locktimeout',
 | 
        
           |  |  | 212 |                                 'enrol_database',
 | 
        
           |  |  | 213 |                                 '',
 | 
        
           |  |  | 214 |                                 null,
 | 
        
           |  |  | 215 |                                 'Could not create database enrolment instance for course ' . $course->id
 | 
        
           |  |  | 216 |                             );
 | 
        
           |  |  | 217 |                         }
 | 
        
           |  |  | 218 |                     }
 | 
        
           |  |  | 219 |                     $instances[$course->id] = $instance;
 | 
        
           | 1 | efrain | 220 |                 }
 | 
        
           |  |  | 221 |             }
 | 
        
           |  |  | 222 |             $rs->Close();
 | 
        
           |  |  | 223 |             $extdb->Close();
 | 
        
           |  |  | 224 |         } else {
 | 
        
           |  |  | 225 |             // Bad luck, something is wrong with the db connection.
 | 
        
           |  |  | 226 |             $extdb->Close();
 | 
        
           |  |  | 227 |             return;
 | 
        
           |  |  | 228 |         }
 | 
        
           |  |  | 229 |   | 
        
           |  |  | 230 |         // Enrol user into courses and sync roles.
 | 
        
           |  |  | 231 |         foreach ($roleassigns as $courseid => $roles) {
 | 
        
           |  |  | 232 |             if (!isset($instances[$courseid])) {
 | 
        
           |  |  | 233 |                 // Ignored.
 | 
        
           |  |  | 234 |                 continue;
 | 
        
           |  |  | 235 |             }
 | 
        
           |  |  | 236 |             $instance = $instances[$courseid];
 | 
        
           |  |  | 237 |   | 
        
           |  |  | 238 |             if (isset($enrols[$courseid])) {
 | 
        
           |  |  | 239 |                 if ($e = $DB->get_record('user_enrolments', array('userid' => $user->id, 'enrolid' => $instance->id))) {
 | 
        
           |  |  | 240 |                     // Reenable enrolment when previously disable enrolment refreshed.
 | 
        
           |  |  | 241 |                     if ($e->status == ENROL_USER_SUSPENDED) {
 | 
        
           |  |  | 242 |                         $this->update_user_enrol($instance, $user->id, ENROL_USER_ACTIVE);
 | 
        
           |  |  | 243 |                     }
 | 
        
           |  |  | 244 |                 } else {
 | 
        
           |  |  | 245 |                     $roleid = reset($enrols[$courseid]);
 | 
        
           |  |  | 246 |                     $this->enrol_user($instance, $user->id, $roleid, 0, 0, ENROL_USER_ACTIVE);
 | 
        
           |  |  | 247 |                 }
 | 
        
           |  |  | 248 |             }
 | 
        
           |  |  | 249 |   | 
        
           |  |  | 250 |             if (!$context = context_course::instance($instance->courseid, IGNORE_MISSING)) {
 | 
        
           |  |  | 251 |                 // Weird.
 | 
        
           |  |  | 252 |                 continue;
 | 
        
           |  |  | 253 |             }
 | 
        
           |  |  | 254 |             $current = $DB->get_records('role_assignments', array('contextid'=>$context->id, 'userid'=>$user->id, 'component'=>'enrol_database', 'itemid'=>$instance->id), '', 'id, roleid');
 | 
        
           |  |  | 255 |   | 
        
           |  |  | 256 |             $existing = array();
 | 
        
           |  |  | 257 |             foreach ($current as $r) {
 | 
        
           |  |  | 258 |                 if (isset($roles[$r->roleid])) {
 | 
        
           |  |  | 259 |                     $existing[$r->roleid] = $r->roleid;
 | 
        
           |  |  | 260 |                 } else {
 | 
        
           |  |  | 261 |                     role_unassign($r->roleid, $user->id, $context->id, 'enrol_database', $instance->id);
 | 
        
           |  |  | 262 |                 }
 | 
        
           |  |  | 263 |             }
 | 
        
           |  |  | 264 |             foreach ($roles as $rid) {
 | 
        
           |  |  | 265 |                 if (!isset($existing[$rid])) {
 | 
        
           |  |  | 266 |                     role_assign($rid, $user->id, $context->id, 'enrol_database', $instance->id);
 | 
        
           |  |  | 267 |                 }
 | 
        
           |  |  | 268 |             }
 | 
        
           |  |  | 269 |         }
 | 
        
           |  |  | 270 |   | 
        
           |  |  | 271 |         // Unenrol as necessary.
 | 
        
           |  |  | 272 |         $sql = "SELECT e.*, c.visible AS cvisible, ue.status AS ustatus
 | 
        
           |  |  | 273 |                   FROM {enrol} e
 | 
        
           |  |  | 274 |                   JOIN {course} c ON c.id = e.courseid
 | 
        
           |  |  | 275 |                   JOIN {role_assignments} ra ON ra.itemid = e.id
 | 
        
           |  |  | 276 |              LEFT JOIN {user_enrolments} ue ON ue.enrolid = e.id AND ue.userid = ra.userid
 | 
        
           |  |  | 277 |                  WHERE ra.userid = :userid AND e.enrol = 'database'";
 | 
        
           |  |  | 278 |         $rs = $DB->get_recordset_sql($sql, array('userid' => $user->id));
 | 
        
           |  |  | 279 |         foreach ($rs as $instance) {
 | 
        
           |  |  | 280 |             if (!$instance->cvisible and $ignorehidden) {
 | 
        
           |  |  | 281 |                 continue;
 | 
        
           |  |  | 282 |             }
 | 
        
           |  |  | 283 |   | 
        
           |  |  | 284 |             if (!$context = context_course::instance($instance->courseid, IGNORE_MISSING)) {
 | 
        
           |  |  | 285 |                 // Very weird.
 | 
        
           |  |  | 286 |                 continue;
 | 
        
           |  |  | 287 |             }
 | 
        
           |  |  | 288 |   | 
        
           |  |  | 289 |             if (!empty($enrols[$instance->courseid])) {
 | 
        
           |  |  | 290 |                 // We want this user enrolled.
 | 
        
           |  |  | 291 |                 continue;
 | 
        
           |  |  | 292 |             }
 | 
        
           |  |  | 293 |   | 
        
           |  |  | 294 |             // Deal with enrolments removed from external table
 | 
        
           |  |  | 295 |             if ($unenrolaction == ENROL_EXT_REMOVED_UNENROL) {
 | 
        
           |  |  | 296 |                 $this->unenrol_user($instance, $user->id);
 | 
        
           |  |  | 297 |   | 
        
           |  |  | 298 |             } else if ($unenrolaction == ENROL_EXT_REMOVED_KEEP) {
 | 
        
           |  |  | 299 |                 // Keep - only adding enrolments.
 | 
        
           |  |  | 300 |   | 
        
           |  |  | 301 |             } else if ($unenrolaction == ENROL_EXT_REMOVED_SUSPEND or $unenrolaction == ENROL_EXT_REMOVED_SUSPENDNOROLES) {
 | 
        
           |  |  | 302 |                 // Suspend users.
 | 
        
           |  |  | 303 |                 if ($instance->ustatus != ENROL_USER_SUSPENDED) {
 | 
        
           |  |  | 304 |                     $this->update_user_enrol($instance, $user->id, ENROL_USER_SUSPENDED);
 | 
        
           |  |  | 305 |                 }
 | 
        
           |  |  | 306 |                 if ($unenrolaction == ENROL_EXT_REMOVED_SUSPENDNOROLES) {
 | 
        
           |  |  | 307 |                     if (!empty($roleassigns[$instance->courseid])) {
 | 
        
           |  |  | 308 |                         // We want this "other user" to keep their roles.
 | 
        
           |  |  | 309 |                         continue;
 | 
        
           |  |  | 310 |                     }
 | 
        
           |  |  | 311 |                     role_unassign_all(array('contextid'=>$context->id, 'userid'=>$user->id, 'component'=>'enrol_database', 'itemid'=>$instance->id));
 | 
        
           |  |  | 312 |                 }
 | 
        
           |  |  | 313 |             }
 | 
        
           |  |  | 314 |         }
 | 
        
           |  |  | 315 |         $rs->close();
 | 
        
           |  |  | 316 |     }
 | 
        
           |  |  | 317 |   | 
        
           |  |  | 318 |     /**
 | 
        
           |  |  | 319 |      * Forces synchronisation of all enrolments with external database.
 | 
        
           |  |  | 320 |      *
 | 
        
           |  |  | 321 |      * @param progress_trace $trace
 | 
        
           |  |  | 322 |      * @param null|int $onecourse limit sync to one course only (used primarily in restore)
 | 
        
           |  |  | 323 |      * @return int 0 means success, 1 db connect failure, 2 db read failure
 | 
        
           |  |  | 324 |      */
 | 
        
           |  |  | 325 |     public function sync_enrolments(progress_trace $trace, $onecourse = null) {
 | 
        
           |  |  | 326 |         global $CFG, $DB;
 | 
        
           |  |  | 327 |   | 
        
           |  |  | 328 |         // We do not create courses here intentionally because it requires full sync and is slow.
 | 
        
           |  |  | 329 |         if (!$this->get_config('dbtype') or !$this->get_config('remoteenroltable') or !$this->get_config('remotecoursefield') or !$this->get_config('remoteuserfield')) {
 | 
        
           |  |  | 330 |             $trace->output('User enrolment synchronisation skipped.');
 | 
        
           |  |  | 331 |             $trace->finished();
 | 
        
           |  |  | 332 |             return 0;
 | 
        
           |  |  | 333 |         }
 | 
        
           |  |  | 334 |   | 
        
           |  |  | 335 |         $trace->output('Starting user enrolment synchronisation...');
 | 
        
           |  |  | 336 |   | 
        
           |  |  | 337 |         if (!$extdb = $this->db_init()) {
 | 
        
           |  |  | 338 |             $trace->output('Error while communicating with external enrolment database');
 | 
        
           |  |  | 339 |             $trace->finished();
 | 
        
           |  |  | 340 |             return 1;
 | 
        
           |  |  | 341 |         }
 | 
        
           |  |  | 342 |   | 
        
           |  |  | 343 |         // We may need a lot of memory here.
 | 
        
           |  |  | 344 |         core_php_time_limit::raise();
 | 
        
           |  |  | 345 |         raise_memory_limit(MEMORY_HUGE);
 | 
        
           |  |  | 346 |   | 
        
           |  |  | 347 |         $table            = $this->get_config('remoteenroltable');
 | 
        
           |  |  | 348 |         $coursefield      = trim($this->get_config('remotecoursefield'));
 | 
        
           |  |  | 349 |         $userfield        = trim($this->get_config('remoteuserfield'));
 | 
        
           |  |  | 350 |         $rolefield        = trim($this->get_config('remoterolefield'));
 | 
        
           |  |  | 351 |         $otheruserfield   = trim($this->get_config('remoteotheruserfield'));
 | 
        
           |  |  | 352 |   | 
        
           |  |  | 353 |         // Lowercased versions - necessary because we normalise the resultset with array_change_key_case().
 | 
        
           |  |  | 354 |         $coursefield_l    = strtolower($coursefield);
 | 
        
           |  |  | 355 |         $userfield_l      = strtolower($userfield);
 | 
        
           |  |  | 356 |         $rolefield_l      = strtolower($rolefield);
 | 
        
           |  |  | 357 |         $otheruserfieldlower = strtolower($otheruserfield);
 | 
        
           |  |  | 358 |   | 
        
           |  |  | 359 |         $localrolefield   = $this->get_config('localrolefield');
 | 
        
           |  |  | 360 |         $localuserfield   = $this->get_config('localuserfield');
 | 
        
           |  |  | 361 |         $localcoursefield = $this->get_config('localcoursefield');
 | 
        
           |  |  | 362 |   | 
        
           |  |  | 363 |         $unenrolaction    = $this->get_config('unenrolaction');
 | 
        
           |  |  | 364 |         $defaultrole      = $this->get_config('defaultrole');
 | 
        
           |  |  | 365 |   | 
        
           |  |  | 366 |         // Create roles mapping.
 | 
        
           |  |  | 367 |         $allroles = get_all_roles();
 | 
        
           |  |  | 368 |         if (!isset($allroles[$defaultrole])) {
 | 
        
           |  |  | 369 |             $defaultrole = 0;
 | 
        
           |  |  | 370 |         }
 | 
        
           |  |  | 371 |         $roles = array();
 | 
        
           |  |  | 372 |         foreach ($allroles as $role) {
 | 
        
           |  |  | 373 |             $roles[$role->$localrolefield] = $role->id;
 | 
        
           |  |  | 374 |         }
 | 
        
           |  |  | 375 |   | 
        
           |  |  | 376 |         if ($onecourse) {
 | 
        
           |  |  | 377 |             $sql = "SELECT c.id, c.visible, c.$localcoursefield AS mapping, c.shortname, e.id AS enrolid
 | 
        
           |  |  | 378 |                       FROM {course} c
 | 
        
           |  |  | 379 |                  LEFT JOIN {enrol} e ON (e.courseid = c.id AND e.enrol = 'database')
 | 
        
           |  |  | 380 |                      WHERE c.id = :id";
 | 
        
           |  |  | 381 |             if (!$course = $DB->get_record_sql($sql, array('id'=>$onecourse))) {
 | 
        
           |  |  | 382 |                 // Course does not exist, nothing to sync.
 | 
        
           |  |  | 383 |                 return 0;
 | 
        
           |  |  | 384 |             }
 | 
        
           |  |  | 385 |             if (empty($course->mapping)) {
 | 
        
           |  |  | 386 |                 // We can not map to this course, sorry.
 | 
        
           |  |  | 387 |                 return 0;
 | 
        
           |  |  | 388 |             }
 | 
        
           |  |  | 389 |             if (empty($course->enrolid)) {
 | 
        
           |  |  | 390 |                 $course->enrolid = $this->add_instance($course);
 | 
        
           |  |  | 391 |             }
 | 
        
           |  |  | 392 |             $existing = array($course->mapping=>$course);
 | 
        
           |  |  | 393 |   | 
        
           |  |  | 394 |             // Feel free to unenrol everybody, no safety tricks here.
 | 
        
           |  |  | 395 |             $preventfullunenrol = false;
 | 
        
           |  |  | 396 |             // Course being restored are always hidden, we have to ignore the setting here.
 | 
        
           |  |  | 397 |             $ignorehidden = false;
 | 
        
           |  |  | 398 |   | 
        
           |  |  | 399 |         } else {
 | 
        
           |  |  | 400 |             // Get a list of courses to be synced that are in external table.
 | 
        
           |  |  | 401 |             $externalcourses = array();
 | 
        
           |  |  | 402 |             $sql = $this->db_get_sql($table, array(), array($coursefield), true);
 | 
        
           |  |  | 403 |             if ($rs = $extdb->Execute($sql)) {
 | 
        
           |  |  | 404 |                 if (!$rs->EOF) {
 | 
        
           |  |  | 405 |                     while ($mapping = $rs->FetchRow()) {
 | 
        
           |  |  | 406 |                         $mapping = reset($mapping);
 | 
        
           |  |  | 407 |                         $mapping = $this->db_decode($mapping);
 | 
        
           |  |  | 408 |                         if (empty($mapping)) {
 | 
        
           |  |  | 409 |                             // invalid mapping
 | 
        
           |  |  | 410 |                             continue;
 | 
        
           |  |  | 411 |                         }
 | 
        
           |  |  | 412 |                         $externalcourses[$mapping] = true;
 | 
        
           |  |  | 413 |                     }
 | 
        
           |  |  | 414 |                 }
 | 
        
           |  |  | 415 |                 $rs->Close();
 | 
        
           |  |  | 416 |             } else {
 | 
        
           |  |  | 417 |                 $trace->output('Error reading data from the external enrolment table');
 | 
        
           |  |  | 418 |                 $extdb->Close();
 | 
        
           |  |  | 419 |                 return 2;
 | 
        
           |  |  | 420 |             }
 | 
        
           |  |  | 421 |             $preventfullunenrol = empty($externalcourses);
 | 
        
           |  |  | 422 |             if ($preventfullunenrol and $unenrolaction == ENROL_EXT_REMOVED_UNENROL) {
 | 
        
           |  |  | 423 |                 $trace->output('Preventing unenrolment of all current users, because it might result in major data loss, there has to be at least one record in external enrol table, sorry.', 1);
 | 
        
           |  |  | 424 |             }
 | 
        
           |  |  | 425 |   | 
        
           |  |  | 426 |             // First find all existing courses with enrol instance.
 | 
        
           |  |  | 427 |             $existing = array();
 | 
        
           |  |  | 428 |             $sql = "SELECT c.id, c.visible, c.$localcoursefield AS mapping, e.id AS enrolid, c.shortname
 | 
        
           |  |  | 429 |                       FROM {course} c
 | 
        
           |  |  | 430 |                       JOIN {enrol} e ON (e.courseid = c.id AND e.enrol = 'database')";
 | 
        
           |  |  | 431 |             $rs = $DB->get_recordset_sql($sql); // Watch out for idnumber duplicates.
 | 
        
           |  |  | 432 |             foreach ($rs as $course) {
 | 
        
           |  |  | 433 |                 if (empty($course->mapping)) {
 | 
        
           |  |  | 434 |                     continue;
 | 
        
           |  |  | 435 |                 }
 | 
        
           |  |  | 436 |                 $existing[$course->mapping] = $course;
 | 
        
           |  |  | 437 |                 unset($externalcourses[$course->mapping]);
 | 
        
           |  |  | 438 |             }
 | 
        
           |  |  | 439 |             $rs->close();
 | 
        
           |  |  | 440 |   | 
        
           |  |  | 441 |             // Add necessary enrol instances that are not present yet.
 | 
        
           |  |  | 442 |             $params = array();
 | 
        
           |  |  | 443 |             $localnotempty = "";
 | 
        
           |  |  | 444 |             if ($localcoursefield !== 'id') {
 | 
        
           |  |  | 445 |                 $localnotempty =  "AND c.$localcoursefield <> :lcfe";
 | 
        
           |  |  | 446 |                 $params['lcfe'] = '';
 | 
        
           |  |  | 447 |             }
 | 
        
           |  |  | 448 |             $sql = "SELECT c.id, c.visible, c.$localcoursefield AS mapping, c.shortname
 | 
        
           |  |  | 449 |                       FROM {course} c
 | 
        
           |  |  | 450 |                  LEFT JOIN {enrol} e ON (e.courseid = c.id AND e.enrol = 'database')
 | 
        
           |  |  | 451 |                      WHERE e.id IS NULL $localnotempty";
 | 
        
           |  |  | 452 |             $rs = $DB->get_recordset_sql($sql, $params);
 | 
        
           |  |  | 453 |             foreach ($rs as $course) {
 | 
        
           |  |  | 454 |                 if (empty($course->mapping)) {
 | 
        
           |  |  | 455 |                     continue;
 | 
        
           |  |  | 456 |                 }
 | 
        
           |  |  | 457 |                 if (!isset($externalcourses[$course->mapping])) {
 | 
        
           |  |  | 458 |                     // Course not synced or duplicate.
 | 
        
           |  |  | 459 |                     continue;
 | 
        
           |  |  | 460 |                 }
 | 
        
           |  |  | 461 |                 $course->enrolid = $this->add_instance($course);
 | 
        
           |  |  | 462 |                 $existing[$course->mapping] = $course;
 | 
        
           |  |  | 463 |                 unset($externalcourses[$course->mapping]);
 | 
        
           |  |  | 464 |             }
 | 
        
           |  |  | 465 |             $rs->close();
 | 
        
           |  |  | 466 |   | 
        
           |  |  | 467 |             // Print list of missing courses.
 | 
        
           |  |  | 468 |             if ($externalcourses) {
 | 
        
           |  |  | 469 |                 $list = implode(', ', array_keys($externalcourses));
 | 
        
           |  |  | 470 |                 $trace->output("error: following courses do not exist - $list", 1);
 | 
        
           |  |  | 471 |                 unset($list);
 | 
        
           |  |  | 472 |             }
 | 
        
           |  |  | 473 |   | 
        
           |  |  | 474 |             // Free memory.
 | 
        
           |  |  | 475 |             unset($externalcourses);
 | 
        
           |  |  | 476 |   | 
        
           |  |  | 477 |             $ignorehidden = $this->get_config('ignorehiddencourses');
 | 
        
           |  |  | 478 |         }
 | 
        
           |  |  | 479 |   | 
        
           |  |  | 480 |         // Sync user enrolments.
 | 
        
           |  |  | 481 |         $sqlfields = array($userfield);
 | 
        
           |  |  | 482 |         if ($rolefield) {
 | 
        
           |  |  | 483 |             $sqlfields[] = $rolefield;
 | 
        
           |  |  | 484 |         }
 | 
        
           |  |  | 485 |         if ($otheruserfield) {
 | 
        
           |  |  | 486 |             $sqlfields[] = $otheruserfield;
 | 
        
           |  |  | 487 |         }
 | 
        
           |  |  | 488 |         foreach ($existing as $course) {
 | 
        
           |  |  | 489 |             if ($ignorehidden and !$course->visible) {
 | 
        
           |  |  | 490 |                 continue;
 | 
        
           |  |  | 491 |             }
 | 
        
           |  |  | 492 |             if (!$instance = $DB->get_record('enrol', array('id'=>$course->enrolid))) {
 | 
        
           |  |  | 493 |                 continue; // Weird!
 | 
        
           |  |  | 494 |             }
 | 
        
           |  |  | 495 |             $context = context_course::instance($course->id);
 | 
        
           |  |  | 496 |   | 
        
           |  |  | 497 |             // Get current list of enrolled users with their roles.
 | 
        
           |  |  | 498 |             $currentroles  = array();
 | 
        
           |  |  | 499 |             $currentenrols = array();
 | 
        
           |  |  | 500 |             $currentstatus = array();
 | 
        
           |  |  | 501 |             $usermapping   = array();
 | 
        
           |  |  | 502 |             $sql = "SELECT u.$localuserfield AS mapping, u.id AS userid, ue.status, ra.roleid
 | 
        
           |  |  | 503 |                       FROM {user} u
 | 
        
           |  |  | 504 |                       JOIN {role_assignments} ra ON (ra.userid = u.id AND ra.component = 'enrol_database' AND ra.itemid = :enrolid)
 | 
        
           |  |  | 505 |                  LEFT JOIN {user_enrolments} ue ON (ue.userid = u.id AND ue.enrolid = ra.itemid)
 | 
        
           |  |  | 506 |                      WHERE u.deleted = 0";
 | 
        
           |  |  | 507 |             $params = array('enrolid'=>$instance->id);
 | 
        
           |  |  | 508 |             if ($localuserfield === 'username') {
 | 
        
           |  |  | 509 |                 $sql .= " AND u.mnethostid = :mnethostid";
 | 
        
           |  |  | 510 |                 $params['mnethostid'] = $CFG->mnet_localhost_id;
 | 
        
           |  |  | 511 |             }
 | 
        
           |  |  | 512 |             $rs = $DB->get_recordset_sql($sql, $params);
 | 
        
           |  |  | 513 |             foreach ($rs as $ue) {
 | 
        
           |  |  | 514 |                 $currentroles[$ue->userid][$ue->roleid] = $ue->roleid;
 | 
        
           |  |  | 515 |                 $usermapping[$ue->mapping] = $ue->userid;
 | 
        
           |  |  | 516 |   | 
        
           |  |  | 517 |                 if (isset($ue->status)) {
 | 
        
           |  |  | 518 |                     $currentenrols[$ue->userid][$ue->roleid] = $ue->roleid;
 | 
        
           |  |  | 519 |                     $currentstatus[$ue->userid] = $ue->status;
 | 
        
           |  |  | 520 |                 }
 | 
        
           |  |  | 521 |             }
 | 
        
           |  |  | 522 |             $rs->close();
 | 
        
           |  |  | 523 |   | 
        
           |  |  | 524 |             // Get list of users that need to be enrolled and their roles.
 | 
        
           |  |  | 525 |             $requestedroles  = array();
 | 
        
           |  |  | 526 |             $requestedenrols = array();
 | 
        
           |  |  | 527 |             $sql = $this->db_get_sql($table, array($coursefield=>$course->mapping), $sqlfields);
 | 
        
           |  |  | 528 |             if ($rs = $extdb->Execute($sql)) {
 | 
        
           |  |  | 529 |                 if (!$rs->EOF) {
 | 
        
           |  |  | 530 |                     $usersearch = array('deleted' => 0);
 | 
        
           |  |  | 531 |                     if ($localuserfield === 'username') {
 | 
        
           |  |  | 532 |                         $usersearch['mnethostid'] = $CFG->mnet_localhost_id;
 | 
        
           |  |  | 533 |                     }
 | 
        
           |  |  | 534 |                     while ($fields = $rs->FetchRow()) {
 | 
        
           |  |  | 535 |                         $fields = array_change_key_case($fields, CASE_LOWER);
 | 
        
           |  |  | 536 |                         if (empty($fields[$userfield_l])) {
 | 
        
           |  |  | 537 |                             $trace->output("error: skipping user without mandatory $localuserfield in course '$course->mapping'", 1);
 | 
        
           |  |  | 538 |                             continue;
 | 
        
           |  |  | 539 |                         }
 | 
        
           |  |  | 540 |                         $mapping = $fields[$userfield_l];
 | 
        
           |  |  | 541 |                         if (!isset($usermapping[$mapping])) {
 | 
        
           |  |  | 542 |                             $usersearch[$localuserfield] = $mapping;
 | 
        
           |  |  | 543 |                             if (!$user = $DB->get_record('user', $usersearch, 'id', IGNORE_MULTIPLE)) {
 | 
        
           |  |  | 544 |                                 $trace->output("error: skipping unknown user $localuserfield '$mapping' in course '$course->mapping'", 1);
 | 
        
           |  |  | 545 |                                 continue;
 | 
        
           |  |  | 546 |                             }
 | 
        
           |  |  | 547 |                             $usermapping[$mapping] = $user->id;
 | 
        
           |  |  | 548 |                             $userid = $user->id;
 | 
        
           |  |  | 549 |                         } else {
 | 
        
           |  |  | 550 |                             $userid = $usermapping[$mapping];
 | 
        
           |  |  | 551 |                         }
 | 
        
           |  |  | 552 |                         if (empty($fields[$rolefield_l]) or !isset($roles[$fields[$rolefield_l]])) {
 | 
        
           |  |  | 553 |                             if (!$defaultrole) {
 | 
        
           |  |  | 554 |                                 $trace->output("error: skipping user '$userid' in course '$course->mapping' - missing course and default role", 1);
 | 
        
           |  |  | 555 |                                 continue;
 | 
        
           |  |  | 556 |                             }
 | 
        
           |  |  | 557 |                             $roleid = $defaultrole;
 | 
        
           |  |  | 558 |                         } else {
 | 
        
           |  |  | 559 |                             $roleid = $roles[$fields[$rolefield_l]];
 | 
        
           |  |  | 560 |                         }
 | 
        
           |  |  | 561 |   | 
        
           |  |  | 562 |                         $requestedroles[$userid][$roleid] = $roleid;
 | 
        
           |  |  | 563 |                         if (empty($fields[$otheruserfieldlower])) {
 | 
        
           |  |  | 564 |                             $requestedenrols[$userid][$roleid] = $roleid;
 | 
        
           |  |  | 565 |                         }
 | 
        
           |  |  | 566 |                     }
 | 
        
           |  |  | 567 |                 }
 | 
        
           |  |  | 568 |                 $rs->Close();
 | 
        
           |  |  | 569 |             } else {
 | 
        
           |  |  | 570 |                 $trace->output("error: skipping course '$course->mapping' - could not match with external database", 1);
 | 
        
           |  |  | 571 |                 continue;
 | 
        
           |  |  | 572 |             }
 | 
        
           |  |  | 573 |             unset($usermapping);
 | 
        
           |  |  | 574 |   | 
        
           |  |  | 575 |             // Enrol all users and sync roles.
 | 
        
           |  |  | 576 |             foreach ($requestedenrols as $userid => $userroles) {
 | 
        
           |  |  | 577 |                 foreach ($userroles as $roleid) {
 | 
        
           |  |  | 578 |                     if (empty($currentenrols[$userid])) {
 | 
        
           |  |  | 579 |                         $this->enrol_user($instance, $userid, $roleid, 0, 0, ENROL_USER_ACTIVE);
 | 
        
           |  |  | 580 |                         $currentroles[$userid][$roleid] = $roleid;
 | 
        
           |  |  | 581 |                         $currentenrols[$userid][$roleid] = $roleid;
 | 
        
           |  |  | 582 |                         $currentstatus[$userid] = ENROL_USER_ACTIVE;
 | 
        
           |  |  | 583 |                         $trace->output("enrolling: $userid ==> $course->shortname as ".$allroles[$roleid]->shortname, 1);
 | 
        
           |  |  | 584 |                     }
 | 
        
           |  |  | 585 |                 }
 | 
        
           |  |  | 586 |   | 
        
           |  |  | 587 |                 // Reenable enrolment when previously disable enrolment refreshed.
 | 
        
           |  |  | 588 |                 if ($currentstatus[$userid] == ENROL_USER_SUSPENDED) {
 | 
        
           |  |  | 589 |                     $this->update_user_enrol($instance, $userid, ENROL_USER_ACTIVE);
 | 
        
           |  |  | 590 |                     $trace->output("unsuspending: $userid ==> $course->shortname", 1);
 | 
        
           |  |  | 591 |                 }
 | 
        
           |  |  | 592 |             }
 | 
        
           |  |  | 593 |   | 
        
           |  |  | 594 |             foreach ($requestedroles as $userid => $userroles) {
 | 
        
           |  |  | 595 |                 // Assign extra roles.
 | 
        
           |  |  | 596 |                 foreach ($userroles as $roleid) {
 | 
        
           |  |  | 597 |                     if (empty($currentroles[$userid][$roleid])) {
 | 
        
           |  |  | 598 |                         role_assign($roleid, $userid, $context->id, 'enrol_database', $instance->id);
 | 
        
           |  |  | 599 |                         $currentroles[$userid][$roleid] = $roleid;
 | 
        
           |  |  | 600 |                         $trace->output("assigning roles: $userid ==> $course->shortname as ".$allroles[$roleid]->shortname, 1);
 | 
        
           |  |  | 601 |                     }
 | 
        
           |  |  | 602 |                 }
 | 
        
           |  |  | 603 |   | 
        
           |  |  | 604 |                 // Unassign removed roles.
 | 
        
           |  |  | 605 |                 foreach ($currentroles[$userid] as $cr) {
 | 
        
           |  |  | 606 |                     if (empty($userroles[$cr])) {
 | 
        
           |  |  | 607 |                         role_unassign($cr, $userid, $context->id, 'enrol_database', $instance->id);
 | 
        
           |  |  | 608 |                         unset($currentroles[$userid][$cr]);
 | 
        
           |  |  | 609 |                         $trace->output("unsassigning roles: $userid ==> $course->shortname", 1);
 | 
        
           |  |  | 610 |                     }
 | 
        
           |  |  | 611 |                 }
 | 
        
           |  |  | 612 |   | 
        
           |  |  | 613 |                 unset($currentroles[$userid]);
 | 
        
           |  |  | 614 |             }
 | 
        
           |  |  | 615 |   | 
        
           |  |  | 616 |             foreach ($currentroles as $userid => $userroles) {
 | 
        
           |  |  | 617 |                 // These are roles that exist only in Moodle, not the external database
 | 
        
           |  |  | 618 |                 // so make sure the unenrol actions will handle them by setting status.
 | 
        
           |  |  | 619 |                 $currentstatus += array($userid => ENROL_USER_ACTIVE);
 | 
        
           |  |  | 620 |             }
 | 
        
           |  |  | 621 |   | 
        
           |  |  | 622 |             // Deal with enrolments removed from external table.
 | 
        
           |  |  | 623 |             if ($unenrolaction == ENROL_EXT_REMOVED_UNENROL) {
 | 
        
           |  |  | 624 |                 if (!$preventfullunenrol) {
 | 
        
           |  |  | 625 |                     // Unenrol.
 | 
        
           |  |  | 626 |                     foreach ($currentstatus as $userid => $status) {
 | 
        
           |  |  | 627 |                         if (isset($requestedenrols[$userid])) {
 | 
        
           |  |  | 628 |                             continue;
 | 
        
           |  |  | 629 |                         }
 | 
        
           |  |  | 630 |                         $this->unenrol_user($instance, $userid);
 | 
        
           |  |  | 631 |                         $trace->output("unenrolling: $userid ==> $course->shortname", 1);
 | 
        
           |  |  | 632 |                     }
 | 
        
           |  |  | 633 |                 }
 | 
        
           |  |  | 634 |   | 
        
           |  |  | 635 |             } else if ($unenrolaction == ENROL_EXT_REMOVED_KEEP) {
 | 
        
           |  |  | 636 |                 // Keep - only adding enrolments.
 | 
        
           |  |  | 637 |   | 
        
           |  |  | 638 |             } else if ($unenrolaction == ENROL_EXT_REMOVED_SUSPEND or $unenrolaction == ENROL_EXT_REMOVED_SUSPENDNOROLES) {
 | 
        
           |  |  | 639 |                 // Suspend enrolments.
 | 
        
           |  |  | 640 |                 foreach ($currentstatus as $userid => $status) {
 | 
        
           |  |  | 641 |                     if (isset($requestedenrols[$userid])) {
 | 
        
           |  |  | 642 |                         continue;
 | 
        
           |  |  | 643 |                     }
 | 
        
           |  |  | 644 |                     if ($status != ENROL_USER_SUSPENDED) {
 | 
        
           |  |  | 645 |                         $this->update_user_enrol($instance, $userid, ENROL_USER_SUSPENDED);
 | 
        
           |  |  | 646 |                         $trace->output("suspending: $userid ==> $course->shortname", 1);
 | 
        
           |  |  | 647 |                     }
 | 
        
           |  |  | 648 |                     if ($unenrolaction == ENROL_EXT_REMOVED_SUSPENDNOROLES) {
 | 
        
           |  |  | 649 |                         if (isset($requestedroles[$userid])) {
 | 
        
           |  |  | 650 |                             // We want this "other user" to keep their roles.
 | 
        
           |  |  | 651 |                             continue;
 | 
        
           |  |  | 652 |                         }
 | 
        
           |  |  | 653 |                         role_unassign_all(array('contextid'=>$context->id, 'userid'=>$userid, 'component'=>'enrol_database', 'itemid'=>$instance->id));
 | 
        
           |  |  | 654 |   | 
        
           |  |  | 655 |                         $trace->output("unsassigning all roles: $userid ==> $course->shortname", 1);
 | 
        
           |  |  | 656 |                     }
 | 
        
           |  |  | 657 |                 }
 | 
        
           |  |  | 658 |             }
 | 
        
           |  |  | 659 |         }
 | 
        
           |  |  | 660 |   | 
        
           |  |  | 661 |         // Close db connection.
 | 
        
           |  |  | 662 |         $extdb->Close();
 | 
        
           |  |  | 663 |   | 
        
           |  |  | 664 |         $trace->output('...user enrolment synchronisation finished.');
 | 
        
           |  |  | 665 |         $trace->finished();
 | 
        
           |  |  | 666 |   | 
        
           |  |  | 667 |         return 0;
 | 
        
           |  |  | 668 |     }
 | 
        
           |  |  | 669 |   | 
        
           |  |  | 670 |     /**
 | 
        
           |  |  | 671 |      * Performs a full sync with external database.
 | 
        
           |  |  | 672 |      *
 | 
        
           |  |  | 673 |      * First it creates new courses if necessary, then
 | 
        
           |  |  | 674 |      * enrols and unenrols users.
 | 
        
           |  |  | 675 |      *
 | 
        
           |  |  | 676 |      * @param progress_trace $trace
 | 
        
           |  |  | 677 |      * @return int 0 means success, 1 db connect failure, 4 db read failure
 | 
        
           |  |  | 678 |      */
 | 
        
           |  |  | 679 |     public function sync_courses(progress_trace $trace) {
 | 
        
           |  |  | 680 |         global $CFG, $DB;
 | 
        
           |  |  | 681 |   | 
        
           |  |  | 682 |         // Make sure we sync either enrolments or courses.
 | 
        
           |  |  | 683 |         if (!$this->get_config('dbtype') or !$this->get_config('newcoursetable') or !$this->get_config('newcoursefullname') or !$this->get_config('newcourseshortname')) {
 | 
        
           |  |  | 684 |             $trace->output('Course synchronisation skipped.');
 | 
        
           |  |  | 685 |             $trace->finished();
 | 
        
           |  |  | 686 |             return 0;
 | 
        
           |  |  | 687 |         }
 | 
        
           |  |  | 688 |   | 
        
           |  |  | 689 |         $trace->output('Starting course synchronisation...');
 | 
        
           |  |  | 690 |   | 
        
           |  |  | 691 |         // We may need a lot of memory here.
 | 
        
           |  |  | 692 |         core_php_time_limit::raise();
 | 
        
           |  |  | 693 |         raise_memory_limit(MEMORY_HUGE);
 | 
        
           |  |  | 694 |   | 
        
           |  |  | 695 |         if (!$extdb = $this->db_init()) {
 | 
        
           |  |  | 696 |             $trace->output('Error while communicating with external enrolment database');
 | 
        
           |  |  | 697 |             $trace->finished();
 | 
        
           |  |  | 698 |             return 1;
 | 
        
           |  |  | 699 |         }
 | 
        
           |  |  | 700 |   | 
        
           | 1441 | ariadna | 701 |         $courseconfig = get_config('moodlecourse');
 | 
        
           |  |  | 702 |   | 
        
           | 1 | efrain | 703 |         $table     = $this->get_config('newcoursetable');
 | 
        
           |  |  | 704 |         $fullname  = trim($this->get_config('newcoursefullname'));
 | 
        
           |  |  | 705 |         $shortname = trim($this->get_config('newcourseshortname'));
 | 
        
           |  |  | 706 |         $idnumber  = trim($this->get_config('newcourseidnumber'));
 | 
        
           |  |  | 707 |         $category  = trim($this->get_config('newcoursecategory'));
 | 
        
           |  |  | 708 |   | 
        
           | 1441 | ariadna | 709 |         $startdate = trim($this->get_config('newcoursestartdate'));
 | 
        
           |  |  | 710 |         $enddate   = trim($this->get_config('newcourseenddate'));
 | 
        
           |  |  | 711 |   | 
        
           | 1 | efrain | 712 |         // Lowercased versions - necessary because we normalise the resultset with array_change_key_case().
 | 
        
           |  |  | 713 |         $fullname_l  = strtolower($fullname);
 | 
        
           |  |  | 714 |         $shortname_l = strtolower($shortname);
 | 
        
           |  |  | 715 |         $idnumber_l  = strtolower($idnumber);
 | 
        
           |  |  | 716 |         $category_l  = strtolower($category);
 | 
        
           | 1441 | ariadna | 717 |         $startdatelowercased = strtolower($startdate);
 | 
        
           |  |  | 718 |         $enddatelowercased   = strtolower($enddate);
 | 
        
           | 1 | efrain | 719 |   | 
        
           |  |  | 720 |         $localcategoryfield = $this->get_config('localcategoryfield', 'id');
 | 
        
           |  |  | 721 |         $defaultcategory    = $this->get_config('defaultcategory');
 | 
        
           |  |  | 722 |   | 
        
           |  |  | 723 |         if (!$DB->record_exists('course_categories', array('id'=>$defaultcategory))) {
 | 
        
           |  |  | 724 |             $trace->output("default course category does not exist!", 1);
 | 
        
           |  |  | 725 |             $categories = $DB->get_records('course_categories', array(), 'sortorder', 'id', 0, 1);
 | 
        
           |  |  | 726 |             $first = reset($categories);
 | 
        
           |  |  | 727 |             $defaultcategory = $first->id;
 | 
        
           |  |  | 728 |         }
 | 
        
           |  |  | 729 |   | 
        
           |  |  | 730 |         $sqlfields = array($fullname, $shortname);
 | 
        
           |  |  | 731 |         if ($category) {
 | 
        
           |  |  | 732 |             $sqlfields[] = $category;
 | 
        
           |  |  | 733 |         }
 | 
        
           |  |  | 734 |         if ($idnumber) {
 | 
        
           |  |  | 735 |             $sqlfields[] = $idnumber;
 | 
        
           |  |  | 736 |         }
 | 
        
           | 1441 | ariadna | 737 |         if ($startdate) {
 | 
        
           |  |  | 738 |             $sqlfields[] = $startdate;
 | 
        
           |  |  | 739 |         }
 | 
        
           |  |  | 740 |         if ($enddate) {
 | 
        
           |  |  | 741 |             $sqlfields[] = $enddate;
 | 
        
           |  |  | 742 |         }
 | 
        
           |  |  | 743 |   | 
        
           | 1 | efrain | 744 |         $sql = $this->db_get_sql($table, array(), $sqlfields, true);
 | 
        
           |  |  | 745 |         $createcourses = array();
 | 
        
           |  |  | 746 |         if ($rs = $extdb->Execute($sql)) {
 | 
        
           |  |  | 747 |             if (!$rs->EOF) {
 | 
        
           |  |  | 748 |                 while ($fields = $rs->FetchRow()) {
 | 
        
           |  |  | 749 |                     $fields = array_change_key_case($fields, CASE_LOWER);
 | 
        
           |  |  | 750 |                     $fields = $this->db_decode($fields);
 | 
        
           |  |  | 751 |                     if (empty($fields[$shortname_l]) or empty($fields[$fullname_l])) {
 | 
        
           |  |  | 752 |                         $trace->output('error: invalid external course record, shortname and fullname are mandatory: ' . json_encode($fields), 1); // Hopefully every geek can read JS, right?
 | 
        
           |  |  | 753 |                         continue;
 | 
        
           |  |  | 754 |                     }
 | 
        
           |  |  | 755 |                     if ($DB->record_exists('course', array('shortname'=>$fields[$shortname_l]))) {
 | 
        
           |  |  | 756 |                         // Already exists, skip.
 | 
        
           |  |  | 757 |                         continue;
 | 
        
           |  |  | 758 |                     }
 | 
        
           |  |  | 759 |                     // Allow empty idnumber but not duplicates.
 | 
        
           |  |  | 760 |                     if ($idnumber and $fields[$idnumber_l] !== '' and $fields[$idnumber_l] !== null and $DB->record_exists('course', array('idnumber'=>$fields[$idnumber_l]))) {
 | 
        
           |  |  | 761 |                         $trace->output('error: duplicate idnumber, can not create course: '.$fields[$shortname_l].' ['.$fields[$idnumber_l].']', 1);
 | 
        
           |  |  | 762 |                         continue;
 | 
        
           |  |  | 763 |                     }
 | 
        
           |  |  | 764 |                     $course = new stdClass();
 | 
        
           |  |  | 765 |                     $course->fullname  = $fields[$fullname_l];
 | 
        
           |  |  | 766 |                     $course->shortname = $fields[$shortname_l];
 | 
        
           |  |  | 767 |                     $course->idnumber  = $idnumber ? $fields[$idnumber_l] : '';
 | 
        
           | 1441 | ariadna | 768 |   | 
        
           | 1 | efrain | 769 |                     if ($category) {
 | 
        
           |  |  | 770 |                         if (empty($fields[$category_l])) {
 | 
        
           |  |  | 771 |                             // Empty category means use default.
 | 
        
           |  |  | 772 |                             $course->category = $defaultcategory;
 | 
        
           |  |  | 773 |                         } else if ($coursecategory = $DB->get_record('course_categories', array($localcategoryfield=>$fields[$category_l]), 'id')) {
 | 
        
           |  |  | 774 |                             // Yay, correctly specified category!
 | 
        
           |  |  | 775 |                             $course->category = $coursecategory->id;
 | 
        
           |  |  | 776 |                             unset($coursecategory);
 | 
        
           |  |  | 777 |                         } else {
 | 
        
           |  |  | 778 |                             // Bad luck, better not continue because unwanted ppl might get access to course in different category.
 | 
        
           |  |  | 779 |                             $trace->output('error: invalid category '.$localcategoryfield.', can not create course: '.$fields[$shortname_l], 1);
 | 
        
           |  |  | 780 |                             continue;
 | 
        
           |  |  | 781 |                         }
 | 
        
           |  |  | 782 |                     } else {
 | 
        
           |  |  | 783 |                         $course->category = $defaultcategory;
 | 
        
           |  |  | 784 |                     }
 | 
        
           | 1441 | ariadna | 785 |   | 
        
           |  |  | 786 |                     if ($startdate) {
 | 
        
           |  |  | 787 |                         if (!empty($fields[$startdatelowercased])) {
 | 
        
           |  |  | 788 |                             $course->startdate = is_number($fields[$startdatelowercased])
 | 
        
           |  |  | 789 |                                 ? $fields[$startdatelowercased]
 | 
        
           |  |  | 790 |                                 : strtotime($fields[$startdatelowercased]);
 | 
        
           |  |  | 791 |   | 
        
           |  |  | 792 |                             // Broken start date. Stop syncing this course.
 | 
        
           |  |  | 793 |                             if ($course->startdate === false) {
 | 
        
           |  |  | 794 |                                 $trace->output('error: invalid external course start date value: ' . json_encode($fields), 1);
 | 
        
           |  |  | 795 |                                 continue;
 | 
        
           |  |  | 796 |                             }
 | 
        
           |  |  | 797 |                         }
 | 
        
           |  |  | 798 |                     }
 | 
        
           |  |  | 799 |   | 
        
           |  |  | 800 |                     if ($enddate) {
 | 
        
           |  |  | 801 |                         if (!empty($fields[$enddatelowercased])) {
 | 
        
           |  |  | 802 |                             $course->enddate = is_number($fields[$enddatelowercased])
 | 
        
           |  |  | 803 |                                 ? $fields[$enddatelowercased]
 | 
        
           |  |  | 804 |                                 : strtotime($fields[$enddatelowercased]);
 | 
        
           |  |  | 805 |   | 
        
           |  |  | 806 |                             // Broken end date. Stop syncing this course.
 | 
        
           |  |  | 807 |                             if ($course->enddate === false) {
 | 
        
           |  |  | 808 |                                 $trace->output('error: invalid external course end date value: ' . json_encode($fields), 1);
 | 
        
           |  |  | 809 |                                 continue;
 | 
        
           |  |  | 810 |                             }
 | 
        
           |  |  | 811 |                         }
 | 
        
           |  |  | 812 |                     }
 | 
        
           |  |  | 813 |   | 
        
           | 1 | efrain | 814 |                     $createcourses[] = $course;
 | 
        
           |  |  | 815 |                 }
 | 
        
           |  |  | 816 |             }
 | 
        
           |  |  | 817 |             $rs->Close();
 | 
        
           |  |  | 818 |         } else {
 | 
        
           |  |  | 819 |             $extdb->Close();
 | 
        
           |  |  | 820 |             $trace->output('Error reading data from the external course table');
 | 
        
           |  |  | 821 |             $trace->finished();
 | 
        
           |  |  | 822 |             return 4;
 | 
        
           |  |  | 823 |         }
 | 
        
           |  |  | 824 |         if ($createcourses) {
 | 
        
           |  |  | 825 |             require_once("$CFG->dirroot/course/lib.php");
 | 
        
           |  |  | 826 |   | 
        
           |  |  | 827 |             $templatecourse = $this->get_config('templatecourse');
 | 
        
           |  |  | 828 |   | 
        
           |  |  | 829 |             $template = false;
 | 
        
           |  |  | 830 |             if ($templatecourse) {
 | 
        
           |  |  | 831 |                 if ($template = $DB->get_record('course', array('shortname'=>$templatecourse))) {
 | 
        
           |  |  | 832 |                     $template = fullclone(course_get_format($template)->get_course());
 | 
        
           |  |  | 833 |                     if (!isset($template->numsections)) {
 | 
        
           |  |  | 834 |                         $template->numsections = course_get_format($template)->get_last_section_number();
 | 
        
           |  |  | 835 |                     }
 | 
        
           |  |  | 836 |                     unset($template->id);
 | 
        
           |  |  | 837 |                     unset($template->fullname);
 | 
        
           |  |  | 838 |                     unset($template->shortname);
 | 
        
           |  |  | 839 |                     unset($template->idnumber);
 | 
        
           |  |  | 840 |                 } else {
 | 
        
           |  |  | 841 |                     $trace->output("can not find template for new course!", 1);
 | 
        
           |  |  | 842 |                 }
 | 
        
           |  |  | 843 |             }
 | 
        
           |  |  | 844 |             if (!$template) {
 | 
        
           |  |  | 845 |                 $template = new stdClass();
 | 
        
           |  |  | 846 |                 $template->summary        = '';
 | 
        
           |  |  | 847 |                 $template->summaryformat  = FORMAT_HTML;
 | 
        
           |  |  | 848 |                 $template->format         = $courseconfig->format;
 | 
        
           |  |  | 849 |                 $template->numsections    = $courseconfig->numsections;
 | 
        
           |  |  | 850 |                 $template->newsitems      = $courseconfig->newsitems;
 | 
        
           |  |  | 851 |                 $template->showgrades     = $courseconfig->showgrades;
 | 
        
           |  |  | 852 |                 $template->showreports    = $courseconfig->showreports;
 | 
        
           |  |  | 853 |                 $template->maxbytes       = $courseconfig->maxbytes;
 | 
        
           |  |  | 854 |                 $template->groupmode      = $courseconfig->groupmode;
 | 
        
           |  |  | 855 |                 $template->groupmodeforce = $courseconfig->groupmodeforce;
 | 
        
           |  |  | 856 |                 $template->visible        = $courseconfig->visible;
 | 
        
           |  |  | 857 |                 $template->lang           = $courseconfig->lang;
 | 
        
           |  |  | 858 |                 $template->enablecompletion = $courseconfig->enablecompletion;
 | 
        
           |  |  | 859 |                 $template->groupmodeforce = $courseconfig->groupmodeforce;
 | 
        
           |  |  | 860 |                 $template->startdate      = usergetmidnight(time());
 | 
        
           |  |  | 861 |                 if ($courseconfig->courseenddateenabled) {
 | 
        
           |  |  | 862 |                     $template->enddate    = usergetmidnight(time()) + $courseconfig->courseduration;
 | 
        
           |  |  | 863 |                 }
 | 
        
           |  |  | 864 |             }
 | 
        
           |  |  | 865 |   | 
        
           |  |  | 866 |             foreach ($createcourses as $fields) {
 | 
        
           |  |  | 867 |                 $newcourse = clone($template);
 | 
        
           |  |  | 868 |                 $newcourse->fullname  = $fields->fullname;
 | 
        
           |  |  | 869 |                 $newcourse->shortname = $fields->shortname;
 | 
        
           |  |  | 870 |                 $newcourse->idnumber  = $fields->idnumber;
 | 
        
           |  |  | 871 |                 $newcourse->category  = $fields->category;
 | 
        
           |  |  | 872 |   | 
        
           | 1441 | ariadna | 873 |                 if (isset($fields->startdate)) {
 | 
        
           |  |  | 874 |                     $newcourse->startdate = $fields->startdate;
 | 
        
           |  |  | 875 |                 }
 | 
        
           |  |  | 876 |   | 
        
           |  |  | 877 |                 if (isset($fields->enddate)) {
 | 
        
           |  |  | 878 |                     // Validating end date.
 | 
        
           |  |  | 879 |                     if ($fields->enddate > 0 && $newcourse->startdate > $fields->enddate) {
 | 
        
           |  |  | 880 |                         $trace->output(
 | 
        
           |  |  | 881 |                             "can not insert new course, the end date must be after the start date: " . $newcourse->shortname, 1
 | 
        
           |  |  | 882 |                         );
 | 
        
           |  |  | 883 |                         continue;
 | 
        
           |  |  | 884 |                     }
 | 
        
           |  |  | 885 |                     $newcourse->enddate = $fields->enddate;
 | 
        
           |  |  | 886 |                 } else {
 | 
        
           |  |  | 887 |                     if ($courseconfig->courseenddateenabled) {
 | 
        
           |  |  | 888 |                         $newcourse->enddate = $newcourse->startdate + $courseconfig->courseduration;
 | 
        
           |  |  | 889 |                     }
 | 
        
           |  |  | 890 |                 }
 | 
        
           |  |  | 891 |   | 
        
           | 1 | efrain | 892 |                 // Detect duplicate data once again, above we can not find duplicates
 | 
        
           |  |  | 893 |                 // in external data using DB collation rules...
 | 
        
           |  |  | 894 |                 if ($DB->record_exists('course', array('shortname' => $newcourse->shortname))) {
 | 
        
           |  |  | 895 |                     $trace->output("can not insert new course, duplicate shortname detected: ".$newcourse->shortname, 1);
 | 
        
           |  |  | 896 |                     continue;
 | 
        
           |  |  | 897 |                 } else if (!empty($newcourse->idnumber) and $DB->record_exists('course', array('idnumber' => $newcourse->idnumber))) {
 | 
        
           |  |  | 898 |                     $trace->output("can not insert new course, duplicate idnumber detected: ".$newcourse->idnumber, 1);
 | 
        
           |  |  | 899 |                     continue;
 | 
        
           |  |  | 900 |                 }
 | 
        
           |  |  | 901 |                 $c = create_course($newcourse);
 | 
        
           |  |  | 902 |                 $trace->output("creating course: $c->id, $c->fullname, $c->shortname, $c->idnumber, $c->category", 1);
 | 
        
           |  |  | 903 |             }
 | 
        
           |  |  | 904 |   | 
        
           |  |  | 905 |             unset($createcourses);
 | 
        
           |  |  | 906 |             unset($template);
 | 
        
           |  |  | 907 |         }
 | 
        
           |  |  | 908 |   | 
        
           |  |  | 909 |         // Close db connection.
 | 
        
           |  |  | 910 |         $extdb->Close();
 | 
        
           |  |  | 911 |   | 
        
           |  |  | 912 |         $trace->output('...course synchronisation finished.');
 | 
        
           |  |  | 913 |         $trace->finished();
 | 
        
           |  |  | 914 |   | 
        
           |  |  | 915 |         return 0;
 | 
        
           |  |  | 916 |     }
 | 
        
           |  |  | 917 |   | 
        
           |  |  | 918 |     protected function db_get_sql($table, array $conditions, array $fields, $distinct = false, $sort = "") {
 | 
        
           |  |  | 919 |         $fields = $fields ? implode(',', $fields) : "*";
 | 
        
           |  |  | 920 |         $where = array();
 | 
        
           |  |  | 921 |         if ($conditions) {
 | 
        
           |  |  | 922 |             foreach ($conditions as $key=>$value) {
 | 
        
           |  |  | 923 |                 $value = $this->db_encode($this->db_addslashes($value));
 | 
        
           |  |  | 924 |   | 
        
           |  |  | 925 |                 $where[] = "$key = '$value'";
 | 
        
           |  |  | 926 |             }
 | 
        
           |  |  | 927 |         }
 | 
        
           |  |  | 928 |         $where = $where ? "WHERE ".implode(" AND ", $where) : "";
 | 
        
           |  |  | 929 |         $sort = $sort ? "ORDER BY $sort" : "";
 | 
        
           |  |  | 930 |         $distinct = $distinct ? "DISTINCT" : "";
 | 
        
           |  |  | 931 |         $sql = "SELECT $distinct $fields
 | 
        
           |  |  | 932 |                   FROM $table
 | 
        
           |  |  | 933 |                  $where
 | 
        
           |  |  | 934 |                   $sort";
 | 
        
           |  |  | 935 |   | 
        
           |  |  | 936 |         return $sql;
 | 
        
           |  |  | 937 |     }
 | 
        
           |  |  | 938 |   | 
        
           |  |  | 939 |     /**
 | 
        
           |  |  | 940 |      * Tries to make connection to the external database.
 | 
        
           |  |  | 941 |      *
 | 
        
           |  |  | 942 |      * @return null|ADONewConnection
 | 
        
           |  |  | 943 |      */
 | 
        
           |  |  | 944 |     protected function db_init() {
 | 
        
           |  |  | 945 |         global $CFG;
 | 
        
           |  |  | 946 |   | 
        
           |  |  | 947 |         require_once($CFG->libdir.'/adodb/adodb.inc.php');
 | 
        
           |  |  | 948 |   | 
        
           |  |  | 949 |         // Connect to the external database (forcing new connection).
 | 
        
           |  |  | 950 |         $extdb = ADONewConnection($this->get_config('dbtype'));
 | 
        
           |  |  | 951 |         if ($this->get_config('debugdb')) {
 | 
        
           |  |  | 952 |             $extdb->debug = true;
 | 
        
           |  |  | 953 |             ob_start(); // Start output buffer to allow later use of the page headers.
 | 
        
           |  |  | 954 |         }
 | 
        
           |  |  | 955 |   | 
        
           |  |  | 956 |         // The dbtype my contain the new connection URL, so make sure we are not connected yet.
 | 
        
           |  |  | 957 |         if (!$extdb->IsConnected()) {
 | 
        
           |  |  | 958 |             $result = $extdb->Connect($this->get_config('dbhost'), $this->get_config('dbuser'), $this->get_config('dbpass'), $this->get_config('dbname'), true);
 | 
        
           |  |  | 959 |             if (!$result) {
 | 
        
           |  |  | 960 |                 return null;
 | 
        
           |  |  | 961 |             }
 | 
        
           |  |  | 962 |         }
 | 
        
           |  |  | 963 |   | 
        
           |  |  | 964 |         $extdb->SetFetchMode(ADODB_FETCH_ASSOC);
 | 
        
           |  |  | 965 |         if ($this->get_config('dbsetupsql')) {
 | 
        
           |  |  | 966 |             $extdb->Execute($this->get_config('dbsetupsql'));
 | 
        
           |  |  | 967 |         }
 | 
        
           |  |  | 968 |         return $extdb;
 | 
        
           |  |  | 969 |     }
 | 
        
           |  |  | 970 |   | 
        
           |  |  | 971 |     protected function db_addslashes($text) {
 | 
        
           |  |  | 972 |         // Use custom made function for now - it is better to not rely on adodb or php defaults.
 | 
        
           |  |  | 973 |         if ($this->get_config('dbsybasequoting')) {
 | 
        
           |  |  | 974 |             $text = str_replace('\\', '\\\\', $text);
 | 
        
           |  |  | 975 |             $text = str_replace(array('\'', '"', "\0"), array('\\\'', '\\"', '\\0'), $text);
 | 
        
           |  |  | 976 |         } else {
 | 
        
           |  |  | 977 |             $text = str_replace("'", "''", $text);
 | 
        
           |  |  | 978 |         }
 | 
        
           |  |  | 979 |         return $text;
 | 
        
           |  |  | 980 |     }
 | 
        
           |  |  | 981 |   | 
        
           |  |  | 982 |     protected function db_encode($text) {
 | 
        
           |  |  | 983 |         $dbenc = $this->get_config('dbencoding');
 | 
        
           |  |  | 984 |         if (empty($dbenc) or $dbenc == 'utf-8') {
 | 
        
           |  |  | 985 |             return $text;
 | 
        
           |  |  | 986 |         }
 | 
        
           |  |  | 987 |         if (is_array($text)) {
 | 
        
           |  |  | 988 |             foreach($text as $k=>$value) {
 | 
        
           |  |  | 989 |                 $text[$k] = $this->db_encode($value);
 | 
        
           |  |  | 990 |             }
 | 
        
           |  |  | 991 |             return $text;
 | 
        
           |  |  | 992 |         } else {
 | 
        
           |  |  | 993 |             return core_text::convert($text, 'utf-8', $dbenc);
 | 
        
           |  |  | 994 |         }
 | 
        
           |  |  | 995 |     }
 | 
        
           |  |  | 996 |   | 
        
           |  |  | 997 |     protected function db_decode($text) {
 | 
        
           |  |  | 998 |         $dbenc = $this->get_config('dbencoding');
 | 
        
           |  |  | 999 |         if (empty($dbenc) or $dbenc == 'utf-8') {
 | 
        
           |  |  | 1000 |             return $text;
 | 
        
           |  |  | 1001 |         }
 | 
        
           |  |  | 1002 |         if (is_array($text)) {
 | 
        
           |  |  | 1003 |             foreach($text as $k=>$value) {
 | 
        
           |  |  | 1004 |                 $text[$k] = $this->db_decode($value);
 | 
        
           |  |  | 1005 |             }
 | 
        
           |  |  | 1006 |             return $text;
 | 
        
           |  |  | 1007 |         } else {
 | 
        
           |  |  | 1008 |             return core_text::convert($text, $dbenc, 'utf-8');
 | 
        
           |  |  | 1009 |         }
 | 
        
           |  |  | 1010 |     }
 | 
        
           |  |  | 1011 |   | 
        
           |  |  | 1012 |     /**
 | 
        
           |  |  | 1013 |      * Automatic enrol sync executed during restore.
 | 
        
           |  |  | 1014 |      * @param stdClass $course course record
 | 
        
           |  |  | 1015 |      */
 | 
        
           |  |  | 1016 |     public function restore_sync_course($course) {
 | 
        
           |  |  | 1017 |         $trace = new null_progress_trace();
 | 
        
           |  |  | 1018 |         $this->sync_enrolments($trace, $course->id);
 | 
        
           |  |  | 1019 |     }
 | 
        
           |  |  | 1020 |   | 
        
           |  |  | 1021 |     /**
 | 
        
           |  |  | 1022 |      * Restore instance and map settings.
 | 
        
           |  |  | 1023 |      *
 | 
        
           |  |  | 1024 |      * @param restore_enrolments_structure_step $step
 | 
        
           |  |  | 1025 |      * @param stdClass $data
 | 
        
           |  |  | 1026 |      * @param stdClass $course
 | 
        
           |  |  | 1027 |      * @param int $oldid
 | 
        
           |  |  | 1028 |      */
 | 
        
           |  |  | 1029 |     public function restore_instance(restore_enrolments_structure_step $step, stdClass $data, $course, $oldid) {
 | 
        
           |  |  | 1030 |         global $DB;
 | 
        
           |  |  | 1031 |   | 
        
           |  |  | 1032 |         if ($instance = $DB->get_record('enrol', array('courseid'=>$course->id, 'enrol'=>$this->get_name()))) {
 | 
        
           |  |  | 1033 |             $instanceid = $instance->id;
 | 
        
           |  |  | 1034 |         } else {
 | 
        
           |  |  | 1035 |             $instanceid = $this->add_instance($course);
 | 
        
           |  |  | 1036 |         }
 | 
        
           |  |  | 1037 |         $step->set_mapping('enrol', $oldid, $instanceid);
 | 
        
           |  |  | 1038 |     }
 | 
        
           |  |  | 1039 |   | 
        
           |  |  | 1040 |     /**
 | 
        
           |  |  | 1041 |      * Restore user enrolment.
 | 
        
           |  |  | 1042 |      *
 | 
        
           |  |  | 1043 |      * @param restore_enrolments_structure_step $step
 | 
        
           |  |  | 1044 |      * @param stdClass $data
 | 
        
           |  |  | 1045 |      * @param stdClass $instance
 | 
        
           |  |  | 1046 |      * @param int $oldinstancestatus
 | 
        
           |  |  | 1047 |      * @param int $userid
 | 
        
           |  |  | 1048 |      */
 | 
        
           |  |  | 1049 |     public function restore_user_enrolment(restore_enrolments_structure_step $step, $data, $instance, $userid, $oldinstancestatus) {
 | 
        
           |  |  | 1050 |         global $DB;
 | 
        
           |  |  | 1051 |   | 
        
           |  |  | 1052 |         if ($this->get_config('unenrolaction') == ENROL_EXT_REMOVED_UNENROL) {
 | 
        
           |  |  | 1053 |             // Enrolments were already synchronised in restore_instance(), we do not want any suspended leftovers.
 | 
        
           |  |  | 1054 |             return;
 | 
        
           |  |  | 1055 |         }
 | 
        
           |  |  | 1056 |         if (!$DB->record_exists('user_enrolments', array('enrolid'=>$instance->id, 'userid'=>$userid))) {
 | 
        
           |  |  | 1057 |             $this->enrol_user($instance, $userid, null, 0, 0, ENROL_USER_SUSPENDED);
 | 
        
           |  |  | 1058 |         }
 | 
        
           |  |  | 1059 |     }
 | 
        
           |  |  | 1060 |   | 
        
           |  |  | 1061 |     /**
 | 
        
           |  |  | 1062 |      * Restore role assignment.
 | 
        
           |  |  | 1063 |      *
 | 
        
           |  |  | 1064 |      * @param stdClass $instance
 | 
        
           |  |  | 1065 |      * @param int $roleid
 | 
        
           |  |  | 1066 |      * @param int $userid
 | 
        
           |  |  | 1067 |      * @param int $contextid
 | 
        
           |  |  | 1068 |      */
 | 
        
           |  |  | 1069 |     public function restore_role_assignment($instance, $roleid, $userid, $contextid) {
 | 
        
           |  |  | 1070 |         if ($this->get_config('unenrolaction') == ENROL_EXT_REMOVED_UNENROL or $this->get_config('unenrolaction') == ENROL_EXT_REMOVED_SUSPENDNOROLES) {
 | 
        
           |  |  | 1071 |             // Role assignments were already synchronised in restore_instance(), we do not want any leftovers.
 | 
        
           |  |  | 1072 |             return;
 | 
        
           |  |  | 1073 |         }
 | 
        
           |  |  | 1074 |         role_assign($roleid, $userid, $contextid, 'enrol_'.$this->get_name(), $instance->id);
 | 
        
           |  |  | 1075 |     }
 | 
        
           |  |  | 1076 |   | 
        
           |  |  | 1077 |     /**
 | 
        
           |  |  | 1078 |      * Test plugin settings, print info to output.
 | 
        
           |  |  | 1079 |      */
 | 
        
           |  |  | 1080 |     public function test_settings() {
 | 
        
           |  |  | 1081 |         global $CFG, $OUTPUT;
 | 
        
           |  |  | 1082 |   | 
        
           |  |  | 1083 |         // NOTE: this is not localised intentionally, admins are supposed to understand English at least a bit...
 | 
        
           |  |  | 1084 |   | 
        
           |  |  | 1085 |         raise_memory_limit(MEMORY_HUGE);
 | 
        
           |  |  | 1086 |   | 
        
           |  |  | 1087 |         $this->load_config();
 | 
        
           |  |  | 1088 |   | 
        
           |  |  | 1089 |         $enroltable = $this->get_config('remoteenroltable');
 | 
        
           |  |  | 1090 |         $coursetable = $this->get_config('newcoursetable');
 | 
        
           |  |  | 1091 |   | 
        
           |  |  | 1092 |         if (empty($enroltable)) {
 | 
        
           |  |  | 1093 |             echo $OUTPUT->notification('External enrolment table not specified.', 'notifyproblem');
 | 
        
           |  |  | 1094 |         }
 | 
        
           |  |  | 1095 |   | 
        
           |  |  | 1096 |         if (empty($coursetable)) {
 | 
        
           |  |  | 1097 |             echo $OUTPUT->notification('External course table not specified.', 'notifyproblem');
 | 
        
           |  |  | 1098 |         }
 | 
        
           |  |  | 1099 |   | 
        
           |  |  | 1100 |         if (empty($coursetable) and empty($enroltable)) {
 | 
        
           |  |  | 1101 |             return;
 | 
        
           |  |  | 1102 |         }
 | 
        
           |  |  | 1103 |   | 
        
           |  |  | 1104 |         $olddebug = $CFG->debug;
 | 
        
           |  |  | 1105 |         $olddisplay = ini_get('display_errors');
 | 
        
           |  |  | 1106 |         ini_set('display_errors', '1');
 | 
        
           |  |  | 1107 |         $CFG->debug = DEBUG_DEVELOPER;
 | 
        
           |  |  | 1108 |         $olddebugdb = $this->config->debugdb;
 | 
        
           |  |  | 1109 |         $this->config->debugdb = 1;
 | 
        
           |  |  | 1110 |         error_reporting($CFG->debug);
 | 
        
           |  |  | 1111 |   | 
        
           |  |  | 1112 |         $adodb = $this->db_init();
 | 
        
           |  |  | 1113 |   | 
        
           |  |  | 1114 |         if (!$adodb or !$adodb->IsConnected()) {
 | 
        
           |  |  | 1115 |             $this->config->debugdb = $olddebugdb;
 | 
        
           |  |  | 1116 |             $CFG->debug = $olddebug;
 | 
        
           |  |  | 1117 |             ini_set('display_errors', $olddisplay);
 | 
        
           |  |  | 1118 |             error_reporting($CFG->debug);
 | 
        
           |  |  | 1119 |             ob_end_flush();
 | 
        
           |  |  | 1120 |   | 
        
           |  |  | 1121 |             echo $OUTPUT->notification('Cannot connect the database.', 'notifyproblem');
 | 
        
           |  |  | 1122 |             return;
 | 
        
           |  |  | 1123 |         }
 | 
        
           |  |  | 1124 |   | 
        
           |  |  | 1125 |         if (!empty($enroltable)) {
 | 
        
           |  |  | 1126 |             $rs = $adodb->Execute("SELECT *
 | 
        
           |  |  | 1127 |                                      FROM $enroltable");
 | 
        
           |  |  | 1128 |             if (!$rs) {
 | 
        
           |  |  | 1129 |                 echo $OUTPUT->notification('Can not read external enrol table.', 'notifyproblem');
 | 
        
           |  |  | 1130 |   | 
        
           |  |  | 1131 |             } else if ($rs->EOF) {
 | 
        
           |  |  | 1132 |                 echo $OUTPUT->notification('External enrol table is empty.', 'notifyproblem');
 | 
        
           |  |  | 1133 |                 $rs->Close();
 | 
        
           |  |  | 1134 |   | 
        
           |  |  | 1135 |             } else {
 | 
        
           |  |  | 1136 |                 $columns = array_keys($rs->fetchRow());
 | 
        
           |  |  | 1137 |                 echo $OUTPUT->notification('External enrolment table contains following columns:<br />'.implode(', ', $columns), 'notifysuccess');
 | 
        
           |  |  | 1138 |                 $rs->Close();
 | 
        
           |  |  | 1139 |             }
 | 
        
           |  |  | 1140 |         }
 | 
        
           |  |  | 1141 |   | 
        
           |  |  | 1142 |         if (!empty($coursetable)) {
 | 
        
           |  |  | 1143 |             $rs = $adodb->Execute("SELECT *
 | 
        
           |  |  | 1144 |                                      FROM $coursetable");
 | 
        
           |  |  | 1145 |             if (!$rs) {
 | 
        
           |  |  | 1146 |                 echo $OUTPUT->notification('Can not read external course table.', 'notifyproblem');
 | 
        
           |  |  | 1147 |   | 
        
           |  |  | 1148 |             } else if ($rs->EOF) {
 | 
        
           |  |  | 1149 |                 echo $OUTPUT->notification('External course table is empty.', 'notifyproblem');
 | 
        
           |  |  | 1150 |                 $rs->Close();
 | 
        
           |  |  | 1151 |   | 
        
           |  |  | 1152 |             } else {
 | 
        
           |  |  | 1153 |                 $columns = array_keys($rs->fetchRow());
 | 
        
           |  |  | 1154 |                 echo $OUTPUT->notification('External course table contains following columns:<br />'.implode(', ', $columns), 'notifysuccess');
 | 
        
           |  |  | 1155 |                 $rs->Close();
 | 
        
           |  |  | 1156 |             }
 | 
        
           |  |  | 1157 |         }
 | 
        
           |  |  | 1158 |   | 
        
           |  |  | 1159 |         $adodb->Close();
 | 
        
           |  |  | 1160 |   | 
        
           |  |  | 1161 |         $this->config->debugdb = $olddebugdb;
 | 
        
           |  |  | 1162 |         $CFG->debug = $olddebug;
 | 
        
           |  |  | 1163 |         ini_set('display_errors', $olddisplay);
 | 
        
           |  |  | 1164 |         error_reporting($CFG->debug);
 | 
        
           |  |  | 1165 |         ob_end_flush();
 | 
        
           |  |  | 1166 |     }
 | 
        
           |  |  | 1167 | }
 |