Пример #1
0
 /**
  * Sync all meta course links.
  *
  * @param progress_trace $trace
  * @param int $courseid one course, empty mean all
  * @return int 0 means ok, 1 means error, 2 means plugin disabled
  */
 public function sync(progress_trace $trace, $courseid = null)
 {
     global $DB;
     if (!enrol_is_enabled('manual')) {
         $trace->finished();
         return 2;
     }
     // Unfortunately this may take a long time, execution can be interrupted safely here.
     @set_time_limit(0);
     raise_memory_limit(MEMORY_HUGE);
     $trace->output('Verifying manual enrolment expiration...');
     $params = array('now' => time(), 'useractive' => ENROL_USER_ACTIVE, 'courselevel' => CONTEXT_COURSE);
     $coursesql = "";
     if ($courseid) {
         $coursesql = "AND e.courseid = :courseid";
         $params['courseid'] = $courseid;
     }
     // Deal with expired accounts.
     $action = $this->get_config('expiredaction', ENROL_EXT_REMOVED_KEEP);
     if ($action == ENROL_EXT_REMOVED_UNENROL) {
         $instances = array();
         $sql = "SELECT ue.*, e.courseid, c.id AS contextid\n                      FROM {user_enrolments} ue\n                      JOIN {enrol} e ON (e.id = ue.enrolid AND e.enrol = 'manual')\n                      JOIN {context} c ON (c.instanceid = e.courseid AND c.contextlevel = :courselevel)\n                     WHERE ue.timeend > 0 AND ue.timeend < :now\n                           {$coursesql}";
         $rs = $DB->get_recordset_sql($sql, $params);
         foreach ($rs as $ue) {
             if (empty($instances[$ue->enrolid])) {
                 $instances[$ue->enrolid] = $DB->get_record('enrol', array('id' => $ue->enrolid));
             }
             $instance = $instances[$ue->enrolid];
             // Always remove all manually assigned roles here, this may break enrol_self roles but we do not want hardcoded hacks here.
             role_unassign_all(array('userid' => $ue->userid, 'contextid' => $ue->contextid, 'component' => '', 'itemid' => 0), true);
             $this->unenrol_user($instance, $ue->userid);
             $trace->output("unenrolling expired user {$ue->userid} from course {$instance->courseid}", 1);
         }
         $rs->close();
         unset($instances);
     } else {
         if ($action == ENROL_EXT_REMOVED_SUSPENDNOROLES) {
             $instances = array();
             $sql = "SELECT ue.*, e.courseid, c.id AS contextid\n                      FROM {user_enrolments} ue\n                      JOIN {enrol} e ON (e.id = ue.enrolid AND e.enrol = 'manual')\n                      JOIN {context} c ON (c.instanceid = e.courseid AND c.contextlevel = :courselevel)\n                     WHERE ue.timeend > 0 AND ue.timeend < :now\n                           AND ue.status = :useractive\n                           {$coursesql}";
             $rs = $DB->get_recordset_sql($sql, $params);
             foreach ($rs as $ue) {
                 if (empty($instances[$ue->enrolid])) {
                     $instances[$ue->enrolid] = $DB->get_record('enrol', array('id' => $ue->enrolid));
                 }
                 $instance = $instances[$ue->enrolid];
                 // Always remove all manually assigned roles here, this may break enrol_self roles but we do not want hardcoded hacks here.
                 role_unassign_all(array('userid' => $ue->userid, 'contextid' => $ue->contextid, 'component' => '', 'itemid' => 0), true);
                 $this->update_user_enrol($instance, $ue->userid, ENROL_USER_SUSPENDED);
                 $trace->output("suspending expired user {$ue->userid} in course {$instance->courseid}", 1);
             }
             $rs->close();
             unset($instances);
         } else {
             // ENROL_EXT_REMOVED_KEEP means no changes.
         }
     }
     $trace->output('...manual enrolment updates finished.');
     $trace->finished();
     return 0;
 }
Пример #2
0
 /**
  * Sync all meta course links.
  *
  * @param progress_trace $trace
  * @param int $courseid one course, empty mean all
  * @return int 0 means ok, 1 means error, 2 means plugin disabled
  */
 public function sync(progress_trace $trace, $courseid = null)
 {
     global $DB;
     if (!enrol_is_enabled('self')) {
         $trace->finished();
         return 2;
     }
     // Unfortunately this may take a long time, execution can be interrupted safely here.
     core_php_time_limit::raise();
     raise_memory_limit(MEMORY_HUGE);
     $trace->output('Verifying self-enrolments...');
     $params = array('now' => time(), 'useractive' => ENROL_USER_ACTIVE, 'courselevel' => CONTEXT_COURSE);
     $coursesql = "";
     if ($courseid) {
         $coursesql = "AND e.courseid = :courseid";
         $params['courseid'] = $courseid;
     }
     // Note: the logic of self enrolment guarantees that user logged in at least once (=== u.lastaccess set)
     //       and that user accessed course at least once too (=== user_lastaccess record exists).
     // First deal with users that did not log in for a really long time - they do not have user_lastaccess records.
     $sql = "SELECT e.*, ue.userid\n                  FROM {user_enrolments} ue\n                  JOIN {enrol} e ON (e.id = ue.enrolid AND e.enrol = 'self' AND e.customint2 > 0)\n                  JOIN {user} u ON u.id = ue.userid\n                 WHERE :now - u.lastaccess > e.customint2\n                       {$coursesql}";
     $rs = $DB->get_recordset_sql($sql, $params);
     foreach ($rs as $instance) {
         $userid = $instance->userid;
         unset($instance->userid);
         $this->unenrol_user($instance, $userid);
         $days = $instance->customint2 / 60 * 60 * 24;
         $trace->output("unenrolling user {$userid} from course {$instance->courseid} as they have did not log in for at least {$days} days", 1);
     }
     $rs->close();
     // Now unenrol from course user did not visit for a long time.
     $sql = "SELECT e.*, ue.userid\n                  FROM {user_enrolments} ue\n                  JOIN {enrol} e ON (e.id = ue.enrolid AND e.enrol = 'self' AND e.customint2 > 0)\n                  JOIN {user_lastaccess} ul ON (ul.userid = ue.userid AND ul.courseid = e.courseid)\n                 WHERE :now - ul.timeaccess > e.customint2\n                       {$coursesql}";
     $rs = $DB->get_recordset_sql($sql, $params);
     foreach ($rs as $instance) {
         $userid = $instance->userid;
         unset($instance->userid);
         $this->unenrol_user($instance, $userid);
         $days = $instance->customint2 / 60 * 60 * 24;
         $trace->output("unenrolling user {$userid} from course {$instance->courseid} as they have did not access course for at least {$days} days", 1);
     }
     $rs->close();
     $trace->output('...user self-enrolment updates finished.');
     $trace->finished();
     $this->process_expirations($trace, $courseid);
     return 0;
 }
Пример #3
0
 /**
  * Synchronizes user from external db to moodle user table.
  *
  * Sync should be done by using idnumber attribute, not username.
  * You need to pass firstsync parameter to function to fill in
  * idnumbers if they don't exists in moodle user table.
  *
  * Syncing users removes (disables) users that don't exists anymore in external db.
  * Creates new users and updates coursecreator status of users.
  *
  * This implementation is simpler but less scalable than the one found in the LDAP module.
  *
  * @param progress_trace $trace
  * @param bool $do_updates  Optional: set to true to force an update of existing accounts
  * @return int 0 means success, 1 means failure
  */
 function sync_users(progress_trace $trace, $do_updates = false)
 {
     global $CFG, $DB;
     require_once $CFG->dirroot . '/user/lib.php';
     // List external users.
     $userlist = $this->get_userlist();
     // Delete obsolete internal users.
     if (!empty($this->config->removeuser)) {
         $suspendselect = "";
         if ($this->config->removeuser == AUTH_REMOVEUSER_SUSPEND) {
             $suspendselect = "AND u.suspended = 0";
         }
         // Find obsolete users.
         if (count($userlist)) {
             list($notin_sql, $params) = $DB->get_in_or_equal($userlist, SQL_PARAMS_NAMED, 'u', false);
             $params['authtype'] = $this->authtype;
             $sql = "SELECT u.*\n                          FROM {user} u\n                         WHERE u.auth=:authtype AND u.deleted=0 AND u.mnethostid=:mnethostid {$suspendselect} AND u.username {$notin_sql}";
         } else {
             $sql = "SELECT u.*\n                          FROM {user} u\n                         WHERE u.auth=:authtype AND u.deleted=0 AND u.mnethostid=:mnethostid {$suspendselect}";
             $params = array();
             $params['authtype'] = $this->authtype;
         }
         $params['mnethostid'] = $CFG->mnet_localhost_id;
         $remove_users = $DB->get_records_sql($sql, $params);
         if (!empty($remove_users)) {
             $trace->output(get_string('auth_dbuserstoremove', 'auth_db', count($remove_users)));
             foreach ($remove_users as $user) {
                 if ($this->config->removeuser == AUTH_REMOVEUSER_FULLDELETE) {
                     delete_user($user);
                     $trace->output(get_string('auth_dbdeleteuser', 'auth_db', array('name' => $user->username, 'id' => $user->id)), 1);
                 } else {
                     if ($this->config->removeuser == AUTH_REMOVEUSER_SUSPEND) {
                         $updateuser = new stdClass();
                         $updateuser->id = $user->id;
                         $updateuser->suspended = 1;
                         user_update_user($updateuser, false);
                         $trace->output(get_string('auth_dbsuspenduser', 'auth_db', array('name' => $user->username, 'id' => $user->id)), 1);
                     }
                 }
             }
         }
         unset($remove_users);
     }
     if (!count($userlist)) {
         // Exit right here, nothing else to do.
         $trace->finished();
         return 0;
     }
     // Update existing accounts.
     if ($do_updates) {
         // Narrow down what fields we need to update.
         $all_keys = array_keys(get_object_vars($this->config));
         $updatekeys = array();
         foreach ($all_keys as $key) {
             if (preg_match('/^field_updatelocal_(.+)$/', $key, $match)) {
                 if ($this->config->{$key} === 'onlogin') {
                     array_push($updatekeys, $match[1]);
                     // The actual key name.
                 }
             }
         }
         unset($all_keys);
         unset($key);
         // Only go ahead if we actually have fields to update locally.
         if (!empty($updatekeys)) {
             list($in_sql, $params) = $DB->get_in_or_equal($userlist, SQL_PARAMS_NAMED, 'u', true);
             $params['authtype'] = $this->authtype;
             $sql = "SELECT u.id, u.username\n                          FROM {user} u\n                         WHERE u.auth=:authtype AND u.deleted=0 AND u.username {$in_sql}";
             if ($update_users = $DB->get_records_sql($sql, $params)) {
                 $trace->output("User entries to update: " . count($update_users));
                 foreach ($update_users as $user) {
                     if ($this->update_user_record($user->username, $updatekeys)) {
                         $trace->output(get_string('auth_dbupdatinguser', 'auth_db', array('name' => $user->username, 'id' => $user->id)), 1);
                     } else {
                         $trace->output(get_string('auth_dbupdatinguser', 'auth_db', array('name' => $user->username, 'id' => $user->id)) . " - " . get_string('skipped'), 1);
                     }
                 }
                 unset($update_users);
             }
         }
     }
     // Create missing accounts.
     // NOTE: this is very memory intensive and generally inefficient.
     $suspendselect = "";
     if ($this->config->removeuser == AUTH_REMOVEUSER_SUSPEND) {
         $suspendselect = "AND u.suspended = 0";
     }
     $sql = "SELECT u.id, u.username\n                  FROM {user} u\n                 WHERE u.auth=:authtype AND u.deleted='0' AND mnethostid=:mnethostid {$suspendselect}";
     $users = $DB->get_records_sql($sql, array('authtype' => $this->authtype, 'mnethostid' => $CFG->mnet_localhost_id));
     // Simplify down to usernames.
     $usernames = array();
     if (!empty($users)) {
         foreach ($users as $user) {
             array_push($usernames, $user->username);
         }
         unset($users);
     }
     $add_users = array_diff($userlist, $usernames);
     unset($usernames);
     if (!empty($add_users)) {
         $trace->output(get_string('auth_dbuserstoadd', 'auth_db', count($add_users)));
         // Do not use transactions around this foreach, we want to skip problematic users, not revert everything.
         foreach ($add_users as $user) {
             $username = $user;
             if ($this->config->removeuser == AUTH_REMOVEUSER_SUSPEND) {
                 if ($olduser = $DB->get_record('user', array('username' => $username, 'deleted' => 0, 'suspended' => 1, 'mnethostid' => $CFG->mnet_localhost_id, 'auth' => $this->authtype))) {
                     $updateuser = new stdClass();
                     $updateuser->id = $olduser->id;
                     $updateuser->suspended = 0;
                     user_update_user($updateuser);
                     $trace->output(get_string('auth_dbreviveduser', 'auth_db', array('name' => $username, 'id' => $olduser->id)), 1);
                     continue;
                 }
             }
             // Do not try to undelete users here, instead select suspending if you ever expect users will reappear.
             // Prep a few params.
             $user = $this->get_userinfo_asobj($user);
             $user->username = $username;
             $user->confirmed = 1;
             $user->auth = $this->authtype;
             $user->mnethostid = $CFG->mnet_localhost_id;
             if (empty($user->lang)) {
                 $user->lang = $CFG->lang;
             }
             if ($collision = $DB->get_record_select('user', "username = :username AND mnethostid = :mnethostid AND auth <> :auth", array('username' => $user->username, 'mnethostid' => $CFG->mnet_localhost_id, 'auth' => $this->authtype), 'id,username,auth')) {
                 $trace->output(get_string('auth_dbinsertuserduplicate', 'auth_db', array('username' => $user->username, 'auth' => $collision->auth)), 1);
                 continue;
             }
             try {
                 $id = user_create_user($user, false);
                 // It is truly a new user.
                 $trace->output(get_string('auth_dbinsertuser', 'auth_db', array('name' => $user->username, 'id' => $id)), 1);
             } catch (moodle_exception $e) {
                 $trace->output(get_string('auth_dbinsertusererror', 'auth_db', $user->username), 1);
                 continue;
             }
             // If relevant, tag for password generation.
             if ($this->is_internal()) {
                 set_user_preference('auth_forcepasswordchange', 1, $id);
                 set_user_preference('create_password', 1, $id);
             }
             // Make sure user context is present.
             context_user::instance($id);
         }
         unset($add_users);
     }
     $trace->finished();
     return 0;
 }
Пример #4
0
 /**
  * Process any future enrollments stored in the buffer.
  * @param progress_trace $trace
  * @return bool true if any data processed, false if not
  */
 protected function process_buffer(progress_trace $trace)
 {
     global $DB;
     if (!($future_enrols = $DB->get_records_select('enrol_flatfile', "timestart < ?", array(time())))) {
         $trace->output("No enrolments to be processed in flatfile buffer");
         $trace->finished();
         return false;
     }
     $trace->output("Starting processing of flatfile buffer");
     foreach ($future_enrols as $en) {
         $user = $DB->get_record('user', array('id' => $en->userid));
         $course = $DB->get_record('course', array('id' => $en->courseid));
         if ($user and $course) {
             $trace->output("buffer: {$en->action} {$en->roleid} {$user->id} {$course->id} {$en->timestart} {$en->timeend}", 1);
             $this->process_records($trace, $en->action, $en->roleid, $user, $course, $en->timestart, $en->timeend, false);
         }
         $DB->delete_records('enrol_flatfile', array('id' => $en->id));
     }
     $trace->output("Finished processing of flatfile buffer");
     $trace->finished();
     return true;
 }
Пример #5
0
/**
 * Synchronise courses in all categories.
 *
 * It gets out-of-sync if:
 * - you move course to different category
 * - reorder categories
 * - disable enrol_category and enable it again
 *
 * @param progress_trace $trace
 * @return int exit code - 0 is ok, 1 means error, 2 if plugin disabled
 */
function enrol_category_sync_full(progress_trace $trace)
{
    global $DB;
    if (!enrol_is_enabled('category')) {
        $trace->finished();
        return 2;
    }
    // We may need a lot of time here.
    core_php_time_limit::raise();
    $plugin = enrol_get_plugin('category');
    $syscontext = context_system::instance();
    // Any interesting roles worth synchronising?
    if (!($roles = get_roles_with_capability('enrol/category:synchronised', CAP_ALLOW, $syscontext))) {
        // yay, nothing to do, so let's remove all leftovers
        $trace->output("No roles with 'enrol/category:synchronised' capability found.");
        if ($instances = $DB->get_records('enrol', array('enrol' => 'category'))) {
            $trace->output("Deleting all category enrol instances...");
            foreach ($instances as $instance) {
                $trace->output("deleting category enrol instance from course {$instance->courseid}", 1);
                $plugin->delete_instance($instance);
            }
            $trace->output("...all instances deleted.");
        }
        $trace->finished();
        return 0;
    }
    $rolenames = role_fix_names($roles, null, ROLENAME_SHORT, true);
    $trace->output('Synchronising category enrolments for roles: ' . implode(', ', $rolenames) . '...');
    list($roleids, $params) = $DB->get_in_or_equal(array_keys($roles), SQL_PARAMS_NAMED, 'r');
    $params['courselevel'] = CONTEXT_COURSE;
    $params['catlevel'] = CONTEXT_COURSECAT;
    // First of all add necessary enrol instances to all courses.
    $parentcat = $DB->sql_concat("cat.path", "'/%'");
    $parentcctx = $DB->sql_concat("cctx.path", "'/%'");
    // Need whole course records to be used by add_instance(), use inner view (ci) to
    // get distinct records only.
    // TODO: Moodle 2.1. Improve enrol API to accept courseid / courserec
    $sql = "SELECT c.*\n              FROM {course} c\n              JOIN (\n                SELECT DISTINCT c.id\n                  FROM {course} c\n                  JOIN {context} ctx ON (ctx.instanceid = c.id AND ctx.contextlevel = :courselevel)\n                  JOIN (SELECT DISTINCT cctx.path\n                          FROM {course_categories} cc\n                          JOIN {context} cctx ON (cctx.instanceid = cc.id AND cctx.contextlevel = :catlevel)\n                          JOIN {role_assignments} ra ON (ra.contextid = cctx.id AND ra.roleid {$roleids})\n                       ) cat ON (ctx.path LIKE {$parentcat})\n             LEFT JOIN {enrol} e ON (e.courseid = c.id AND e.enrol = 'category')\n                 WHERE e.id IS NULL) ci ON (c.id = ci.id)";
    $rs = $DB->get_recordset_sql($sql, $params);
    foreach ($rs as $course) {
        $plugin->add_instance($course);
    }
    $rs->close();
    // Now look for courses that do not have any interesting roles in parent contexts,
    // but still have the instance and delete them.
    $sql = "SELECT e.*\n              FROM {enrol} e\n              JOIN {context} ctx ON (ctx.instanceid = e.courseid AND ctx.contextlevel = :courselevel)\n         LEFT JOIN ({course_categories} cc\n                      JOIN {context} cctx ON (cctx.instanceid = cc.id AND cctx.contextlevel = :catlevel)\n                      JOIN {role_assignments} ra ON (ra.contextid = cctx.id AND ra.roleid {$roleids})\n                   ) ON (ctx.path LIKE {$parentcctx})\n             WHERE e.enrol = 'category' AND cc.id IS NULL";
    $rs = $DB->get_recordset_sql($sql, $params);
    foreach ($rs as $instance) {
        $plugin->delete_instance($instance);
    }
    $rs->close();
    // Add missing enrolments.
    $sql = "SELECT e.*, cat.userid, cat.estart\n              FROM {enrol} e\n              JOIN {context} ctx ON (ctx.instanceid = e.courseid AND ctx.contextlevel = :courselevel)\n              JOIN (SELECT cctx.path, ra.userid, MIN(ra.timemodified) AS estart\n                      FROM {course_categories} cc\n                      JOIN {context} cctx ON (cctx.instanceid = cc.id AND cctx.contextlevel = :catlevel)\n                      JOIN {role_assignments} ra ON (ra.contextid = cctx.id AND ra.roleid {$roleids})\n                  GROUP BY cctx.path, ra.userid\n                   ) cat ON (ctx.path LIKE {$parentcat})\n         LEFT JOIN {user_enrolments} ue ON (ue.enrolid = e.id AND ue.userid = cat.userid)\n             WHERE e.enrol = 'category' AND ue.id IS NULL";
    $rs = $DB->get_recordset_sql($sql, $params);
    foreach ($rs as $instance) {
        $userid = $instance->userid;
        $estart = $instance->estart;
        unset($instance->userid);
        unset($instance->estart);
        $plugin->enrol_user($instance, $userid, null, $estart);
        $trace->output("enrolling: user {$userid} ==> course {$instance->courseid}", 1);
    }
    $rs->close();
    // Remove stale enrolments.
    $sql = "SELECT e.*, ue.userid\n              FROM {enrol} e\n              JOIN {context} ctx ON (ctx.instanceid = e.courseid AND ctx.contextlevel = :courselevel)\n              JOIN {user_enrolments} ue ON (ue.enrolid = e.id)\n         LEFT JOIN ({course_categories} cc\n                      JOIN {context} cctx ON (cctx.instanceid = cc.id AND cctx.contextlevel = :catlevel)\n                      JOIN {role_assignments} ra ON (ra.contextid = cctx.id AND ra.roleid {$roleids})\n                   ) ON (ctx.path LIKE {$parentcctx} AND ra.userid = ue.userid)\n             WHERE e.enrol = 'category' AND cc.id IS NULL";
    $rs = $DB->get_recordset_sql($sql, $params);
    foreach ($rs as $instance) {
        $userid = $instance->userid;
        unset($instance->userid);
        $plugin->unenrol_user($instance, $userid);
        $trace->output("unenrolling: user {$userid} ==> course {$instance->courseid}", 1);
    }
    $rs->close();
    $trace->output('...user enrolment synchronisation finished.');
    $trace->finished();
    return 0;
}
Пример #6
0
 /**
  * Send expiry notifications.
  *
  * Plugin that wants to have expiry notification MUST implement following:
  * - expirynotifyhour plugin setting,
  * - configuration options in instance edit form (expirynotify, notifyall and expirythreshold),
  * - notification strings (expirymessageenrollersubject, expirymessageenrollerbody,
  *   expirymessageenrolledsubject and expirymessageenrolledbody),
  * - expiry_notification provider in db/messages.php,
  * - upgrade code that sets default thresholds for existing courses (should be 1 day),
  * - something that calls this method, such as cron.
  *
  * @param progress_trace $trace (accepts bool for backwards compatibility only)
  */
 public function send_expiry_notifications($trace)
 {
     global $DB, $CFG;
     $name = $this->get_name();
     if (!enrol_is_enabled($name)) {
         $trace->finished();
         return;
     }
     // Unfortunately this may take a long time, it should not be interrupted,
     // otherwise users get duplicate notification.
     @set_time_limit(0);
     raise_memory_limit(MEMORY_HUGE);
     $expirynotifylast = $this->get_config('expirynotifylast', 0);
     $expirynotifyhour = $this->get_config('expirynotifyhour');
     if (is_null($expirynotifyhour)) {
         debugging("send_expiry_notifications() in {$name} enrolment plugin needs expirynotifyhour setting");
         $trace->finished();
         return;
     }
     if (!$trace instanceof progress_trace) {
         $trace = $trace ? new text_progress_trace() : new null_progress_trace();
         debugging('enrol_plugin::send_expiry_notifications() now expects progress_trace instance as parameter!', DEBUG_DEVELOPER);
     }
     $timenow = time();
     $notifytime = usergetmidnight($timenow, $CFG->timezone) + $expirynotifyhour * 3600;
     if ($expirynotifylast > $notifytime) {
         $trace->output($name . ' enrolment expiry notifications were already sent today at ' . userdate($expirynotifylast, '', $CFG->timezone) . '.');
         $trace->finished();
         return;
     } else {
         if ($timenow < $notifytime) {
             $trace->output($name . ' enrolment expiry notifications will be sent at ' . userdate($notifytime, '', $CFG->timezone) . '.');
             $trace->finished();
             return;
         }
     }
     $trace->output('Processing ' . $name . ' enrolment expiration notifications...');
     // Notify users responsible for enrolment once every day.
     $sql = "SELECT ue.*, e.expirynotify, e.notifyall, e.expirythreshold, e.courseid, c.fullname\n                  FROM {user_enrolments} ue\n                  JOIN {enrol} e ON (e.id = ue.enrolid AND e.enrol = :name AND e.expirynotify > 0 AND e.status = :enabled)\n                  JOIN {course} c ON (c.id = e.courseid)\n                  JOIN {user} u ON (u.id = ue.userid AND u.deleted = 0 AND u.suspended = 0)\n                 WHERE ue.status = :active AND ue.timeend > 0 AND ue.timeend > :now1 AND ue.timeend < (e.expirythreshold + :now2)\n              ORDER BY ue.enrolid ASC, u.lastname ASC, u.firstname ASC, u.id ASC";
     $params = array('enabled' => ENROL_INSTANCE_ENABLED, 'active' => ENROL_USER_ACTIVE, 'now1' => $timenow, 'now2' => $timenow, 'name' => $name);
     $rs = $DB->get_recordset_sql($sql, $params);
     $lastenrollid = 0;
     $users = array();
     foreach ($rs as $ue) {
         if ($lastenrollid and $lastenrollid != $ue->enrolid) {
             $this->notify_expiry_enroller($lastenrollid, $users, $trace);
             $users = array();
         }
         $lastenrollid = $ue->enrolid;
         $enroller = $this->get_enroller($ue->enrolid);
         $context = context_course::instance($ue->courseid);
         $user = $DB->get_record('user', array('id' => $ue->userid));
         $users[] = array('fullname' => fullname($user, has_capability('moodle/site:viewfullnames', $context, $enroller)), 'timeend' => $ue->timeend);
         if (!$ue->notifyall) {
             continue;
         }
         if ($ue->timeend - $ue->expirythreshold + 86400 < $timenow) {
             // Notify enrolled users only once at the start of the threshold.
             $trace->output("user {$ue->userid} was already notified that enrolment in course {$ue->courseid} expires on " . userdate($ue->timeend, '', $CFG->timezone), 1);
             continue;
         }
         $this->notify_expiry_enrolled($user, $ue, $trace);
     }
     $rs->close();
     if ($lastenrollid and $users) {
         $this->notify_expiry_enroller($lastenrollid, $users, $trace);
     }
     $trace->output('...notification processing finished.');
     $trace->finished();
     $this->set_config('expirynotifylast', $timenow);
 }
Пример #7
0
 /**
  * Performs a full sync with external database.
  *
  * First it creates new courses if necessary, then
  * enrols and unenrols users.
  *
  * @param progress_trace $trace
  * @return int 0 means success, 1 db connect failure, 4 db read failure
  */
 public function sync_courses(progress_trace $trace)
 {
     global $CFG, $DB;
     // Make sure we sync either enrolments or courses.
     if (!$this->get_config('dbtype') or !$this->get_config('newcoursetable') or !$this->get_config('newcoursefullname') or !$this->get_config('newcourseshortname')) {
         $trace->output('Course synchronisation skipped.');
         $trace->finished();
         return 0;
     }
     $trace->output('Starting course synchronisation...');
     // We may need a lot of memory here.
     core_php_time_limit::raise();
     raise_memory_limit(MEMORY_HUGE);
     if (!($extdb = $this->db_init())) {
         $trace->output('Error while communicating with external enrolment database');
         $trace->finished();
         return 1;
     }
     $table = $this->get_config('newcoursetable');
     $fullname = trim($this->get_config('newcoursefullname'));
     $shortname = trim($this->get_config('newcourseshortname'));
     $idnumber = trim($this->get_config('newcourseidnumber'));
     $category = trim($this->get_config('newcoursecategory'));
     // Lowercased versions - necessary because we normalise the resultset with array_change_key_case().
     $fullname_l = strtolower($fullname);
     $shortname_l = strtolower($shortname);
     $idnumber_l = strtolower($idnumber);
     $category_l = strtolower($category);
     $localcategoryfield = $this->get_config('localcategoryfield', 'id');
     $defaultcategory = $this->get_config('defaultcategory');
     if (!$DB->record_exists('course_categories', array('id' => $defaultcategory))) {
         $trace->output("default course category does not exist!", 1);
         $categories = $DB->get_records('course_categories', array(), 'sortorder', 'id', 0, 1);
         $first = reset($categories);
         $defaultcategory = $first->id;
     }
     $sqlfields = array($fullname, $shortname);
     if ($category) {
         $sqlfields[] = $category;
     }
     if ($idnumber) {
         $sqlfields[] = $idnumber;
     }
     $sql = $this->db_get_sql($table, array(), $sqlfields, true);
     $createcourses = array();
     if ($rs = $extdb->Execute($sql)) {
         if (!$rs->EOF) {
             while ($fields = $rs->FetchRow()) {
                 $fields = array_change_key_case($fields, CASE_LOWER);
                 $fields = $this->db_decode($fields);
                 if (empty($fields[$shortname_l]) or empty($fields[$fullname_l])) {
                     $trace->output('error: invalid external course record, shortname and fullname are mandatory: ' . json_encode($fields), 1);
                     // Hopefully every geek can read JS, right?
                     continue;
                 }
                 if ($DB->record_exists('course', array('shortname' => $fields[$shortname_l]))) {
                     // Already exists, skip.
                     continue;
                 }
                 // Allow empty idnumber but not duplicates.
                 if ($idnumber and $fields[$idnumber_l] !== '' and $fields[$idnumber_l] !== null and $DB->record_exists('course', array('idnumber' => $fields[$idnumber_l]))) {
                     $trace->output('error: duplicate idnumber, can not create course: ' . $fields[$shortname_l] . ' [' . $fields[$idnumber_l] . ']', 1);
                     continue;
                 }
                 $course = new stdClass();
                 $course->fullname = $fields[$fullname_l];
                 $course->shortname = $fields[$shortname_l];
                 $course->idnumber = $idnumber ? $fields[$idnumber_l] : '';
                 if ($category) {
                     if (empty($fields[$category_l])) {
                         // Empty category means use default.
                         $course->category = $defaultcategory;
                     } else {
                         if ($coursecategory = $DB->get_record('course_categories', array($localcategoryfield => $fields[$category_l]), 'id')) {
                             // Yay, correctly specified category!
                             $course->category = $coursecategory->id;
                             unset($coursecategory);
                         } else {
                             // Bad luck, better not continue because unwanted ppl might get access to course in different category.
                             $trace->output('error: invalid category ' . $localcategoryfield . ', can not create course: ' . $fields[$shortname_l], 1);
                             continue;
                         }
                     }
                 } else {
                     $course->category = $defaultcategory;
                 }
                 $createcourses[] = $course;
             }
         }
         $rs->Close();
     } else {
         $extdb->Close();
         $trace->output('Error reading data from the external course table');
         $trace->finished();
         return 4;
     }
     if ($createcourses) {
         require_once "{$CFG->dirroot}/course/lib.php";
         $templatecourse = $this->get_config('templatecourse');
         $template = false;
         if ($templatecourse) {
             if ($template = $DB->get_record('course', array('shortname' => $templatecourse))) {
                 $template = fullclone(course_get_format($template)->get_course());
                 unset($template->id);
                 unset($template->fullname);
                 unset($template->shortname);
                 unset($template->idnumber);
             } else {
                 $trace->output("can not find template for new course!", 1);
             }
         }
         if (!$template) {
             $courseconfig = get_config('moodlecourse');
             $template = new stdClass();
             $template->summary = '';
             $template->summaryformat = FORMAT_HTML;
             $template->format = $courseconfig->format;
             $template->newsitems = $courseconfig->newsitems;
             $template->showgrades = $courseconfig->showgrades;
             $template->showreports = $courseconfig->showreports;
             $template->maxbytes = $courseconfig->maxbytes;
             $template->groupmode = $courseconfig->groupmode;
             $template->groupmodeforce = $courseconfig->groupmodeforce;
             $template->visible = $courseconfig->visible;
             $template->lang = $courseconfig->lang;
             $template->groupmodeforce = $courseconfig->groupmodeforce;
         }
         foreach ($createcourses as $fields) {
             $newcourse = clone $template;
             $newcourse->fullname = $fields->fullname;
             $newcourse->shortname = $fields->shortname;
             $newcourse->idnumber = $fields->idnumber;
             $newcourse->category = $fields->category;
             // Detect duplicate data once again, above we can not find duplicates
             // in external data using DB collation rules...
             if ($DB->record_exists('course', array('shortname' => $newcourse->shortname))) {
                 $trace->output("can not insert new course, duplicate shortname detected: " . $newcourse->shortname, 1);
                 continue;
             } else {
                 if (!empty($newcourse->idnumber) and $DB->record_exists('course', array('idnumber' => $newcourse->idnumber))) {
                     $trace->output("can not insert new course, duplicate idnumber detected: " . $newcourse->idnumber, 1);
                     continue;
                 }
             }
             $c = create_course($newcourse);
             $trace->output("creating course: {$c->id}, {$c->fullname}, {$c->shortname}, {$c->idnumber}, {$c->category}", 1);
         }
         unset($createcourses);
         unset($template);
     }
     // Close db connection.
     $extdb->Close();
     $trace->output('...course synchronisation finished.');
     $trace->finished();
     return 0;
 }