static function cron()
 {
     // Setup user object (as admin)
     cron_setup_user();
     // Newline after the forum...
     mtrace("");
     // Send forum emails and digests
     self::email();
     // Delete old playsapces
     self::daily_housekeeping();
 }
 /**
  * Run the deletion task.
  *
  * @throws \coding_exception if the module could not be removed.
  */
 public function execute()
 {
     global $CFG;
     require_once $CFG->dirroot . '/course/lib.php';
     // Set the proper user.
     if ($this->get_custom_data()->userid !== $this->get_custom_data()->realuserid) {
         $realuser = \core_user::get_user($this->get_custom_data()->realuserid, '*', MUST_EXIST);
         cron_setup_user($realuser);
         \core\session\manager::loginas($this->get_custom_data()->userid, \context_system::instance(), false);
     } else {
         $user = \core_user::get_user($this->get_custom_data()->userid, '*', MUST_EXIST);
         cron_setup_user($user);
     }
     $cms = $this->get_custom_data()->cms;
     foreach ($cms as $cm) {
         try {
             course_delete_module($cm->id);
         } catch (\Exception $e) {
             throw new \coding_exception("The course module {$cm->id} could not be deleted. {$e->getTraceAsString()}");
         }
     }
 }
 /**
  * Process a message and pass it through the Inbound Message handling systems.
  *
  * @param \Horde_Imap_Client_Data_Fetch $message The message to process
  * @param bool $viewreadmessages Whether to also look at messages which have been marked as read
  * @param bool $skipsenderverification Whether to skip the sender verification stage
  */
 public function process_message(\Horde_Imap_Client_Data_Fetch $message, $viewreadmessages = false, $skipsenderverification = false)
 {
     global $USER;
     // We use the Client IDs several times - store them here.
     $messageid = new \Horde_Imap_Client_Ids($message->getUid());
     mtrace("- Parsing message " . $messageid);
     // First flag this message to prevent another running hitting this message while we look at the headers.
     $this->add_flag_to_message($messageid, self::MESSAGE_FLAGGED);
     if ($this->is_bulk_message($message, $messageid)) {
         mtrace("- The message has a bulk header set. This is likely an auto-generated reply - discarding.");
         return;
     }
     // Record the user that this script is currently being run as.  This is important when re-processing existing
     // messages, as cron_setup_user is called multiple times.
     $originaluser = $USER;
     $envelope = $message->getEnvelope();
     $recipients = $envelope->to->bare_addresses;
     foreach ($recipients as $recipient) {
         if (!\core\message\inbound\address_manager::is_correct_format($recipient)) {
             // Message did not contain a subaddress.
             mtrace("- Recipient '{$recipient}' did not match Inbound Message headers.");
             continue;
         }
         // Message contained a match.
         $senders = $message->getEnvelope()->from->bare_addresses;
         if (count($senders) !== 1) {
             mtrace("- Received multiple senders. Only the first sender will be used.");
         }
         $sender = array_shift($senders);
         mtrace("-- Subject:\t" . $envelope->subject);
         mtrace("-- From:\t" . $sender);
         mtrace("-- Recipient:\t" . $recipient);
         // Grab messagedata including flags.
         $query = new \Horde_Imap_Client_Fetch_Query();
         $query->structure();
         $messagedata = $this->client->fetch($this->get_mailbox(), $query, array('ids' => $messageid))->first();
         if (!$viewreadmessages && $this->message_has_flag($messageid, self::MESSAGE_SEEN)) {
             // Something else has already seen this message. Skip it now.
             mtrace("-- Skipping the message - it has been marked as seen - perhaps by another process.");
             continue;
         }
         // Mark it as read to lock the message.
         $this->add_flag_to_message($messageid, self::MESSAGE_SEEN);
         // Now pass it through the Inbound Message processor.
         $status = $this->addressmanager->process_envelope($recipient, $sender);
         if (($status & ~\core\message\inbound\address_manager::VALIDATION_DISABLED_HANDLER) !== $status) {
             // The handler is disabled.
             mtrace("-- Skipped message - Handler is disabled. Fail code {$status}");
             // In order to handle the user error, we need more information about the message being failed.
             $this->process_message_data($envelope, $messagedata, $messageid);
             $this->inform_user_of_error(get_string('handlerdisabled', 'tool_messageinbound', $this->currentmessagedata));
             return;
         }
         // Check the validation status early. No point processing garbage messages, but we do need to process it
         // for some validation failure types.
         if (!$this->passes_key_validation($status, $messageid)) {
             // None of the above validation failures were found. Skip this message.
             mtrace("-- Skipped message - it does not appear to relate to a Inbound Message pickup. Fail code {$status}");
             // Remove the seen flag from the message as there may be multiple recipients.
             $this->remove_flag_from_message($messageid, self::MESSAGE_SEEN);
             // Skip further processing for this recipient.
             continue;
         }
         // Process the message as the user.
         $user = $this->addressmanager->get_data()->user;
         mtrace("-- Processing the message as user {$user->id} ({$user->username}).");
         cron_setup_user($user);
         // Process and retrieve the message data for this message.
         // This includes fetching the full content, as well as all headers, and attachments.
         if (!$this->process_message_data($envelope, $messagedata, $messageid)) {
             mtrace("--- Message could not be found on the server. Is another process removing messages?");
             return;
         }
         // When processing validation replies, we need to skip the sender verification phase as this has been
         // manually completed.
         if (!$skipsenderverification && $status !== 0) {
             // Check the validation status for failure types which require confirmation.
             // The validation result is tested in a bitwise operation.
             mtrace("-- Message did not meet validation but is possibly recoverable. Fail code {$status}");
             // This is a recoverable error, but requires user input.
             if ($this->handle_verification_failure($messageid, $recipient)) {
                 mtrace("--- Original message retained on mail server and confirmation message sent to user.");
             } else {
                 mtrace("--- Invalid Recipient Handler - unable to save. Informing the user of the failure.");
                 $this->inform_user_of_error(get_string('invalidrecipientfinal', 'tool_messageinbound', $this->currentmessagedata));
             }
             // Returning to normal cron user.
             mtrace("-- Returning to the original user.");
             cron_setup_user($originaluser);
             return;
         }
         // Add the content and attachment data.
         mtrace("-- Validation completed. Fetching rest of message content.");
         $this->process_message_data_body($messagedata, $messageid);
         // The message processor throws exceptions upon failure. These must be caught and notifications sent to
         // the user here.
         try {
             $result = $this->send_to_handler();
         } catch (\core\message\inbound\processing_failed_exception $e) {
             // We know about these kinds of errors and they should result in the user being notified of the
             // failure. Send the user a notification here.
             $this->inform_user_of_error($e->getMessage());
             // Returning to normal cron user.
             mtrace("-- Returning to the original user.");
             cron_setup_user($originaluser);
             return;
         } catch (\Exception $e) {
             // An unknown error occurred. The user is not informed, but the administrator is.
             mtrace("-- Message processing failed. An unexpected exception was thrown. Details follow.");
             mtrace($e->getMessage());
             // Returning to normal cron user.
             mtrace("-- Returning to the original user.");
             cron_setup_user($originaluser);
             return;
         }
         if ($result) {
             // Handle message cleanup. Messages are deleted once fully processed.
             mtrace("-- Marking the message for removal.");
             $this->add_flag_to_message($messageid, self::MESSAGE_DELETED);
         } else {
             mtrace("-- The Inbound Message processor did not return a success status. Skipping message removal.");
         }
         // Returning to normal cron user.
         mtrace("-- Returning to the original user.");
         cron_setup_user($originaluser);
         mtrace("-- Finished processing " . $message->getUid());
         // Skip the outer loop too. The message has already been processed and it could be possible for there to
         // be two recipients in the envelope which match somehow.
         return;
     }
 }
/**
 * Function to be run periodically according to the moodle
 * This function searches for things that need to be done, such
 * as sending out mail, toggling flags etc ...
 * @return boolean always true
 * @uses $CFG
 * @uses $DB
 */
function scheduler_cron()
{
    global $CFG, $DB;
    $date = make_timestamp(date('Y'), date('m'), date('d'), date('H'), date('i'));
    // for every appointment in all schedulers
    $select = 'emaildate > 0 AND emaildate <= ? AND starttime > ?';
    $slots = $DB->get_records_select('scheduler_slots', $select, array($date, $date), 'starttime');
    foreach ($slots as $slot) {
        // get teacher
        $teacher = $DB->get_record('user', array('id' => $slot->teacherid));
        // get scheduler, slot and course
        $scheduler = scheduler_instance::load_by_id($slot->schedulerid);
        $slotm = $scheduler->get_slot($slot->id);
        $course = $DB->get_record('course', array('id' => $scheduler->course));
        // get appointed student list
        $appointments = $DB->get_records('scheduler_appointment', array('slotid' => $slot->id), '', 'id, studentid');
        //if no email previously sent and one is required
        foreach ($appointments as $appointment) {
            $student = $DB->get_record('user', array('id' => $appointment->studentid));
            cron_setup_user($student, $course);
            $vars = scheduler_get_mail_variables($scheduler, $slotm, $teacher, $student, $course, $student);
            scheduler_send_email_from_template($student, $teacher, $course, 'remindtitle', 'reminder', $vars, 'scheduler');
        }
        // mark as sent
        $slot->emaildate = -1;
        $DB->update_record('scheduler_slots', $slot);
    }
    cron_setup_user();
    return true;
}
Exemple #5
0
/**
 * Cron functions.
 *
 * @package    core
 * @subpackage admin
 * @copyright  1999 onwards Martin Dougiamas  http://dougiamas.com
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 */
function cron_run()
{
    global $DB, $CFG, $OUTPUT;
    if (CLI_MAINTENANCE) {
        echo "CLI maintenance mode active, cron execution suspended.\n";
        exit(1);
    }
    if (moodle_needs_upgrading()) {
        echo "Moodle upgrade pending, cron execution suspended.\n";
        exit(1);
    }
    require_once $CFG->libdir . '/adminlib.php';
    require_once $CFG->libdir . '/gradelib.php';
    if (!empty($CFG->showcronsql)) {
        $DB->set_debug(true);
    }
    if (!empty($CFG->showcrondebugging)) {
        $CFG->debug = DEBUG_DEVELOPER;
        $CFG->debugdisplay = true;
    }
    set_time_limit(0);
    $starttime = microtime();
    /// increase memory limit
    raise_memory_limit(MEMORY_EXTRA);
    /// emulate normal session
    cron_setup_user();
    /// Start output log
    $timenow = time();
    mtrace("Server Time: " . date('r', $timenow) . "\n\n");
    /// Session gc
    mtrace("Cleaning up stale sessions");
    session_gc();
    /// Run all cron jobs for each module
    mtrace("Starting activity modules");
    get_mailer('buffer');
    if ($mods = $DB->get_records_select("modules", "cron > 0 AND ((? - lastcron) > cron) AND visible = 1", array($timenow))) {
        foreach ($mods as $mod) {
            $libfile = "{$CFG->dirroot}/mod/{$mod->name}/lib.php";
            if (file_exists($libfile)) {
                include_once $libfile;
                $cron_function = $mod->name . "_cron";
                if (function_exists($cron_function)) {
                    mtrace("Processing module function {$cron_function} ...", '');
                    $pre_dbqueries = null;
                    $pre_dbqueries = $DB->perf_get_queries();
                    $pre_time = microtime(1);
                    if ($cron_function()) {
                        $DB->set_field("modules", "lastcron", $timenow, array("id" => $mod->id));
                    }
                    if (isset($pre_dbqueries)) {
                        mtrace("... used " . ($DB->perf_get_queries() - $pre_dbqueries) . " dbqueries");
                        mtrace("... used " . (microtime(1) - $pre_time) . " seconds");
                    }
                    /// Reset possible changes by modules to time_limit. MDL-11597
                    @set_time_limit(0);
                    mtrace("done.");
                }
            }
        }
    }
    get_mailer('close');
    mtrace("Finished activity modules");
    mtrace("Starting blocks");
    if ($blocks = $DB->get_records_select("block", "cron > 0 AND ((? - lastcron) > cron) AND visible = 1", array($timenow))) {
        // we will need the base class.
        require_once $CFG->dirroot . '/blocks/moodleblock.class.php';
        foreach ($blocks as $block) {
            $blockfile = $CFG->dirroot . '/blocks/' . $block->name . '/block_' . $block->name . '.php';
            if (file_exists($blockfile)) {
                require_once $blockfile;
                $classname = 'block_' . $block->name;
                $blockobj = new $classname();
                if (method_exists($blockobj, 'cron')) {
                    mtrace("Processing cron function for " . $block->name . '....', '');
                    if ($blockobj->cron()) {
                        $DB->set_field('block', 'lastcron', $timenow, array('id' => $block->id));
                    }
                    /// Reset possible changes by blocks to time_limit. MDL-11597
                    @set_time_limit(0);
                    mtrace('done.');
                }
            }
        }
    }
    mtrace('Finished blocks');
    //now do plagiarism checks
    require_once $CFG->libdir . '/plagiarismlib.php';
    plagiarism_cron();
    mtrace("Starting quiz reports");
    if ($reports = $DB->get_records_select('quiz_report', "cron > 0 AND ((? - lastcron) > cron)", array($timenow))) {
        foreach ($reports as $report) {
            $cronfile = "{$CFG->dirroot}/mod/quiz/report/{$report->name}/cron.php";
            if (file_exists($cronfile)) {
                include_once $cronfile;
                $cron_function = 'quiz_report_' . $report->name . "_cron";
                if (function_exists($cron_function)) {
                    mtrace("Processing quiz report cron function {$cron_function} ...", '');
                    $pre_dbqueries = null;
                    $pre_dbqueries = $DB->perf_get_queries();
                    $pre_time = microtime(1);
                    if ($cron_function()) {
                        $DB->set_field('quiz_report', "lastcron", $timenow, array("id" => $report->id));
                    }
                    if (isset($pre_dbqueries)) {
                        mtrace("... used " . ($DB->perf_get_queries() - $pre_dbqueries) . " dbqueries");
                        mtrace("... used " . (microtime(1) - $pre_time) . " seconds");
                    }
                    mtrace("done.");
                }
            }
        }
    }
    mtrace("Finished quiz reports");
    mtrace('Starting admin reports');
    // Admin reports do not have a database table that lists them. Instead a
    // report includes cron.php with function report_reportname_cron() if it wishes
    // to be cronned. It is up to cron.php to handle e.g. if it only needs to
    // actually do anything occasionally.
    $reports = get_plugin_list('report');
    foreach ($reports as $report => $reportdir) {
        $cronfile = $reportdir . '/cron.php';
        if (file_exists($cronfile)) {
            require_once $cronfile;
            $cronfunction = 'report_' . $report . '_cron';
            mtrace('Processing cron function for ' . $report . '...', '');
            $pre_dbqueries = null;
            $pre_dbqueries = $DB->perf_get_queries();
            $pre_time = microtime(true);
            $cronfunction();
            if (isset($pre_dbqueries)) {
                mtrace("... used " . ($DB->perf_get_queries() - $pre_dbqueries) . " dbqueries");
                mtrace("... used " . round(microtime(true) - $pre_time, 2) . " seconds");
            }
            mtrace('done.');
        }
    }
    mtrace('Finished admin reports');
    mtrace('Starting main gradebook job ...');
    grade_cron();
    mtrace('done.');
    mtrace('Starting processing the event queue...');
    events_cron();
    mtrace('done.');
    if ($CFG->enablecompletion) {
        // Completion cron
        mtrace('Starting the completion cron...');
        require_once $CFG->libdir . '/completion/cron.php';
        completion_cron();
        mtrace('done');
    }
    if ($CFG->enableportfolios) {
        // Portfolio cron
        mtrace('Starting the portfolio cron...');
        require_once $CFG->libdir . '/portfoliolib.php';
        portfolio_cron();
        mtrace('done');
    }
    /// Run all core cron jobs, but not every time since they aren't too important.
    /// These don't have a timer to reduce load, so we'll use a random number
    /// to randomly choose the percentage of times we should run these jobs.
    srand((double) microtime() * 10000000);
    $random100 = rand(0, 100);
    if ($random100 < 20) {
        // Approximately 20% of the time.
        mtrace("Running clean-up tasks...");
        /// Delete users who haven't confirmed within required period
        if (!empty($CFG->deleteunconfirmed)) {
            $cuttime = $timenow - $CFG->deleteunconfirmed * 3600;
            $rs = $DB->get_recordset_sql("SELECT id, firstname, lastname\n                                             FROM {user}\n                                            WHERE confirmed = 0 AND firstaccess > 0\n                                                  AND firstaccess < ?", array($cuttime));
            foreach ($rs as $user) {
                if ($DB->delete_records('user', array('id' => $user->id))) {
                    mtrace("Deleted unconfirmed user for " . fullname($user, true) . " ({$user->id})");
                }
            }
            $rs->close();
        }
        flush();
        /// Delete users who haven't completed profile within required period
        if (!empty($CFG->deleteincompleteusers)) {
            $cuttime = $timenow - $CFG->deleteincompleteusers * 3600;
            $rs = $DB->get_recordset_sql("SELECT id, username\n                                             FROM {user}\n                                            WHERE confirmed = 1 AND lastaccess > 0\n                                                  AND lastaccess < ? AND deleted = 0\n                                                  AND (lastname = '' OR firstname = '' OR email = '')", array($cuttime));
            foreach ($rs as $user) {
                if (delete_user($user)) {
                    mtrace("Deleted not fully setup user {$user->username} ({$user->id})");
                }
            }
            $rs->close();
        }
        flush();
        /// Delete old logs to save space (this might need a timer to slow it down...)
        if (!empty($CFG->loglifetime)) {
            // value in days
            $loglifetime = $timenow - $CFG->loglifetime * 3600 * 24;
            if ($DB->delete_records_select("log", "time < ?", array($loglifetime))) {
                mtrace("Deleted old log records");
            }
        }
        flush();
        // Delete old backup_controllers and logs
        if (!empty($CFG->loglifetime)) {
            // value in days
            $loglifetime = $timenow - $CFG->loglifetime * 3600 * 24;
            // Delete child records from backup_logs
            $DB->execute("DELETE FROM {backup_logs}\n                           WHERE EXISTS (\n                               SELECT 'x'\n                                 FROM {backup_controllers} bc\n                                WHERE bc.backupid = {backup_logs}.backupid\n                                  AND bc.timecreated < ?)", array($loglifetime));
            // Delete records from backup_controllers
            $DB->execute("DELETE FROM {backup_controllers}\n                          WHERE timecreated < ?", array($loglifetime));
            mtrace("Deleted old backup records");
        }
        flush();
        /// Delete old cached texts
        if (!empty($CFG->cachetext)) {
            // Defined in config.php
            $cachelifetime = time() - $CFG->cachetext - 60;
            // Add an extra minute to allow for really heavy sites
            if ($DB->delete_records_select('cache_text', "timemodified < ?", array($cachelifetime))) {
                mtrace("Deleted old cache_text records");
            }
        }
        flush();
        if (!empty($CFG->notifyloginfailures)) {
            notify_login_failures();
            mtrace('Notified login failured');
        }
        flush();
        //
        // generate new password emails for users
        //
        mtrace('checking for create_password');
        if ($DB->count_records('user_preferences', array('name' => 'create_password', 'value' => '1'))) {
            mtrace('creating passwords for new users');
            $newusers = $DB->get_records_sql("SELECT u.id as id, u.email, u.firstname,\n                                                     u.lastname, u.username,\n                                                     p.id as prefid\n                                                FROM {user} u\n                                                JOIN {user_preferences} p ON u.id=p.userid\n                                               WHERE p.name='create_password' AND p.value='1' AND u.email !='' ");
            foreach ($newusers as $newuserid => $newuser) {
                // email user
                if (setnew_password_and_mail($newuser)) {
                    // remove user pref
                    $DB->delete_records('user_preferences', array('id' => $newuser->prefid));
                } else {
                    trigger_error("Could not create and mail new user password!");
                }
            }
        }
        if (!empty($CFG->usetags)) {
            require_once $CFG->dirroot . '/tag/lib.php';
            tag_cron();
            mtrace('Executed tag cron');
        }
        // Accesslib stuff
        cleanup_contexts();
        mtrace('Cleaned up contexts');
        gc_cache_flags();
        mtrace('Cleaned cache flags');
        // If you suspect that the context paths are somehow corrupt
        // replace the line below with: build_context_path(true);
        build_context_path();
        mtrace('Built context paths');
        if (!empty($CFG->messagingdeletereadnotificationsdelay)) {
            $notificationdeletetime = time() - $CFG->messagingdeletereadnotificationsdelay;
            $DB->delete_records_select('message_read', 'notification=1 AND timeread<:notificationdeletetime', array('notificationdeletetime' => $notificationdeletetime));
            mtrace('Cleaned up read notifications');
        }
        mtrace("Finished clean-up tasks...");
    }
    // End of occasional clean-up tasks
    // Run automated backups if required.
    require_once $CFG->dirroot . '/backup/util/includes/backup_includes.php';
    require_once $CFG->dirroot . '/backup/util/helper/backup_cron_helper.class.php';
    backup_cron_automated_helper::run_automated_backup();
    /// Run the auth cron, if any
    /// before enrolments because it might add users that will be needed in enrol plugins
    $auths = get_enabled_auth_plugins();
    mtrace("Running auth crons if required...");
    foreach ($auths as $auth) {
        $authplugin = get_auth_plugin($auth);
        if (method_exists($authplugin, 'cron')) {
            mtrace("Running cron for auth/{$auth}...");
            $authplugin->cron();
            if (!empty($authplugin->log)) {
                mtrace($authplugin->log);
            }
        }
        unset($authplugin);
    }
    mtrace("Running enrol crons if required...");
    $enrols = enrol_get_plugins(true);
    foreach ($enrols as $ename => $enrol) {
        // do this for all plugins, disabled plugins might want to cleanup stuff such as roles
        if (!$enrol->is_cron_required()) {
            continue;
        }
        mtrace("Running cron for enrol_{$ename}...");
        $enrol->cron();
        $enrol->set_config('lastcron', time());
    }
    if (!empty($CFG->enablestats) and empty($CFG->disablestatsprocessing)) {
        require_once $CFG->dirroot . '/lib/statslib.php';
        // check we're not before our runtime
        $timetocheck = stats_get_base_daily() + $CFG->statsruntimestarthour * 60 * 60 + $CFG->statsruntimestartminute * 60;
        if (time() > $timetocheck) {
            // process configured number of days as max (defaulting to 31)
            $maxdays = empty($CFG->statsruntimedays) ? 31 : abs($CFG->statsruntimedays);
            if (stats_cron_daily($maxdays)) {
                if (stats_cron_weekly()) {
                    if (stats_cron_monthly()) {
                        stats_clean_old();
                    }
                }
            }
            @set_time_limit(0);
        } else {
            mtrace('Next stats run after:' . userdate($timetocheck));
        }
    }
    // run gradebook import/export/report cron
    if ($gradeimports = get_plugin_list('gradeimport')) {
        foreach ($gradeimports as $gradeimport => $plugindir) {
            if (file_exists($plugindir . '/lib.php')) {
                require_once $plugindir . '/lib.php';
                $cron_function = 'grade_import_' . $gradeimport . '_cron';
                if (function_exists($cron_function)) {
                    mtrace("Processing gradebook import function {$cron_function} ...", '');
                    $cron_function();
                }
            }
        }
    }
    if ($gradeexports = get_plugin_list('gradeexport')) {
        foreach ($gradeexports as $gradeexport => $plugindir) {
            if (file_exists($plugindir . '/lib.php')) {
                require_once $plugindir . '/lib.php';
                $cron_function = 'grade_export_' . $gradeexport . '_cron';
                if (function_exists($cron_function)) {
                    mtrace("Processing gradebook export function {$cron_function} ...", '');
                    $cron_function();
                }
            }
        }
    }
    if ($gradereports = get_plugin_list('gradereport')) {
        foreach ($gradereports as $gradereport => $plugindir) {
            if (file_exists($plugindir . '/lib.php')) {
                require_once $plugindir . '/lib.php';
                $cron_function = 'grade_report_' . $gradereport . '_cron';
                if (function_exists($cron_function)) {
                    mtrace("Processing gradebook report function {$cron_function} ...", '');
                    $cron_function();
                }
            }
        }
    }
    // Run external blog cron if needed
    if ($CFG->useexternalblogs) {
        require_once $CFG->dirroot . '/blog/lib.php';
        mtrace("Fetching external blog entries...", '');
        $sql = "timefetched < ? OR timefetched = 0";
        $externalblogs = $DB->get_records_select('blog_external', $sql, array(mktime() - $CFG->externalblogcrontime));
        foreach ($externalblogs as $eb) {
            blog_sync_external_entries($eb);
        }
    }
    // Run blog associations cleanup
    if ($CFG->useblogassociations) {
        require_once $CFG->dirroot . '/blog/lib.php';
        // delete entries whose contextids no longer exists
        mtrace("Deleting blog associations linked to non-existent contexts...", '');
        $DB->delete_records_select('blog_association', 'contextid NOT IN (SELECT id FROM {context})');
    }
    //Run registration updated cron
    mtrace(get_string('siteupdatesstart', 'hub'));
    require_once $CFG->dirroot . '/admin/registration/lib.php';
    $registrationmanager = new registration_manager();
    $registrationmanager->cron();
    mtrace(get_string('siteupdatesend', 'hub'));
    // cleanup file trash
    $fs = get_file_storage();
    $fs->cron();
    //cleanup old session linked tokens
    //deletes the session linked tokens that are over a day old.
    mtrace("Deleting session linked tokens more than one day old...", '');
    $DB->delete_records_select('external_tokens', 'lastaccess < :onedayago AND tokentype = :tokentype', array('onedayago' => time() - DAYSECS, 'tokentype' => EXTERNAL_TOKEN_EMBEDDED));
    mtrace('done.');
    // run any customized cronjobs, if any
    if ($locals = get_plugin_list('local')) {
        mtrace('Processing customized cron scripts ...', '');
        foreach ($locals as $local => $localdir) {
            if (file_exists("{$localdir}/cron.php")) {
                include "{$localdir}/cron.php";
            }
        }
        mtrace('done.');
    }
    mtrace("Cron script completed correctly");
    $difftime = microtime_diff($starttime, microtime());
    mtrace("Execution took " . $difftime . " seconds");
}
Exemple #6
0
 public function test_cron_message_includes_courseid()
 {
     // First run cron so there are no messages waiting to be sent (from other tests).
     cron_setup_user();
     assign::cron();
     // Now create an assignment.
     $this->setUser($this->editingteachers[0]);
     $assign = $this->create_instance(array('sendstudentnotifications' => 1));
     // Simulate adding a grade.
     $this->setUser($this->teachers[0]);
     $data = new stdClass();
     $data->grade = '50.0';
     $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
     $this->preventResetByRollback();
     $sink = $this->redirectEvents();
     $this->expectOutputRegex('/Done processing 1 assignment submissions/');
     assign::cron();
     $events = $sink->get_events();
     // Two messages are sent, one to student and one to teacher. This generates
     // four events:
     // core\event\message_sent
     // core\event\message_viewed
     // core\event\message_sent
     // core\event\message_viewed.
     $event = reset($events);
     $this->assertInstanceOf('\\core\\event\\message_sent', $event);
     $this->assertEquals($assign->get_course()->id, $event->other['courseid']);
     $sink->close();
 }
/**
 * Execute cron tasks
 */
function cron_run()
{
    global $DB, $CFG, $OUTPUT;
    if (CLI_MAINTENANCE) {
        echo "CLI maintenance mode active, cron execution suspended.\n";
        exit(1);
    }
    if (moodle_needs_upgrading()) {
        echo "Moodle upgrade pending, cron execution suspended.\n";
        exit(1);
    }
    require_once $CFG->libdir . '/adminlib.php';
    require_once $CFG->libdir . '/gradelib.php';
    if (!empty($CFG->showcronsql)) {
        $DB->set_debug(true);
    }
    if (!empty($CFG->showcrondebugging)) {
        set_debugging(DEBUG_DEVELOPER, true);
    }
    set_time_limit(0);
    $starttime = microtime();
    // Increase memory limit
    raise_memory_limit(MEMORY_EXTRA);
    // Emulate normal session - we use admin accoutn by default
    cron_setup_user();
    // Start output log
    $timenow = time();
    mtrace("Server Time: " . date('r', $timenow) . "\n\n");
    // Run cleanup core cron jobs, but not every time since they aren't too important.
    // These don't have a timer to reduce load, so we'll use a random number
    // to randomly choose the percentage of times we should run these jobs.
    $random100 = rand(0, 100);
    if ($random100 < 20) {
        // Approximately 20% of the time.
        mtrace("Running clean-up tasks...");
        cron_trace_time_and_memory();
        // Delete users who haven't confirmed within required period
        if (!empty($CFG->deleteunconfirmed)) {
            $cuttime = $timenow - $CFG->deleteunconfirmed * 3600;
            $rs = $DB->get_recordset_sql("SELECT *\n                                             FROM {user}\n                                            WHERE confirmed = 0 AND firstaccess > 0\n                                                  AND firstaccess < ?", array($cuttime));
            foreach ($rs as $user) {
                delete_user($user);
                // we MUST delete user properly first
                $DB->delete_records('user', array('id' => $user->id));
                // this is a bloody hack, but it might work
                mtrace(" Deleted unconfirmed user for " . fullname($user, true) . " ({$user->id})");
            }
            $rs->close();
        }
        // Delete users who haven't completed profile within required period
        if (!empty($CFG->deleteincompleteusers)) {
            $cuttime = $timenow - $CFG->deleteincompleteusers * 3600;
            $rs = $DB->get_recordset_sql("SELECT *\n                                             FROM {user}\n                                            WHERE confirmed = 1 AND lastaccess > 0\n                                                  AND lastaccess < ? AND deleted = 0\n                                                  AND (lastname = '' OR firstname = '' OR email = '')", array($cuttime));
            foreach ($rs as $user) {
                if (isguestuser($user) or is_siteadmin($user)) {
                    continue;
                }
                delete_user($user);
                mtrace(" Deleted not fully setup user {$user->username} ({$user->id})");
            }
            $rs->close();
        }
        // Delete old logs to save space (this might need a timer to slow it down...)
        if (!empty($CFG->loglifetime)) {
            // value in days
            $loglifetime = $timenow - $CFG->loglifetime * 3600 * 24;
            $DB->delete_records_select("log", "time < ?", array($loglifetime));
            mtrace(" Deleted old log records");
        }
        // Delete old backup_controllers and logs.
        $loglifetime = get_config('backup', 'loglifetime');
        if (!empty($loglifetime)) {
            // Value in days.
            $loglifetime = $timenow - $loglifetime * 3600 * 24;
            // Delete child records from backup_logs.
            $DB->execute("DELETE FROM {backup_logs}\n                           WHERE EXISTS (\n                               SELECT 'x'\n                                 FROM {backup_controllers} bc\n                                WHERE bc.backupid = {backup_logs}.backupid\n                                  AND bc.timecreated < ?)", array($loglifetime));
            // Delete records from backup_controllers.
            $DB->execute("DELETE FROM {backup_controllers}\n                          WHERE timecreated < ?", array($loglifetime));
            mtrace(" Deleted old backup records");
        }
        // Delete old cached texts
        if (!empty($CFG->cachetext)) {
            // Defined in config.php
            $cachelifetime = time() - $CFG->cachetext - 60;
            // Add an extra minute to allow for really heavy sites
            $DB->delete_records_select('cache_text', "timemodified < ?", array($cachelifetime));
            mtrace(" Deleted old cache_text records");
        }
        if (!empty($CFG->usetags)) {
            require_once $CFG->dirroot . '/tag/lib.php';
            tag_cron();
            mtrace(' Executed tag cron');
        }
        // Context maintenance stuff
        context_helper::cleanup_instances();
        mtrace(' Cleaned up context instances');
        context_helper::build_all_paths(false);
        // If you suspect that the context paths are somehow corrupt
        // replace the line below with: context_helper::build_all_paths(true);
        mtrace(' Built context paths');
        // Remove expired cache flags
        gc_cache_flags();
        mtrace(' Cleaned cache flags');
        // Cleanup messaging
        if (!empty($CFG->messagingdeletereadnotificationsdelay)) {
            $notificationdeletetime = time() - $CFG->messagingdeletereadnotificationsdelay;
            $DB->delete_records_select('message_read', 'notification=1 AND timeread<:notificationdeletetime', array('notificationdeletetime' => $notificationdeletetime));
            mtrace(' Cleaned up read notifications');
        }
        mtrace(' Deleting temporary files...');
        cron_delete_from_temp();
        // Cleanup user password reset records
        // Delete any reset request records which are expired by more than a day.
        // (We keep recently expired requests around so we can give a different error msg to users who
        // are trying to user a recently expired reset attempt).
        $pwresettime = isset($CFG->pwresettime) ? $CFG->pwresettime : 1800;
        $earliestvalid = time() - $pwresettime - DAYSECS;
        $DB->delete_records_select('user_password_resets', "timerequested < ?", array($earliestvalid));
        mtrace(' Cleaned up old password reset records');
        mtrace("...finished clean-up tasks");
    }
    // End of occasional clean-up tasks
    // Send login failures notification - brute force protection in moodle is weak,
    // we should at least send notices early in each cron execution
    if (notify_login_failures()) {
        mtrace(' Notified login failures');
    }
    // Make sure all context instances are properly created - they may be required in auth, enrol, etc.
    context_helper::create_instances();
    mtrace(' Created missing context instances');
    // Session gc.
    mtrace("Running session gc tasks...");
    \core\session\manager::gc();
    mtrace("...finished stale session cleanup");
    // Run the auth cron, if any before enrolments
    // because it might add users that will be needed in enrol plugins
    $auths = get_enabled_auth_plugins();
    mtrace("Running auth crons if required...");
    cron_trace_time_and_memory();
    foreach ($auths as $auth) {
        $authplugin = get_auth_plugin($auth);
        if (method_exists($authplugin, 'cron')) {
            mtrace("Running cron for auth/{$auth}...");
            $authplugin->cron();
            if (!empty($authplugin->log)) {
                mtrace($authplugin->log);
            }
        }
        unset($authplugin);
    }
    // Generate new password emails for users - ppl expect these generated asap
    if ($DB->count_records('user_preferences', array('name' => 'create_password', 'value' => '1'))) {
        mtrace('Creating passwords for new users...');
        $usernamefields = get_all_user_name_fields(true, 'u');
        $newusers = $DB->get_recordset_sql("SELECT u.id as id, u.email,\n                                                 {$usernamefields}, u.username, u.lang,\n                                                 p.id as prefid\n                                            FROM {user} u\n                                            JOIN {user_preferences} p ON u.id=p.userid\n                                           WHERE p.name='create_password' AND p.value='1' AND u.email !='' AND u.suspended = 0 AND u.auth != 'nologin' AND u.deleted = 0");
        // note: we can not send emails to suspended accounts
        foreach ($newusers as $newuser) {
            // Use a low cost factor when generating bcrypt hash otherwise
            // hashing would be slow when emailing lots of users. Hashes
            // will be automatically updated to a higher cost factor the first
            // time the user logs in.
            if (setnew_password_and_mail($newuser, true)) {
                unset_user_preference('create_password', $newuser);
                set_user_preference('auth_forcepasswordchange', 1, $newuser);
            } else {
                trigger_error("Could not create and mail new user password!");
            }
        }
        $newusers->close();
    }
    // It is very important to run enrol early
    // because other plugins depend on correct enrolment info.
    mtrace("Running enrol crons if required...");
    $enrols = enrol_get_plugins(true);
    foreach ($enrols as $ename => $enrol) {
        // do this for all plugins, disabled plugins might want to cleanup stuff such as roles
        if (!$enrol->is_cron_required()) {
            continue;
        }
        mtrace("Running cron for enrol_{$ename}...");
        cron_trace_time_and_memory();
        $enrol->cron();
        $enrol->set_config('lastcron', time());
    }
    // Run all cron jobs for each module
    mtrace("Starting activity modules");
    get_mailer('buffer');
    if ($mods = $DB->get_records_select("modules", "cron > 0 AND ((? - lastcron) > cron) AND visible = 1", array($timenow))) {
        foreach ($mods as $mod) {
            $libfile = "{$CFG->dirroot}/mod/{$mod->name}/lib.php";
            if (file_exists($libfile)) {
                include_once $libfile;
                $cron_function = $mod->name . "_cron";
                if (function_exists($cron_function)) {
                    mtrace("Processing module function {$cron_function} ...", '');
                    cron_trace_time_and_memory();
                    $pre_dbqueries = null;
                    $pre_dbqueries = $DB->perf_get_queries();
                    $pre_time = microtime(1);
                    if ($cron_function()) {
                        $DB->set_field("modules", "lastcron", $timenow, array("id" => $mod->id));
                    }
                    if (isset($pre_dbqueries)) {
                        mtrace("... used " . ($DB->perf_get_queries() - $pre_dbqueries) . " dbqueries");
                        mtrace("... used " . (microtime(1) - $pre_time) . " seconds");
                    }
                    // Reset possible changes by modules to time_limit. MDL-11597
                    @set_time_limit(0);
                    mtrace("done.");
                }
            }
        }
    }
    get_mailer('close');
    mtrace("Finished activity modules");
    mtrace("Starting blocks");
    if ($blocks = $DB->get_records_select("block", "cron > 0 AND ((? - lastcron) > cron) AND visible = 1", array($timenow))) {
        // We will need the base class.
        require_once $CFG->dirroot . '/blocks/moodleblock.class.php';
        foreach ($blocks as $block) {
            $blockfile = $CFG->dirroot . '/blocks/' . $block->name . '/block_' . $block->name . '.php';
            if (file_exists($blockfile)) {
                require_once $blockfile;
                $classname = 'block_' . $block->name;
                $blockobj = new $classname();
                if (method_exists($blockobj, 'cron')) {
                    mtrace("Processing cron function for " . $block->name . '....', '');
                    cron_trace_time_and_memory();
                    if ($blockobj->cron()) {
                        $DB->set_field('block', 'lastcron', $timenow, array('id' => $block->id));
                    }
                    // Reset possible changes by blocks to time_limit. MDL-11597
                    @set_time_limit(0);
                    mtrace('done.');
                }
            }
        }
    }
    mtrace('Finished blocks');
    mtrace('Starting admin reports');
    cron_execute_plugin_type('report');
    mtrace('Finished admin reports');
    mtrace('Starting main gradebook job...');
    cron_trace_time_and_memory();
    grade_cron();
    mtrace('done.');
    mtrace('Starting processing the event queue...');
    cron_trace_time_and_memory();
    events_cron();
    mtrace('done.');
    if ($CFG->enablecompletion) {
        // Completion cron
        mtrace('Starting the completion cron...');
        cron_trace_time_and_memory();
        require_once $CFG->dirroot . '/completion/cron.php';
        completion_cron();
        mtrace('done');
    }
    if ($CFG->enableportfolios) {
        // Portfolio cron
        mtrace('Starting the portfolio cron...');
        cron_trace_time_and_memory();
        require_once $CFG->libdir . '/portfoliolib.php';
        portfolio_cron();
        mtrace('done');
    }
    //now do plagiarism checks
    require_once $CFG->libdir . '/plagiarismlib.php';
    plagiarism_cron();
    mtrace('Starting course reports');
    cron_execute_plugin_type('coursereport');
    mtrace('Finished course reports');
    // run gradebook import/export/report cron
    mtrace('Starting gradebook plugins');
    cron_execute_plugin_type('gradeimport');
    cron_execute_plugin_type('gradeexport');
    cron_execute_plugin_type('gradereport');
    mtrace('Finished gradebook plugins');
    // run calendar cron
    require_once "{$CFG->dirroot}/calendar/lib.php";
    calendar_cron();
    // Run external blog cron if needed
    if (!empty($CFG->enableblogs) && $CFG->useexternalblogs) {
        require_once $CFG->dirroot . '/blog/lib.php';
        mtrace("Fetching external blog entries...", '');
        cron_trace_time_and_memory();
        $sql = "timefetched < ? OR timefetched = 0";
        $externalblogs = $DB->get_records_select('blog_external', $sql, array(time() - $CFG->externalblogcrontime));
        foreach ($externalblogs as $eb) {
            blog_sync_external_entries($eb);
        }
        mtrace('done.');
    }
    // Run blog associations cleanup
    if (!empty($CFG->enableblogs) && $CFG->useblogassociations) {
        require_once $CFG->dirroot . '/blog/lib.php';
        // delete entries whose contextids no longer exists
        mtrace("Deleting blog associations linked to non-existent contexts...", '');
        cron_trace_time_and_memory();
        $DB->delete_records_select('blog_association', 'contextid NOT IN (SELECT id FROM {context})');
        mtrace('done.');
    }
    // Run question bank clean-up.
    mtrace("Starting the question bank cron...", '');
    cron_trace_time_and_memory();
    require_once $CFG->libdir . '/questionlib.php';
    question_bank::cron();
    mtrace('done.');
    //Run registration updated cron
    mtrace(get_string('siteupdatesstart', 'hub'));
    cron_trace_time_and_memory();
    require_once $CFG->dirroot . '/' . $CFG->admin . '/registration/lib.php';
    $registrationmanager = new registration_manager();
    $registrationmanager->cron();
    mtrace(get_string('siteupdatesend', 'hub'));
    // If enabled, fetch information about available updates and eventually notify site admins
    if (empty($CFG->disableupdatenotifications)) {
        $updateschecker = \core\update\checker::instance();
        $updateschecker->cron();
    }
    //cleanup old session linked tokens
    //deletes the session linked tokens that are over a day old.
    mtrace("Deleting session linked tokens more than one day old...", '');
    cron_trace_time_and_memory();
    $DB->delete_records_select('external_tokens', 'lastaccess < :onedayago AND tokentype = :tokentype', array('onedayago' => time() - DAYSECS, 'tokentype' => EXTERNAL_TOKEN_EMBEDDED));
    mtrace('done.');
    // all other plugins
    cron_execute_plugin_type('message', 'message plugins');
    cron_execute_plugin_type('filter', 'filters');
    cron_execute_plugin_type('editor', 'editors');
    cron_execute_plugin_type('format', 'course formats');
    cron_execute_plugin_type('profilefield', 'profile fields');
    cron_execute_plugin_type('webservice', 'webservices');
    cron_execute_plugin_type('repository', 'repository plugins');
    cron_execute_plugin_type('qbehaviour', 'question behaviours');
    cron_execute_plugin_type('qformat', 'question import/export formats');
    cron_execute_plugin_type('qtype', 'question types');
    cron_execute_plugin_type('plagiarism', 'plagiarism plugins');
    cron_execute_plugin_type('theme', 'themes');
    cron_execute_plugin_type('tool', 'admin tools');
    // and finally run any local cronjobs, if any
    if ($locals = core_component::get_plugin_list('local')) {
        mtrace('Processing customized cron scripts ...', '');
        // new cron functions in lib.php first
        cron_execute_plugin_type('local');
        // legacy cron files are executed directly
        foreach ($locals as $local => $localdir) {
            if (file_exists("{$localdir}/cron.php")) {
                include "{$localdir}/cron.php";
            }
        }
        mtrace('done.');
    }
    mtrace('Running cache cron routines');
    cache_helper::cron();
    mtrace('done.');
    // Run automated backups if required - these may take a long time to execute
    require_once $CFG->dirroot . '/backup/util/includes/backup_includes.php';
    require_once $CFG->dirroot . '/backup/util/helper/backup_cron_helper.class.php';
    backup_cron_automated_helper::run_automated_backup();
    // Run stats as at the end because they are known to take very long time on large sites
    if (!empty($CFG->enablestats) and empty($CFG->disablestatsprocessing)) {
        require_once $CFG->dirroot . '/lib/statslib.php';
        // check we're not before our runtime
        $timetocheck = stats_get_base_daily() + $CFG->statsruntimestarthour * 60 * 60 + $CFG->statsruntimestartminute * 60;
        if (time() > $timetocheck) {
            // process configured number of days as max (defaulting to 31)
            $maxdays = empty($CFG->statsruntimedays) ? 31 : abs($CFG->statsruntimedays);
            if (stats_cron_daily($maxdays)) {
                if (stats_cron_weekly()) {
                    if (stats_cron_monthly()) {
                        stats_clean_old();
                    }
                }
            }
            @set_time_limit(0);
        } else {
            mtrace('Next stats run after:' . userdate($timetocheck));
        }
    }
    // Run badges review cron.
    mtrace("Starting badges cron...");
    require_once $CFG->dirroot . '/badges/cron.php';
    badge_cron();
    mtrace('done.');
    // cleanup file trash - not very important
    $fs = get_file_storage();
    $fs->cron();
    mtrace("Cron script completed correctly");
    gc_collect_cycles();
    mtrace('Cron completed at ' . date('H:i:s') . '. Memory used ' . display_size(memory_get_usage()) . '.');
    $difftime = microtime_diff($starttime, microtime());
    mtrace("Execution took " . $difftime . " seconds");
}
Exemple #8
0
/**
 * Function to be run periodically according to the moodle cron
 *
 * Finds all assignment notifications that have yet to be mailed out, and mails them
 */
function assignment_cron () {
    global $CFG, $USER, $DB;

    /// first execute all crons in plugins
    if ($plugins = get_plugin_list('assignment')) {
        foreach ($plugins as $plugin=>$dir) {
            require_once("$dir/assignment.class.php");
            $assignmentclass = "assignment_$plugin";
            $ass = new $assignmentclass();
            $ass->cron();
        }
    }

    /// Notices older than 1 day will not be mailed.  This is to avoid the problem where
    /// cron has not been running for a long time, and then suddenly people are flooded
    /// with mail from the past few weeks or months

    $timenow   = time();
    $endtime   = $timenow - $CFG->maxeditingtime;
    $starttime = $endtime - 24 * 3600;   /// One day earlier

    if ($submissions = assignment_get_unmailed_submissions($starttime, $endtime)) {

        $realuser = clone($USER);

        foreach ($submissions as $key => $submission) {
            $DB->set_field("assignment_submissions", "mailed", "1", array("id"=>$submission->id));
        }

        $timenow = time();

        foreach ($submissions as $submission) {

            echo "Processing assignment submission $submission->id\n";

            if (! $user = $DB->get_record("user", array("id"=>$submission->userid))) {
                echo "Could not find user $user->id\n";
                continue;
            }

            if (! $course = $DB->get_record("course", array("id"=>$submission->course))) {
                echo "Could not find course $submission->course\n";
                continue;
            }

            /// Override the language and timezone of the "current" user, so that
            /// mail is customised for the receiver.
            cron_setup_user($user, $course);

            if (!is_enrolled(get_context_instance(CONTEXT_COURSE, $submission->course), $user->id)) {
                echo fullname($user)." not an active participant in " . format_string($course->shortname) . "\n";
                continue;
            }

            if (! $teacher = $DB->get_record("user", array("id"=>$submission->teacher))) {
                echo "Could not find teacher $submission->teacher\n";
                continue;
            }

            if (! $mod = get_coursemodule_from_instance("assignment", $submission->assignment, $course->id)) {
                echo "Could not find course module for assignment id $submission->assignment\n";
                continue;
            }

            if (! $mod->visible) {    /// Hold mail notification for hidden assignments until later
                continue;
            }

            $strassignments = get_string("modulenameplural", "assignment");
            $strassignment  = get_string("modulename", "assignment");

            $assignmentinfo = new stdClass();
            $assignmentinfo->teacher = fullname($teacher);
            $assignmentinfo->assignment = format_string($submission->name,true);
            $assignmentinfo->url = "$CFG->wwwroot/mod/assignment/view.php?id=$mod->id";

            $postsubject = "$course->shortname: $strassignments: ".format_string($submission->name,true);
            $posttext  = "$course->shortname -> $strassignments -> ".format_string($submission->name,true)."\n";
            $posttext .= "---------------------------------------------------------------------\n";
            $posttext .= get_string("assignmentmail", "assignment", $assignmentinfo)."\n";
            $posttext .= "---------------------------------------------------------------------\n";

            if ($user->mailformat == 1) {  // HTML
                $posthtml = "<p><font face=\"sans-serif\">".
                "<a href=\"$CFG->wwwroot/course/view.php?id=$course->id\">$course->shortname</a> ->".
                "<a href=\"$CFG->wwwroot/mod/assignment/index.php?id=$course->id\">$strassignments</a> ->".
                "<a href=\"$CFG->wwwroot/mod/assignment/view.php?id=$mod->id\">".format_string($submission->name,true)."</a></font></p>";
                $posthtml .= "<hr /><font face=\"sans-serif\">";
                $posthtml .= "<p>".get_string("assignmentmailhtml", "assignment", $assignmentinfo)."</p>";
                $posthtml .= "</font><hr />";
            } else {
                $posthtml = "";
            }

            $eventdata = new stdClass();
            $eventdata->modulename       = 'assignment';
            $eventdata->userfrom         = $teacher;
            $eventdata->userto           = $user;
            $eventdata->subject          = $postsubject;
            $eventdata->fullmessage      = $posttext;
            $eventdata->fullmessageformat = FORMAT_PLAIN;
            $eventdata->fullmessagehtml  = $posthtml;
            $eventdata->smallmessage     = '';

            $eventdata->name            = 'assignment_updates';
            $eventdata->component       = 'mod_assignment';
            $eventdata->notification    = 1;

            message_send($eventdata);
        }

        cron_setup_user();
    }

    return true;
}
Exemple #9
0
 /**
  * Finds all assignment notifications that have yet to be mailed out, and mails them.
  *
  * Cron function to be run periodically according to the moodle cron.
  *
  * @return bool
  */
 public static function cron()
 {
     global $DB;
     // Only ever send a max of one days worth of updates.
     $yesterday = time() - 24 * 3600;
     $timenow = time();
     $lastcron = $DB->get_field('modules', 'lastcron', array('name' => 'assign'));
     // Collect all submissions that require mailing.
     // Submissions are included if all are true:
     //   - The assignment is visible in the gradebook.
     //   - No previous notification has been sent.
     //   - If marking workflow is not enabled, the grade was updated in the past 24 hours, or
     //     if marking workflow is enabled, the workflow state is at 'released'.
     $sql = "SELECT g.id as gradeid, a.course, a.name, a.blindmarking, a.revealidentities,\n                       g.*, g.timemodified as lastmodified, cm.id as cmid\n                 FROM {assign} a\n                 JOIN {assign_grades} g ON g.assignment = a.id\n            LEFT JOIN {assign_user_flags} uf ON uf.assignment = a.id AND uf.userid = g.userid\n                 JOIN {course_modules} cm ON cm.course = a.course AND cm.instance = a.id\n                 JOIN {modules} md ON md.id = cm.module AND md.name = 'assign'\n                 JOIN {grade_items} gri ON gri.iteminstance = a.id AND gri.courseid = a.course AND gri.itemmodule = md.name\n                 WHERE ((a.markingworkflow = 0 AND g.timemodified >= :yesterday AND g.timemodified <= :today) OR\n                        (a.markingworkflow = 1 AND uf.workflowstate = :wfreleased)) AND\n                       uf.mailed = 0 AND gri.hidden = 0\n              ORDER BY a.course, cm.id";
     $params = array('yesterday' => $yesterday, 'today' => $timenow, 'wfreleased' => ASSIGN_MARKING_WORKFLOW_STATE_RELEASED);
     $submissions = $DB->get_records_sql($sql, $params);
     if (!empty($submissions)) {
         mtrace('Processing ' . count($submissions) . ' assignment submissions ...');
         // Preload courses we are going to need those.
         $courseids = array();
         foreach ($submissions as $submission) {
             $courseids[] = $submission->course;
         }
         // Filter out duplicates.
         $courseids = array_unique($courseids);
         $ctxselect = context_helper::get_preload_record_columns_sql('ctx');
         list($courseidsql, $params) = $DB->get_in_or_equal($courseids, SQL_PARAMS_NAMED);
         $sql = 'SELECT c.*, ' . $ctxselect . ' FROM {course} c
              LEFT JOIN {context} ctx ON ctx.instanceid = c.id AND ctx.contextlevel = :contextlevel
                  WHERE c.id ' . $courseidsql;
         $params['contextlevel'] = CONTEXT_COURSE;
         $courses = $DB->get_records_sql($sql, $params);
         // Clean up... this could go on for a while.
         unset($courseids);
         unset($ctxselect);
         unset($courseidsql);
         unset($params);
         // Message students about new feedback.
         foreach ($submissions as $submission) {
             mtrace("Processing assignment submission {$submission->id} ...");
             // Do not cache user lookups - could be too many.
             if (!($user = $DB->get_record('user', array('id' => $submission->userid)))) {
                 mtrace('Could not find user ' . $submission->userid);
                 continue;
             }
             // Use a cache to prevent the same DB queries happening over and over.
             if (!array_key_exists($submission->course, $courses)) {
                 mtrace('Could not find course ' . $submission->course);
                 continue;
             }
             $course = $courses[$submission->course];
             if (isset($course->ctxid)) {
                 // Context has not yet been preloaded. Do so now.
                 context_helper::preload_from_record($course);
             }
             // Override the language and timezone of the "current" user, so that
             // mail is customised for the receiver.
             cron_setup_user($user, $course);
             // Context lookups are already cached.
             $coursecontext = context_course::instance($course->id);
             if (!is_enrolled($coursecontext, $user->id)) {
                 $courseshortname = format_string($course->shortname, true, array('context' => $coursecontext));
                 mtrace(fullname($user) . ' not an active participant in ' . $courseshortname);
                 continue;
             }
             if (!($grader = $DB->get_record('user', array('id' => $submission->grader)))) {
                 mtrace('Could not find grader ' . $submission->grader);
                 continue;
             }
             $modinfo = get_fast_modinfo($course, $user->id);
             $cm = $modinfo->get_cm($submission->cmid);
             // Context lookups are already cached.
             $contextmodule = context_module::instance($cm->id);
             if (!$cm->uservisible) {
                 // Hold mail notification for assignments the user cannot access until later.
                 continue;
             }
             // Need to send this to the student.
             $messagetype = 'feedbackavailable';
             $eventtype = 'assign_notification';
             $updatetime = $submission->lastmodified;
             $modulename = get_string('modulename', 'assign');
             $uniqueid = 0;
             if ($submission->blindmarking && !$submission->revealidentities) {
                 $uniqueid = self::get_uniqueid_for_user_static($submission->assignment, $user->id);
             }
             $showusers = $submission->blindmarking && !$submission->revealidentities;
             self::send_assignment_notification($grader, $user, $messagetype, $eventtype, $updatetime, $cm, $contextmodule, $course, $modulename, $submission->name, $showusers, $uniqueid);
             $flags = $DB->get_record('assign_user_flags', array('userid' => $user->id, 'assignment' => $submission->assignment));
             if ($flags) {
                 $flags->mailed = 1;
                 $DB->update_record('assign_user_flags', $flags);
             } else {
                 $flags = new stdClass();
                 $flags->userid = $user->id;
                 $flags->assignment = $submission->assignment;
                 $flags->mailed = 1;
                 $DB->insert_record('assign_user_flags', $flags);
             }
             mtrace('Done');
         }
         mtrace('Done processing ' . count($submissions) . ' assignment submissions');
         cron_setup_user();
         // Free up memory just to be sure.
         unset($courses);
     }
     // Update calendar events to provide a description.
     $sql = 'SELECT id
                 FROM {assign}
                 WHERE
                     allowsubmissionsfromdate >= :lastcron AND
                     allowsubmissionsfromdate <= :timenow AND
                     alwaysshowdescription = 0';
     $params = array('lastcron' => $lastcron, 'timenow' => $timenow);
     $newlyavailable = $DB->get_records_sql($sql, $params);
     foreach ($newlyavailable as $record) {
         $cm = get_coursemodule_from_instance('assign', $record->id, 0, false, MUST_EXIST);
         $context = context_module::instance($cm->id);
         $assignment = new assign($context, null, null);
         $assignment->update_calendar($cm->id);
     }
     return true;
 }
Exemple #10
0
 /**
 * Notify users about enrolments that are going to expire soon!
 * This function is run by admin/cron.php
 * @return void
 */
 function cron()
 {
     global $CFG, $USER, $SITE, $DB;
     if (!isset($CFG->lastexpirynotify)) {
         set_config('lastexpirynotify', 0);
     }
     // notify once a day only - TODO: add some tz handling here, maybe use timestamps
     if ($CFG->lastexpirynotify == date('Ymd')) {
         return;
     }
     if ($rs = $DB->get_recordset_select('course', 'enrolperiod > 0 AND expirynotify > 0 AND expirythreshold > 0')) {
         $admin = get_admin();
         foreach ($rs as $course) {
             $a = new object();
             $a->coursename = $course->shortname . '/' . $course->fullname;
             // must be processed by format_string later
             $a->threshold = $course->expirythreshold / 86400;
             $a->extendurl = $CFG->wwwroot . '/user/index.php?id=' . $course->id;
             $a->current = array();
             $a->past = array();
             $expiry = time() + $course->expirythreshold;
             $cname = $course->fullname;
             /// Get all the manual role assignments for this course that have expired.
             if (!($context = get_context_instance(CONTEXT_COURSE, $course->id))) {
                 continue;
             }
             if ($oldenrolments = $DB->get_records_sql("\n                      SELECT u.*, ra.timeend\n                        FROM {user} u\n                             JOIN {role_assignments} ra ON (ra.userid = u.id)\n                        WHERE ra.contextid = {$context->id}\n                              AND ra.timeend > 0 AND ra.timeend <= {$expiry}\n                              AND ra.enrol = 'manual'")) {
                 // inform user who can assign roles or admin
                 if ($teachers = get_users_by_capability($context, 'moodle/role:assign', '', '', '', '', '', '', false)) {
                     $teachers = sort_by_roleassignment_authority($teachers, $context);
                     $teacher = reset($teachers);
                 } else {
                     $teachers = array($admin);
                     $teacher = $admin;
                 }
                 $a->teacherstr = fullname($teacher, true);
                 foreach ($oldenrolments as $user) {
                     /// Email all users about to expire
                     $a->studentstr = fullname($user, true);
                     if ($user->timeend < $expiry - 86400) {
                         $a->past[] = fullname($user) . " <{$user->email}>";
                     } else {
                         $a->current[] = fullname($user) . " <{$user->email}>";
                         if ($course->notifystudents) {
                             // Send this guy notice
                             // setup global $COURSE properly - needed for languages
                             cron_setup_user($user, $course);
                             $a->coursename = format_string($cname);
                             $a->course = $a->coursename;
                             $strexpirynotifystudentsemail = get_string('expirynotifystudentsemail', '', $a);
                             $strexpirynotify = get_string('expirynotify');
                             $eventdata = new object();
                             $eventdata->modulename = 'moodle';
                             $eventdata->userfrom = $teacher;
                             $eventdata->userto = $user;
                             $eventdata->subject = format_string($SITE->fullname) . ' ' . $strexpirynotify;
                             $eventdata->fullmessage = $strexpirynotifystudentsemail;
                             $eventdata->fullmessageformat = FORMAT_PLAIN;
                             $eventdata->fullmessagehtml = '';
                             $eventdata->smallmessage = '';
                             events_trigger('message_send', $eventdata);
                         }
                     }
                 }
                 $a->current = implode("\n", $a->current);
                 $a->past = implode("\n", $a->past);
                 if ($a->current || $a->past) {
                     foreach ($teachers as $teacher) {
                         // setup global $COURSE properly - needed for languages
                         cron_setup_user($teacher, $course);
                         $a->coursename = format_string($cname);
                         $strexpirynotifyemail = get_string('expirynotifyemail', '', $a);
                         $strexpirynotify = get_string('expirynotify');
                         $eventdata = new object();
                         $eventdata->modulename = 'moodle';
                         $eventdata->userfrom = $admin;
                         $eventdata->userto = $teacher;
                         $eventdata->subject = $a->coursename . ' ' . $strexpirynotify;
                         $eventdata->fullmessage = $strexpirynotifyemail;
                         $eventdata->fullmessageformat = FORMAT_PLAIN;
                         $eventdata->fullmessagehtml = '';
                         $eventdata->smallmessage = '';
                         events_trigger('message_send', $eventdata);
                     }
                 }
             }
         }
         $rs->close();
         cron_setup_user();
     }
     set_config('lastexpirynotify', date('Ymd'));
 }
Exemple #11
0
/**
 * Execute cron tasks
 */
function cron_run()
{
    global $DB, $CFG, $OUTPUT;
    if (CLI_MAINTENANCE) {
        echo "CLI maintenance mode active, cron execution suspended.\n";
        exit(1);
    }
    if (moodle_needs_upgrading()) {
        echo "Moodle upgrade pending, cron execution suspended.\n";
        exit(1);
    }
    require_once $CFG->libdir . '/adminlib.php';
    if (!empty($CFG->showcronsql)) {
        $DB->set_debug(true);
    }
    if (!empty($CFG->showcrondebugging)) {
        set_debugging(DEBUG_DEVELOPER, true);
    }
    core_php_time_limit::raise();
    $starttime = microtime();
    // Increase memory limit
    raise_memory_limit(MEMORY_EXTRA);
    // Emulate normal session - we use admin accoutn by default
    cron_setup_user();
    // Start output log
    $timenow = time();
    mtrace("Server Time: " . date('r', $timenow) . "\n\n");
    // Run all scheduled tasks.
    while (!\core\task\manager::static_caches_cleared_since($timenow) && ($task = \core\task\manager::get_next_scheduled_task($timenow))) {
        mtrace("Execute scheduled task: " . $task->get_name());
        cron_trace_time_and_memory();
        $predbqueries = null;
        $predbqueries = $DB->perf_get_queries();
        $pretime = microtime(1);
        try {
            get_mailer('buffer');
            $task->execute();
            if ($DB->is_transaction_started()) {
                throw new coding_exception("Task left transaction open");
            }
            if (isset($predbqueries)) {
                mtrace("... used " . ($DB->perf_get_queries() - $predbqueries) . " dbqueries");
                mtrace("... used " . (microtime(1) - $pretime) . " seconds");
            }
            mtrace("Scheduled task complete: " . $task->get_name());
            \core\task\manager::scheduled_task_complete($task);
        } catch (Exception $e) {
            if ($DB && $DB->is_transaction_started()) {
                error_log('Database transaction aborted automatically in ' . get_class($task));
                $DB->force_transaction_rollback();
            }
            if (isset($predbqueries)) {
                mtrace("... used " . ($DB->perf_get_queries() - $predbqueries) . " dbqueries");
                mtrace("... used " . (microtime(1) - $pretime) . " seconds");
            }
            mtrace("Scheduled task failed: " . $task->get_name() . "," . $e->getMessage());
            if ($CFG->debugdeveloper) {
                if (!empty($e->debuginfo)) {
                    mtrace("Debug info:");
                    mtrace($e->debuginfo);
                }
                mtrace("Backtrace:");
                mtrace(format_backtrace($e->getTrace(), true));
            }
            \core\task\manager::scheduled_task_failed($task);
        }
        get_mailer('close');
        unset($task);
    }
    // Run all adhoc tasks.
    while (!\core\task\manager::static_caches_cleared_since($timenow) && ($task = \core\task\manager::get_next_adhoc_task($timenow))) {
        mtrace("Execute adhoc task: " . get_class($task));
        cron_trace_time_and_memory();
        $predbqueries = null;
        $predbqueries = $DB->perf_get_queries();
        $pretime = microtime(1);
        try {
            get_mailer('buffer');
            $task->execute();
            if ($DB->is_transaction_started()) {
                throw new coding_exception("Task left transaction open");
            }
            if (isset($predbqueries)) {
                mtrace("... used " . ($DB->perf_get_queries() - $predbqueries) . " dbqueries");
                mtrace("... used " . (microtime(1) - $pretime) . " seconds");
            }
            mtrace("Adhoc task complete: " . get_class($task));
            \core\task\manager::adhoc_task_complete($task);
        } catch (Exception $e) {
            if ($DB && $DB->is_transaction_started()) {
                error_log('Database transaction aborted automatically in ' . get_class($task));
                $DB->force_transaction_rollback();
            }
            if (isset($predbqueries)) {
                mtrace("... used " . ($DB->perf_get_queries() - $predbqueries) . " dbqueries");
                mtrace("... used " . (microtime(1) - $pretime) . " seconds");
            }
            mtrace("Adhoc task failed: " . get_class($task) . "," . $e->getMessage());
            if ($CFG->debugdeveloper) {
                if (!empty($e->debuginfo)) {
                    mtrace("Debug info:");
                    mtrace($e->debuginfo);
                }
                mtrace("Backtrace:");
                mtrace(format_backtrace($e->getTrace(), true));
            }
            \core\task\manager::adhoc_task_failed($task);
        }
        get_mailer('close');
        unset($task);
    }
    mtrace("Cron script completed correctly");
    gc_collect_cycles();
    mtrace('Cron completed at ' . date('H:i:s') . '. Memory used ' . display_size(memory_get_usage()) . '.');
    $difftime = microtime_diff($starttime, microtime());
    mtrace("Execution took " . $difftime . " seconds");
}
Exemple #12
0
 /**
  * Runs all ad-hoc tasks in the queue.
  *
  * This is faster and more reliable than running cron (running cron won't
  * work more than once in the same test, for instance). However it is
  * a little less 'realistic'.
  *
  * While the task is running, we suppress mtrace output because it makes
  * the Behat result look ugly.
  *
  * @Given /^I run all adhoc tasks$/
  * @throws DriverException
  */
 public function i_run_all_adhoc_tasks()
 {
     // Do setup for cron task.
     cron_setup_user();
     // Run tasks. Locking is handled by get_next_adhoc_task.
     $now = time();
     ob_start();
     // Discard task output as not appropriate for Behat output!
     while (($task = \core\task\manager::get_next_adhoc_task($now)) !== null) {
         try {
             $task->execute();
             // Mark task complete.
             \core\task\manager::adhoc_task_complete($task);
         } catch (Exception $e) {
             // Mark task failed and throw exception.
             \core\task\manager::adhoc_task_failed($task);
             ob_end_clean();
             throw new DriverException('An adhoc task failed', 0, $e);
         }
     }
     ob_end_clean();
 }
 public function test_cron_setup_user()
 {
     global $PAGE, $USER, $SESSION, $SITE, $CFG;
     $this->resetAfterTest();
     // NOTE: this function contains some static caches, let's reset first.
     cron_setup_user('reset');
     $admin = get_admin();
     $user1 = $this->getDataGenerator()->create_user();
     $user2 = $this->getDataGenerator()->create_user();
     $course = $this->getDataGenerator()->create_course();
     cron_setup_user();
     $this->assertSame($admin->id, $USER->id);
     $this->assertSame($PAGE->context, context_course::instance($SITE->id));
     $this->assertSame($CFG->timezone, $USER->timezone);
     $this->assertSame('', $USER->lang);
     $this->assertSame('', $USER->theme);
     $SESSION->test1 = true;
     $adminsession = $SESSION;
     $adminuser = $USER;
     $this->assertSame($GLOBALS['SESSION'], $_SESSION['SESSION']);
     $this->assertSame($GLOBALS['SESSION'], $SESSION);
     $this->assertSame($GLOBALS['USER'], $_SESSION['USER']);
     $this->assertSame($GLOBALS['USER'], $USER);
     cron_setup_user(null, $course);
     $this->assertSame($admin->id, $USER->id);
     $this->assertSame($PAGE->context, context_course::instance($course->id));
     $this->assertSame($adminsession, $SESSION);
     $this->assertSame($GLOBALS['SESSION'], $_SESSION['SESSION']);
     $this->assertSame($GLOBALS['SESSION'], $SESSION);
     $this->assertSame($GLOBALS['USER'], $_SESSION['USER']);
     $this->assertSame($GLOBALS['USER'], $USER);
     cron_setup_user($user1);
     $this->assertSame($user1->id, $USER->id);
     $this->assertSame($PAGE->context, context_course::instance($SITE->id));
     $this->assertNotSame($adminsession, $SESSION);
     $this->assertObjectNotHasAttribute('test1', $SESSION);
     $this->assertEmpty((array) $SESSION);
     $usersession1 = $SESSION;
     $SESSION->test2 = true;
     $this->assertSame($GLOBALS['SESSION'], $_SESSION['SESSION']);
     $this->assertSame($GLOBALS['SESSION'], $SESSION);
     $this->assertSame($GLOBALS['USER'], $_SESSION['USER']);
     $this->assertSame($GLOBALS['USER'], $USER);
     cron_setup_user($user1);
     $this->assertSame($user1->id, $USER->id);
     $this->assertSame($PAGE->context, context_course::instance($SITE->id));
     $this->assertNotSame($adminsession, $SESSION);
     $this->assertSame($usersession1, $SESSION);
     $this->assertSame($GLOBALS['SESSION'], $_SESSION['SESSION']);
     $this->assertSame($GLOBALS['SESSION'], $SESSION);
     $this->assertSame($GLOBALS['USER'], $_SESSION['USER']);
     $this->assertSame($GLOBALS['USER'], $USER);
     cron_setup_user($user2);
     $this->assertSame($user2->id, $USER->id);
     $this->assertSame($PAGE->context, context_course::instance($SITE->id));
     $this->assertNotSame($adminsession, $SESSION);
     $this->assertNotSame($usersession1, $SESSION);
     $this->assertEmpty((array) $SESSION);
     $usersession2 = $SESSION;
     $usersession2->test3 = true;
     $this->assertSame($GLOBALS['SESSION'], $_SESSION['SESSION']);
     $this->assertSame($GLOBALS['SESSION'], $SESSION);
     $this->assertSame($GLOBALS['USER'], $_SESSION['USER']);
     $this->assertSame($GLOBALS['USER'], $USER);
     cron_setup_user($user2, $course);
     $this->assertSame($user2->id, $USER->id);
     $this->assertSame($PAGE->context, context_course::instance($course->id));
     $this->assertNotSame($adminsession, $SESSION);
     $this->assertNotSame($usersession1, $SESSION);
     $this->assertSame($usersession2, $SESSION);
     $this->assertSame($GLOBALS['SESSION'], $_SESSION['SESSION']);
     $this->assertSame($GLOBALS['SESSION'], $SESSION);
     $this->assertSame($GLOBALS['USER'], $_SESSION['USER']);
     $this->assertSame($GLOBALS['USER'], $USER);
     cron_setup_user($user1);
     $this->assertSame($user1->id, $USER->id);
     $this->assertSame($PAGE->context, context_course::instance($SITE->id));
     $this->assertNotSame($adminsession, $SESSION);
     $this->assertNotSame($usersession1, $SESSION);
     $this->assertEmpty((array) $SESSION);
     $this->assertSame($GLOBALS['SESSION'], $_SESSION['SESSION']);
     $this->assertSame($GLOBALS['SESSION'], $SESSION);
     $this->assertSame($GLOBALS['USER'], $_SESSION['USER']);
     $this->assertSame($GLOBALS['USER'], $USER);
     cron_setup_user();
     $this->assertSame($admin->id, $USER->id);
     $this->assertSame($PAGE->context, context_course::instance($SITE->id));
     $this->assertSame($adminsession, $SESSION);
     $this->assertSame($adminuser, $USER);
     $this->assertSame($GLOBALS['SESSION'], $_SESSION['SESSION']);
     $this->assertSame($GLOBALS['SESSION'], $SESSION);
     $this->assertSame($GLOBALS['USER'], $_SESSION['USER']);
     $this->assertSame($GLOBALS['USER'], $USER);
     cron_setup_user('reset');
     $this->assertSame($GLOBALS['SESSION'], $_SESSION['SESSION']);
     $this->assertSame($GLOBALS['SESSION'], $SESSION);
     $this->assertSame($GLOBALS['USER'], $_SESSION['USER']);
     $this->assertSame($GLOBALS['USER'], $USER);
     cron_setup_user();
     $this->assertNotSame($adminsession, $SESSION);
     $this->assertNotSame($adminuser, $USER);
     $this->assertSame($GLOBALS['SESSION'], $_SESSION['SESSION']);
     $this->assertSame($GLOBALS['SESSION'], $SESSION);
     $this->assertSame($GLOBALS['USER'], $_SESSION['USER']);
     $this->assertSame($GLOBALS['USER'], $USER);
 }
 /**
  * Gets called by the adhoc_taskmanager and its task in send_distribution_notification
  * 
  * @param user $userfrom
  */
 public function notify_users_distribution($userfrom)
 {
     global $CFG;
     $userfrom = get_complete_user_data('id', $userfrom);
     // make sure we have not sent them yet
     if ($this->origdbrecord->{this_db\ratingallocate::NOTIFICATIONSEND} != -1) {
         mtrace('seems we have sent them already');
         return true;
     }
     $choices = $this->get_choices_with_allocationcount();
     $allocations = $this->get_allocations();
     foreach ($allocations as $userid => $allocobj) {
         // get the assigned choice_id
         $alloc_choic_id = $allocobj->choiceid;
         // Prepare the email to be sent to the user
         $userto = get_complete_user_data('id', $allocobj->userid);
         cron_setup_user($userto);
         // prepare Text
         $notiftext = $this->make_mail_text($choices[$alloc_choic_id]);
         $notifhtml = $this->make_mail_html($choices[$alloc_choic_id]);
         $notifsubject = format_string($this->course->shortname, true) . ': ' . get_string('allocation_notification_message_subject', 'ratingallocate', $this->ratingallocate->name);
         // Send the post now!
         if (empty($userto->mailformat) || $userto->mailformat != 1) {
             // This user DOESN'T want to receive HTML
             $notifhtml = '';
         }
         $attachment = $attachname = '';
         $mailresult = email_to_user($userto, $userfrom, $notifsubject, $notiftext, $notifhtml, $attachment, $attachname);
         if (!$mailresult) {
             mtrace("ERROR: mod/ratingallocate/locallib.php: Could not send out digest mail to user {$userto->id} " . "({$userto->email})... not trying again.");
         } else {
             mtrace("success.");
         }
     }
     // update the 'notified' flag
     $this->origdbrecord->{this_db\ratingallocate::NOTIFICATIONSEND} = 1;
     $this->ratingallocate = new ratingallocate_db_wrapper($this->origdbrecord);
     $this->db->update_record(this_db\ratingallocate::TABLE, $this->origdbrecord);
 }
 public function test_cron()
 {
     // First run cron so there are no messages waiting to be sent (from other tests).
     cron_setup_user();
     assign::cron();
     // Now create an assignment and add some feedback.
     $this->setUser($this->editingteachers[0]);
     $assign = $this->create_instance(array('sendstudentnotifications' => 1));
     // Simulate adding a grade.
     $this->setUser($this->teachers[0]);
     $data = new stdClass();
     $data->grade = '50.0';
     $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
     $assign->testable_apply_grade_to_user($data, $this->students[1]->id, 0);
     $data->sendstudentnotifications = false;
     $assign->testable_apply_grade_to_user($data, $this->students[2]->id, 0);
     // Now run cron and see that one message was sent.
     $this->preventResetByRollback();
     $sink = $this->redirectMessages();
     cron_setup_user();
     $this->expectOutputRegex('/Done processing 2 assignment submissions/');
     assign::cron();
     $messages = $sink->get_messages();
     // The sent count should be 2, because the 3rd one was marked as do not send notifications.
     $this->assertEquals(2, count($messages));
     $this->assertEquals(1, $messages[0]->notification);
     $this->assertEquals($assign->get_instance()->name, $messages[0]->contexturlname);
 }
/**
 * Duplicates a Moodle module in an existing course
 * @param  int $cmid     Course module id
 * @param  int $courseid Course id
 * @return int           New course module id
 */
function local_ltiprovider_duplicate_module($cmid, $courseid, $newidnumber, $lticontext)
{
    global $CFG, $DB, $USER;
    require_once $CFG->dirroot . '/backup/util/includes/backup_includes.php';
    require_once $CFG->dirroot . '/backup/util/includes/restore_includes.php';
    require_once $CFG->libdir . '/filelib.php';
    if (empty($USER)) {
        // Emulate session.
        cron_setup_user();
    }
    $course = $DB->get_record('course', array('id' => $courseid), '*', MUST_EXIST);
    $cm = get_coursemodule_from_id('', $cmid, 0, true, MUST_EXIST);
    $cmcontext = context_module::instance($cm->id);
    $context = context_course::instance($course->id);
    if (!plugin_supports('mod', $cm->modname, FEATURE_BACKUP_MOODLE2)) {
        $url = course_get_url($course, $cm->sectionnum, array('sr' => $sectionreturn));
        print_error('duplicatenosupport', 'error', $url, $a);
    }
    // backup the activity
    $admin = get_admin();
    $bc = new backup_controller(backup::TYPE_1ACTIVITY, $cm->id, backup::FORMAT_MOODLE, backup::INTERACTIVE_NO, backup::MODE_IMPORT, $admin->id);
    $backupid = $bc->get_backupid();
    $backupbasepath = $bc->get_plan()->get_basepath();
    $bc->execute_plan();
    $bc->destroy();
    // restore the backup immediately
    $rc = new restore_controller($backupid, $courseid, backup::INTERACTIVE_NO, backup::MODE_IMPORT, $admin->id, backup::TARGET_CURRENT_ADDING);
    if (!$rc->execute_precheck()) {
        $precheckresults = $rc->get_precheck_results();
        if (is_array($precheckresults) && !empty($precheckresults['errors'])) {
            if (empty($CFG->keeptempdirectoriesonbackup)) {
                fulldelete($backupbasepath);
            }
            print_r($precheckresults);
            die;
        }
    }
    $rc->execute_plan();
    $newcmid = null;
    $tasks = $rc->get_plan()->get_tasks();
    foreach ($tasks as $task) {
        if (is_subclass_of($task, 'restore_activity_task')) {
            if ($task->get_old_contextid() == $cmcontext->id) {
                $newcmid = $task->get_moduleid();
                break;
            }
        }
    }
    $rc->destroy();
    if ($module = $DB->get_record('course_modules', array('id' => $newcmid))) {
        $module->idnumber = $newidnumber;
        $DB->update_record('course_modules', $module);
    }
    $newtoolid = 0;
    if ($tools = $DB->get_records('local_ltiprovider', array('contextid' => $cmcontext->id))) {
        $newcmcontext = context_module::instance($newcmid);
        foreach ($tools as $tool) {
            $tool->courseid = $course->id;
            $tool->contextid = $newcmcontext->id;
            $newtoolid = $DB->insert_record('local_ltiprovider', $tool);
        }
    }
    if (!$newtoolid) {
        $tool = local_ltiprovider_create_tool($course->id, $newcmcontext->id, $lticontext);
    }
    if (empty($CFG->keeptempdirectoriesonbackup)) {
        fulldelete($backupbasepath);
    }
    return $newcmid;
}
Exemple #17
0
/**
 * Function to be run periodically according to the scheduled task.
 *
 * Finds all posts that have yet to be mailed out, and mails them
 * out to all subscribers as well as other maintance tasks.
 *
 * NOTE: Since 2.7.2 this function is run by scheduled task rather
 * than standard cron.
 *
 * @todo MDL-44734 The function will be split up into seperate tasks.
 */
function forum_cron()
{
    global $CFG, $USER, $DB, $PAGE;
    $site = get_site();
    // The main renderers.
    $htmlout = $PAGE->get_renderer('mod_forum', 'email', 'htmlemail');
    $textout = $PAGE->get_renderer('mod_forum', 'email', 'textemail');
    $htmldigestfullout = $PAGE->get_renderer('mod_forum', 'emaildigestfull', 'htmlemail');
    $textdigestfullout = $PAGE->get_renderer('mod_forum', 'emaildigestfull', 'textemail');
    $htmldigestbasicout = $PAGE->get_renderer('mod_forum', 'emaildigestbasic', 'htmlemail');
    $textdigestbasicout = $PAGE->get_renderer('mod_forum', 'emaildigestbasic', 'textemail');
    // All users that are subscribed to any post that needs sending,
    // please increase $CFG->extramemorylimit on large sites that
    // send notifications to a large number of users.
    $users = array();
    $userscount = 0;
    // Cached user counter - count($users) in PHP is horribly slow!!!
    // Status arrays.
    $mailcount = array();
    $errorcount = array();
    // caches
    $discussions = array();
    $forums = array();
    $courses = array();
    $coursemodules = array();
    $subscribedusers = array();
    $messageinboundhandlers = array();
    // Posts older than 2 days will not be mailed.  This is to avoid the problem where
    // cron has not been running for a long time, and then suddenly people are flooded
    // with mail from the past few weeks or months
    $timenow = time();
    $endtime = $timenow - $CFG->maxeditingtime;
    $starttime = $endtime - 48 * 3600;
    // Two days earlier
    // Get the list of forum subscriptions for per-user per-forum maildigest settings.
    $digestsset = $DB->get_recordset('forum_digests', null, '', 'id, userid, forum, maildigest');
    $digests = array();
    foreach ($digestsset as $thisrow) {
        if (!isset($digests[$thisrow->forum])) {
            $digests[$thisrow->forum] = array();
        }
        $digests[$thisrow->forum][$thisrow->userid] = $thisrow->maildigest;
    }
    $digestsset->close();
    // Create the generic messageinboundgenerator.
    $messageinboundgenerator = new \core\message\inbound\address_manager();
    $messageinboundgenerator->set_handler('\\mod_forum\\message\\inbound\\reply_handler');
    if ($posts = forum_get_unmailed_posts($starttime, $endtime, $timenow)) {
        // Mark them all now as being mailed.  It's unlikely but possible there
        // might be an error later so that a post is NOT actually mailed out,
        // but since mail isn't crucial, we can accept this risk.  Doing it now
        // prevents the risk of duplicated mails, which is a worse problem.
        if (!forum_mark_old_posts_as_mailed($endtime)) {
            mtrace('Errors occurred while trying to mark some posts as being mailed.');
            return false;
            // Don't continue trying to mail them, in case we are in a cron loop
        }
        // checking post validity, and adding users to loop through later
        foreach ($posts as $pid => $post) {
            $discussionid = $post->discussion;
            if (!isset($discussions[$discussionid])) {
                if ($discussion = $DB->get_record('forum_discussions', array('id' => $post->discussion))) {
                    $discussions[$discussionid] = $discussion;
                    \mod_forum\subscriptions::fill_subscription_cache($discussion->forum);
                    \mod_forum\subscriptions::fill_discussion_subscription_cache($discussion->forum);
                } else {
                    mtrace('Could not find discussion ' . $discussionid);
                    unset($posts[$pid]);
                    continue;
                }
            }
            $forumid = $discussions[$discussionid]->forum;
            if (!isset($forums[$forumid])) {
                if ($forum = $DB->get_record('forum', array('id' => $forumid))) {
                    $forums[$forumid] = $forum;
                } else {
                    mtrace('Could not find forum ' . $forumid);
                    unset($posts[$pid]);
                    continue;
                }
            }
            $courseid = $forums[$forumid]->course;
            if (!isset($courses[$courseid])) {
                if ($course = $DB->get_record('course', array('id' => $courseid))) {
                    $courses[$courseid] = $course;
                } else {
                    mtrace('Could not find course ' . $courseid);
                    unset($posts[$pid]);
                    continue;
                }
            }
            if (!isset($coursemodules[$forumid])) {
                if ($cm = get_coursemodule_from_instance('forum', $forumid, $courseid)) {
                    $coursemodules[$forumid] = $cm;
                } else {
                    mtrace('Could not find course module for forum ' . $forumid);
                    unset($posts[$pid]);
                    continue;
                }
            }
            // Save the Inbound Message datakey here to reduce DB queries later.
            $messageinboundgenerator->set_data($pid);
            $messageinboundhandlers[$pid] = $messageinboundgenerator->fetch_data_key();
            // Caching subscribed users of each forum.
            if (!isset($subscribedusers[$forumid])) {
                $modcontext = context_module::instance($coursemodules[$forumid]->id);
                if ($subusers = \mod_forum\subscriptions::fetch_subscribed_users($forums[$forumid], 0, $modcontext, 'u.*', true)) {
                    foreach ($subusers as $postuser) {
                        // this user is subscribed to this forum
                        $subscribedusers[$forumid][$postuser->id] = $postuser->id;
                        $userscount++;
                        if ($userscount > FORUM_CRON_USER_CACHE) {
                            // Store minimal user info.
                            $minuser = new stdClass();
                            $minuser->id = $postuser->id;
                            $users[$postuser->id] = $minuser;
                        } else {
                            // Cache full user record.
                            forum_cron_minimise_user_record($postuser);
                            $users[$postuser->id] = $postuser;
                        }
                    }
                    // Release memory.
                    unset($subusers);
                    unset($postuser);
                }
            }
            $mailcount[$pid] = 0;
            $errorcount[$pid] = 0;
        }
    }
    if ($users && $posts) {
        foreach ($users as $userto) {
            // Terminate if processing of any account takes longer than 2 minutes.
            core_php_time_limit::raise(120);
            mtrace('Processing user ' . $userto->id);
            // Init user caches - we keep the cache for one cycle only, otherwise it could consume too much memory.
            if (isset($userto->username)) {
                $userto = clone $userto;
            } else {
                $userto = $DB->get_record('user', array('id' => $userto->id));
                forum_cron_minimise_user_record($userto);
            }
            $userto->viewfullnames = array();
            $userto->canpost = array();
            $userto->markposts = array();
            // Setup this user so that the capabilities are cached, and environment matches receiving user.
            cron_setup_user($userto);
            // Reset the caches.
            foreach ($coursemodules as $forumid => $unused) {
                $coursemodules[$forumid]->cache = new stdClass();
                $coursemodules[$forumid]->cache->caps = array();
                unset($coursemodules[$forumid]->uservisible);
            }
            foreach ($posts as $pid => $post) {
                $discussion = $discussions[$post->discussion];
                $forum = $forums[$discussion->forum];
                $course = $courses[$forum->course];
                $cm =& $coursemodules[$forum->id];
                // Do some checks to see if we can bail out now.
                // Only active enrolled users are in the list of subscribers.
                // This does not necessarily mean that the user is subscribed to the forum or to the discussion though.
                if (!isset($subscribedusers[$forum->id][$userto->id])) {
                    // The user does not subscribe to this forum.
                    continue;
                }
                if (!\mod_forum\subscriptions::is_subscribed($userto->id, $forum, $post->discussion, $coursemodules[$forum->id])) {
                    // The user does not subscribe to this forum, or to this specific discussion.
                    continue;
                }
                if ($subscriptiontime = \mod_forum\subscriptions::fetch_discussion_subscription($forum->id, $userto->id)) {
                    // Skip posts if the user subscribed to the discussion after it was created.
                    if (isset($subscriptiontime[$post->discussion]) && $subscriptiontime[$post->discussion] > $post->created) {
                        continue;
                    }
                }
                // Don't send email if the forum is Q&A and the user has not posted.
                // Initial topics are still mailed.
                if ($forum->type == 'qanda' && !forum_get_user_posted_time($discussion->id, $userto->id) && $pid != $discussion->firstpost) {
                    mtrace('Did not email ' . $userto->id . ' because user has not posted in discussion');
                    continue;
                }
                // Get info about the sending user.
                if (array_key_exists($post->userid, $users)) {
                    // We might know the user already.
                    $userfrom = $users[$post->userid];
                    if (!isset($userfrom->idnumber)) {
                        // Minimalised user info, fetch full record.
                        $userfrom = $DB->get_record('user', array('id' => $userfrom->id));
                        forum_cron_minimise_user_record($userfrom);
                    }
                } else {
                    if ($userfrom = $DB->get_record('user', array('id' => $post->userid))) {
                        forum_cron_minimise_user_record($userfrom);
                        // Fetch only once if possible, we can add it to user list, it will be skipped anyway.
                        if ($userscount <= FORUM_CRON_USER_CACHE) {
                            $userscount++;
                            $users[$userfrom->id] = $userfrom;
                        }
                    } else {
                        mtrace('Could not find user ' . $post->userid . ', author of post ' . $post->id . '. Unable to send message.');
                        continue;
                    }
                }
                // Note: If we want to check that userto and userfrom are not the same person this is probably the spot to do it.
                // Setup global $COURSE properly - needed for roles and languages.
                cron_setup_user($userto, $course);
                // Fill caches.
                if (!isset($userto->viewfullnames[$forum->id])) {
                    $modcontext = context_module::instance($cm->id);
                    $userto->viewfullnames[$forum->id] = has_capability('moodle/site:viewfullnames', $modcontext);
                }
                if (!isset($userto->canpost[$discussion->id])) {
                    $modcontext = context_module::instance($cm->id);
                    $userto->canpost[$discussion->id] = forum_user_can_post($forum, $discussion, $userto, $cm, $course, $modcontext);
                }
                if (!isset($userfrom->groups[$forum->id])) {
                    if (!isset($userfrom->groups)) {
                        $userfrom->groups = array();
                        if (isset($users[$userfrom->id])) {
                            $users[$userfrom->id]->groups = array();
                        }
                    }
                    $userfrom->groups[$forum->id] = groups_get_all_groups($course->id, $userfrom->id, $cm->groupingid);
                    if (isset($users[$userfrom->id])) {
                        $users[$userfrom->id]->groups[$forum->id] = $userfrom->groups[$forum->id];
                    }
                }
                // Make sure groups allow this user to see this email.
                if ($discussion->groupid > 0 and $groupmode = groups_get_activity_groupmode($cm, $course)) {
                    // Groups are being used.
                    if (!groups_group_exists($discussion->groupid)) {
                        // Can't find group - be safe and don't this message.
                        continue;
                    }
                    if (!groups_is_member($discussion->groupid) and !has_capability('moodle/site:accessallgroups', $modcontext)) {
                        // Do not send posts from other groups when in SEPARATEGROUPS or VISIBLEGROUPS.
                        continue;
                    }
                }
                // Make sure we're allowed to see the post.
                if (!forum_user_can_see_post($forum, $discussion, $post, null, $cm)) {
                    mtrace('User ' . $userto->id . ' can not see ' . $post->id . '. Not sending message.');
                    continue;
                }
                // OK so we need to send the email.
                // Does the user want this post in a digest?  If so postpone it for now.
                $maildigest = forum_get_user_maildigest_bulk($digests, $userto, $forum->id);
                if ($maildigest > 0) {
                    // This user wants the mails to be in digest form.
                    $queue = new stdClass();
                    $queue->userid = $userto->id;
                    $queue->discussionid = $discussion->id;
                    $queue->postid = $post->id;
                    $queue->timemodified = $post->created;
                    $DB->insert_record('forum_queue', $queue);
                    continue;
                }
                // Prepare to actually send the post now, and build up the content.
                $cleanforumname = str_replace('"', "'", strip_tags(format_string($forum->name)));
                $userfrom->customheaders = array('List-Id: "' . $cleanforumname . '" ' . generate_email_messageid('moodleforum' . $forum->id), 'List-Help: ' . $CFG->wwwroot . '/mod/forum/view.php?f=' . $forum->id, 'Message-ID: ' . forum_get_email_message_id($post->id, $userto->id), 'X-Course-Id: ' . $course->id, 'X-Course-Name: ' . format_string($course->fullname, true), 'Precedence: Bulk', 'X-Auto-Response-Suppress: All', 'Auto-Submitted: auto-generated');
                $shortname = format_string($course->shortname, true, array('context' => context_course::instance($course->id)));
                // Generate a reply-to address from using the Inbound Message handler.
                $replyaddress = null;
                if ($userto->canpost[$discussion->id] && array_key_exists($post->id, $messageinboundhandlers)) {
                    $messageinboundgenerator->set_data($post->id, $messageinboundhandlers[$post->id]);
                    $replyaddress = $messageinboundgenerator->generate($userto->id);
                }
                if (!isset($userto->canpost[$discussion->id])) {
                    $canreply = forum_user_can_post($forum, $discussion, $userto, $cm, $course, $modcontext);
                } else {
                    $canreply = $userto->canpost[$discussion->id];
                }
                $data = new \mod_forum\output\forum_post_email($course, $cm, $forum, $discussion, $post, $userfrom, $userto, $canreply);
                $userfrom->customheaders[] = sprintf('List-Unsubscribe: <%s>', $data->get_unsubscribediscussionlink());
                if (!isset($userto->viewfullnames[$forum->id])) {
                    $data->viewfullnames = has_capability('moodle/site:viewfullnames', $modcontext, $userto->id);
                } else {
                    $data->viewfullnames = $userto->viewfullnames[$forum->id];
                }
                // Not all of these variables are used in the default language
                // string but are made available to support custom subjects.
                $a = new stdClass();
                $a->subject = $data->get_subject();
                $a->forumname = $cleanforumname;
                $a->sitefullname = format_string($site->fullname);
                $a->siteshortname = format_string($site->shortname);
                $a->courseidnumber = $data->get_courseidnumber();
                $a->coursefullname = $data->get_coursefullname();
                $a->courseshortname = $data->get_coursename();
                $postsubject = html_to_text(get_string('postmailsubject', 'forum', $a), 0);
                $rootid = forum_get_email_message_id($discussion->firstpost, $userto->id);
                if ($post->parent) {
                    // This post is a reply, so add reply header (RFC 2822).
                    $parentid = forum_get_email_message_id($post->parent, $userto->id);
                    $userfrom->customheaders[] = "In-Reply-To: {$parentid}";
                    // If the post is deeply nested we also reference the parent message id and
                    // the root message id (if different) to aid threading when parts of the email
                    // conversation have been deleted (RFC1036).
                    if ($post->parent != $discussion->firstpost) {
                        $userfrom->customheaders[] = "References: {$rootid} {$parentid}";
                    } else {
                        $userfrom->customheaders[] = "References: {$parentid}";
                    }
                }
                // MS Outlook / Office uses poorly documented and non standard headers, including
                // Thread-Topic which overrides the Subject and shouldn't contain Re: or Fwd: etc.
                $a->subject = $discussion->name;
                $threadtopic = html_to_text(get_string('postmailsubject', 'forum', $a), 0);
                $userfrom->customheaders[] = "Thread-Topic: {$threadtopic}";
                $userfrom->customheaders[] = "Thread-Index: " . substr($rootid, 1, 28);
                // Send the post now!
                mtrace('Sending ', '');
                $eventdata = new \core\message\message();
                $eventdata->courseid = $course->id;
                $eventdata->component = 'mod_forum';
                $eventdata->name = 'posts';
                $eventdata->userfrom = $userfrom;
                $eventdata->userto = $userto;
                $eventdata->subject = $postsubject;
                $eventdata->fullmessage = $textout->render($data);
                $eventdata->fullmessageformat = FORMAT_PLAIN;
                $eventdata->fullmessagehtml = $htmlout->render($data);
                $eventdata->notification = 1;
                $eventdata->replyto = $replyaddress;
                if (!empty($replyaddress)) {
                    // Add extra text to email messages if they can reply back.
                    $textfooter = "\n\n" . get_string('replytopostbyemail', 'mod_forum');
                    $htmlfooter = html_writer::tag('p', get_string('replytopostbyemail', 'mod_forum'));
                    $additionalcontent = array('fullmessage' => array('footer' => $textfooter), 'fullmessagehtml' => array('footer' => $htmlfooter));
                    $eventdata->set_additional_content('email', $additionalcontent);
                }
                $smallmessagestrings = new stdClass();
                $smallmessagestrings->user = fullname($userfrom);
                $smallmessagestrings->forumname = "{$shortname}: " . format_string($forum->name, true) . ": " . $discussion->name;
                $smallmessagestrings->message = $post->message;
                // Make sure strings are in message recipients language.
                $eventdata->smallmessage = get_string_manager()->get_string('smallmessage', 'forum', $smallmessagestrings, $userto->lang);
                $contexturl = new moodle_url('/mod/forum/discuss.php', array('d' => $discussion->id), 'p' . $post->id);
                $eventdata->contexturl = $contexturl->out();
                $eventdata->contexturlname = $discussion->name;
                $mailresult = message_send($eventdata);
                if (!$mailresult) {
                    mtrace("Error: mod/forum/lib.php forum_cron(): Could not send out mail for id {$post->id} to user {$userto->id}" . " ({$userto->email}) .. not trying again.");
                    $errorcount[$post->id]++;
                } else {
                    $mailcount[$post->id]++;
                    // Mark post as read if forum_usermarksread is set off.
                    if (!$CFG->forum_usermarksread) {
                        $userto->markposts[$post->id] = $post->id;
                    }
                }
                mtrace('post ' . $post->id . ': ' . $post->subject);
            }
            // Mark processed posts as read.
            if (get_user_preferences('forum_markasreadonnotification', 1, $userto->id) == 1) {
                forum_tp_mark_posts_read($userto, $userto->markposts);
            }
            unset($userto);
        }
    }
    if ($posts) {
        foreach ($posts as $post) {
            mtrace($mailcount[$post->id] . " users were sent post {$post->id}, '{$post->subject}'");
            if ($errorcount[$post->id]) {
                $DB->set_field('forum_posts', 'mailed', FORUM_MAILED_ERROR, array('id' => $post->id));
            }
        }
    }
    // release some memory
    unset($subscribedusers);
    unset($mailcount);
    unset($errorcount);
    cron_setup_user();
    $sitetimezone = core_date::get_server_timezone();
    // Now see if there are any digest mails waiting to be sent, and if we should send them
    mtrace('Starting digest processing...');
    core_php_time_limit::raise(300);
    // terminate if not able to fetch all digests in 5 minutes
    if (!isset($CFG->digestmailtimelast)) {
        // To catch the first time
        set_config('digestmailtimelast', 0);
    }
    $timenow = time();
    $digesttime = usergetmidnight($timenow, $sitetimezone) + $CFG->digestmailtime * 3600;
    // Delete any really old ones (normally there shouldn't be any)
    $weekago = $timenow - 7 * 24 * 3600;
    $DB->delete_records_select('forum_queue', "timemodified < ?", array($weekago));
    mtrace('Cleaned old digest records');
    if ($CFG->digestmailtimelast < $digesttime and $timenow > $digesttime) {
        mtrace('Sending forum digests: ' . userdate($timenow, '', $sitetimezone));
        $digestposts_rs = $DB->get_recordset_select('forum_queue', "timemodified < ?", array($digesttime));
        if ($digestposts_rs->valid()) {
            // We have work to do
            $usermailcount = 0;
            //caches - reuse the those filled before too
            $discussionposts = array();
            $userdiscussions = array();
            foreach ($digestposts_rs as $digestpost) {
                if (!isset($posts[$digestpost->postid])) {
                    if ($post = $DB->get_record('forum_posts', array('id' => $digestpost->postid))) {
                        $posts[$digestpost->postid] = $post;
                    } else {
                        continue;
                    }
                }
                $discussionid = $digestpost->discussionid;
                if (!isset($discussions[$discussionid])) {
                    if ($discussion = $DB->get_record('forum_discussions', array('id' => $discussionid))) {
                        $discussions[$discussionid] = $discussion;
                    } else {
                        continue;
                    }
                }
                $forumid = $discussions[$discussionid]->forum;
                if (!isset($forums[$forumid])) {
                    if ($forum = $DB->get_record('forum', array('id' => $forumid))) {
                        $forums[$forumid] = $forum;
                    } else {
                        continue;
                    }
                }
                $courseid = $forums[$forumid]->course;
                if (!isset($courses[$courseid])) {
                    if ($course = $DB->get_record('course', array('id' => $courseid))) {
                        $courses[$courseid] = $course;
                    } else {
                        continue;
                    }
                }
                if (!isset($coursemodules[$forumid])) {
                    if ($cm = get_coursemodule_from_instance('forum', $forumid, $courseid)) {
                        $coursemodules[$forumid] = $cm;
                    } else {
                        continue;
                    }
                }
                $userdiscussions[$digestpost->userid][$digestpost->discussionid] = $digestpost->discussionid;
                $discussionposts[$digestpost->discussionid][$digestpost->postid] = $digestpost->postid;
            }
            $digestposts_rs->close();
            /// Finished iteration, let's close the resultset
            // Data collected, start sending out emails to each user
            foreach ($userdiscussions as $userid => $thesediscussions) {
                core_php_time_limit::raise(120);
                // terminate if processing of any account takes longer than 2 minutes
                cron_setup_user();
                mtrace(get_string('processingdigest', 'forum', $userid), '... ');
                // First of all delete all the queue entries for this user
                $DB->delete_records_select('forum_queue', "userid = ? AND timemodified < ?", array($userid, $digesttime));
                // Init user caches - we keep the cache for one cycle only,
                // otherwise it would unnecessarily consume memory.
                if (array_key_exists($userid, $users) and isset($users[$userid]->username)) {
                    $userto = clone $users[$userid];
                } else {
                    $userto = $DB->get_record('user', array('id' => $userid));
                    forum_cron_minimise_user_record($userto);
                }
                $userto->viewfullnames = array();
                $userto->canpost = array();
                $userto->markposts = array();
                // Override the language and timezone of the "current" user, so that
                // mail is customised for the receiver.
                cron_setup_user($userto);
                $postsubject = get_string('digestmailsubject', 'forum', format_string($site->shortname, true));
                $headerdata = new stdClass();
                $headerdata->sitename = format_string($site->fullname, true);
                $headerdata->userprefs = $CFG->wwwroot . '/user/forum.php?id=' . $userid . '&amp;course=' . $site->id;
                $posttext = get_string('digestmailheader', 'forum', $headerdata) . "\n\n";
                $headerdata->userprefs = '<a target="_blank" href="' . $headerdata->userprefs . '">' . get_string('digestmailprefs', 'forum') . '</a>';
                $posthtml = '<p>' . get_string('digestmailheader', 'forum', $headerdata) . '</p>' . '<br /><hr size="1" noshade="noshade" />';
                foreach ($thesediscussions as $discussionid) {
                    core_php_time_limit::raise(120);
                    // to be reset for each post
                    $discussion = $discussions[$discussionid];
                    $forum = $forums[$discussion->forum];
                    $course = $courses[$forum->course];
                    $cm = $coursemodules[$forum->id];
                    //override language
                    cron_setup_user($userto, $course);
                    // Fill caches
                    if (!isset($userto->viewfullnames[$forum->id])) {
                        $modcontext = context_module::instance($cm->id);
                        $userto->viewfullnames[$forum->id] = has_capability('moodle/site:viewfullnames', $modcontext);
                    }
                    if (!isset($userto->canpost[$discussion->id])) {
                        $modcontext = context_module::instance($cm->id);
                        $userto->canpost[$discussion->id] = forum_user_can_post($forum, $discussion, $userto, $cm, $course, $modcontext);
                    }
                    $strforums = get_string('forums', 'forum');
                    $canunsubscribe = !\mod_forum\subscriptions::is_forcesubscribed($forum);
                    $canreply = $userto->canpost[$discussion->id];
                    $shortname = format_string($course->shortname, true, array('context' => context_course::instance($course->id)));
                    $posttext .= "\n \n";
                    $posttext .= '=====================================================================';
                    $posttext .= "\n \n";
                    $posttext .= "{$shortname} -> {$strforums} -> " . format_string($forum->name, true);
                    if ($discussion->name != $forum->name) {
                        $posttext .= " -> " . format_string($discussion->name, true);
                    }
                    $posttext .= "\n";
                    $posttext .= $CFG->wwwroot . '/mod/forum/discuss.php?d=' . $discussion->id;
                    $posttext .= "\n";
                    $posthtml .= "<p><font face=\"sans-serif\">" . "<a target=\"_blank\" href=\"{$CFG->wwwroot}/course/view.php?id={$course->id}\">{$shortname}</a> -> " . "<a target=\"_blank\" href=\"{$CFG->wwwroot}/mod/forum/index.php?id={$course->id}\">{$strforums}</a> -> " . "<a target=\"_blank\" href=\"{$CFG->wwwroot}/mod/forum/view.php?f={$forum->id}\">" . format_string($forum->name, true) . "</a>";
                    if ($discussion->name == $forum->name) {
                        $posthtml .= "</font></p>";
                    } else {
                        $posthtml .= " -> <a target=\"_blank\" href=\"{$CFG->wwwroot}/mod/forum/discuss.php?d={$discussion->id}\">" . format_string($discussion->name, true) . "</a></font></p>";
                    }
                    $posthtml .= '<p>';
                    $postsarray = $discussionposts[$discussionid];
                    sort($postsarray);
                    $sentcount = 0;
                    foreach ($postsarray as $postid) {
                        $post = $posts[$postid];
                        if (array_key_exists($post->userid, $users)) {
                            // we might know him/her already
                            $userfrom = $users[$post->userid];
                            if (!isset($userfrom->idnumber)) {
                                $userfrom = $DB->get_record('user', array('id' => $userfrom->id));
                                forum_cron_minimise_user_record($userfrom);
                            }
                        } else {
                            if ($userfrom = $DB->get_record('user', array('id' => $post->userid))) {
                                forum_cron_minimise_user_record($userfrom);
                                if ($userscount <= FORUM_CRON_USER_CACHE) {
                                    $userscount++;
                                    $users[$userfrom->id] = $userfrom;
                                }
                            } else {
                                mtrace('Could not find user ' . $post->userid);
                                continue;
                            }
                        }
                        if (!isset($userfrom->groups[$forum->id])) {
                            if (!isset($userfrom->groups)) {
                                $userfrom->groups = array();
                                if (isset($users[$userfrom->id])) {
                                    $users[$userfrom->id]->groups = array();
                                }
                            }
                            $userfrom->groups[$forum->id] = groups_get_all_groups($course->id, $userfrom->id, $cm->groupingid);
                            if (isset($users[$userfrom->id])) {
                                $users[$userfrom->id]->groups[$forum->id] = $userfrom->groups[$forum->id];
                            }
                        }
                        // Headers to help prevent auto-responders.
                        $userfrom->customheaders = array("Precedence: Bulk", 'X-Auto-Response-Suppress: All', 'Auto-Submitted: auto-generated');
                        $maildigest = forum_get_user_maildigest_bulk($digests, $userto, $forum->id);
                        if (!isset($userto->canpost[$discussion->id])) {
                            $canreply = forum_user_can_post($forum, $discussion, $userto, $cm, $course, $modcontext);
                        } else {
                            $canreply = $userto->canpost[$discussion->id];
                        }
                        $data = new \mod_forum\output\forum_post_email($course, $cm, $forum, $discussion, $post, $userfrom, $userto, $canreply);
                        if (!isset($userto->viewfullnames[$forum->id])) {
                            $data->viewfullnames = has_capability('moodle/site:viewfullnames', $modcontext, $userto->id);
                        } else {
                            $data->viewfullnames = $userto->viewfullnames[$forum->id];
                        }
                        if ($maildigest == 2) {
                            // Subjects and link only.
                            $posttext .= $textdigestbasicout->render($data);
                            $posthtml .= $htmldigestbasicout->render($data);
                        } else {
                            // The full treatment.
                            $posttext .= $textdigestfullout->render($data);
                            $posthtml .= $htmldigestfullout->render($data);
                            // Create an array of postid's for this user to mark as read.
                            if (!$CFG->forum_usermarksread) {
                                $userto->markposts[$post->id] = $post->id;
                            }
                        }
                        $sentcount++;
                    }
                    $footerlinks = array();
                    if ($canunsubscribe) {
                        $footerlinks[] = "<a href=\"{$CFG->wwwroot}/mod/forum/subscribe.php?id={$forum->id}\">" . get_string("unsubscribe", "forum") . "</a>";
                    } else {
                        $footerlinks[] = get_string("everyoneissubscribed", "forum");
                    }
                    $footerlinks[] = "<a href='{$CFG->wwwroot}/mod/forum/index.php?id={$forum->course}'>" . get_string("digestmailpost", "forum") . '</a>';
                    $posthtml .= "\n<div class='mdl-right'><font size=\"1\">" . implode('&nbsp;', $footerlinks) . '</font></div>';
                    $posthtml .= '<hr size="1" noshade="noshade" /></p>';
                }
                if (empty($userto->mailformat) || $userto->mailformat != 1) {
                    // This user DOESN'T want to receive HTML
                    $posthtml = '';
                }
                $eventdata = new \core\message\message();
                $eventdata->courseid = SITEID;
                $eventdata->component = 'mod_forum';
                $eventdata->name = 'digests';
                $eventdata->userfrom = core_user::get_noreply_user();
                $eventdata->userto = $userto;
                $eventdata->subject = $postsubject;
                $eventdata->fullmessage = $posttext;
                $eventdata->fullmessageformat = FORMAT_PLAIN;
                $eventdata->fullmessagehtml = $posthtml;
                $eventdata->notification = 1;
                $eventdata->smallmessage = get_string('smallmessagedigest', 'forum', $sentcount);
                $mailresult = message_send($eventdata);
                if (!$mailresult) {
                    mtrace("ERROR: mod/forum/cron.php: Could not send out digest mail to user {$userto->id} " . "({$userto->email})... not trying again.");
                } else {
                    mtrace("success.");
                    $usermailcount++;
                    // Mark post as read if forum_usermarksread is set off
                    if (get_user_preferences('forum_markasreadonnotification', 1, $userto->id) == 1) {
                        forum_tp_mark_posts_read($userto, $userto->markposts);
                    }
                }
            }
        }
        /// We have finishied all digest emails, update $CFG->digestmailtimelast
        set_config('digestmailtimelast', $timenow);
    }
    cron_setup_user();
    if (!empty($usermailcount)) {
        mtrace(get_string('digestsentusers', 'forum', $usermailcount));
    }
    if (!empty($CFG->forum_lastreadclean)) {
        $timenow = time();
        if ($CFG->forum_lastreadclean + 24 * 3600 < $timenow) {
            set_config('forum_lastreadclean', $timenow);
            mtrace('Removing old forum read tracking info...');
            forum_tp_clean_read_records();
        }
    } else {
        set_config('forum_lastreadclean', time());
    }
    return true;
}
Exemple #18
0
 /**
  * Test delivery of grade notifications as controlled by marking workflow.
  */
 public function test_markingworkflow_cron()
 {
     // First run cron so there are no messages waiting to be sent (from other tests).
     cron_setup_user();
     assign::cron();
     // Now create an assignment with marking workflow enabled.
     $this->setUser($this->editingteachers[0]);
     $assign = $this->create_instance(array('sendstudentnotifications' => 1, 'markingworkflow' => 1));
     // Simulate adding a grade.
     $this->setUser($this->teachers[0]);
     $data = new stdClass();
     $data->grade = '50.0';
     // This student will not receive notification.
     $data->workflowstate = ASSIGN_MARKING_WORKFLOW_STATE_READYFORRELEASE;
     $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
     // This student will receive notification.
     $data->workflowstate = ASSIGN_MARKING_WORKFLOW_STATE_RELEASED;
     $assign->testable_apply_grade_to_user($data, $this->students[1]->id, 0);
     // Now run cron and see that one message was sent.
     $this->preventResetByRollback();
     $sink = $this->redirectMessages();
     cron_setup_user();
     $this->expectOutputRegex('/Done processing 1 assignment submissions/');
     assign::cron();
     $messages = $sink->get_messages();
     $this->assertEquals(1, count($messages));
     $this->assertEquals($messages[0]->useridto, $this->students[1]->id);
     $this->assertEquals($assign->get_instance()->name, $messages[0]->contexturlname);
 }
Exemple #19
0
/**
 * Function to be run periodically according to the moodle cron
 *
 * Finds all assignment notifications that have yet to be mailed out, and mails them
 */
function assignment_cron()
{
    global $CFG, $USER, $DB;
    /// first execute all crons in plugins
    if ($plugins = get_plugin_list('assignment')) {
        foreach ($plugins as $plugin => $dir) {
            require_once "{$dir}/assignment.class.php";
            $assignmentclass = "assignment_{$plugin}";
            $ass = new $assignmentclass();
            $ass->cron();
        }
    }
    /// Notices older than 1 day will not be mailed.  This is to avoid the problem where
    /// cron has not been running for a long time, and then suddenly people are flooded
    /// with mail from the past few weeks or months
    $timenow = time();
    $endtime = $timenow - $CFG->maxeditingtime;
    $starttime = $endtime - 24 * 3600;
    /// One day earlier
    if ($submissions = assignment_get_unmailed_submissions($starttime, $endtime)) {
        $realuser = clone $USER;
        foreach ($submissions as $key => $submission) {
            if (!$DB->set_field("assignment_submissions", "mailed", "1", array("id" => $submission->id))) {
                echo "Could not update the mailed field for id {$submission->id}.  Not mailed.\n";
                unset($submissions[$key]);
            }
        }
        $timenow = time();
        foreach ($submissions as $submission) {
            echo "Processing assignment submission {$submission->id}\n";
            if (!($user = $DB->get_record("user", array("id" => $submission->userid)))) {
                echo "Could not find user {$post->userid}\n";
                continue;
            }
            if (!($course = $DB->get_record("course", array("id" => $submission->course)))) {
                echo "Could not find course {$submission->course}\n";
                continue;
            }
            /// Override the language and timezone of the "current" user, so that
            /// mail is customised for the receiver.
            cron_setup_user($user, $course);
            if (!has_capability('moodle/course:view', get_context_instance(CONTEXT_COURSE, $submission->course), $user->id)) {
                echo fullname($user) . " not an active participant in " . format_string($course->shortname) . "\n";
                continue;
            }
            if (!($teacher = $DB->get_record("user", array("id" => $submission->teacher)))) {
                echo "Could not find teacher {$submission->teacher}\n";
                continue;
            }
            if (!($mod = get_coursemodule_from_instance("assignment", $submission->assignment, $course->id))) {
                echo "Could not find course module for assignment id {$submission->assignment}\n";
                continue;
            }
            if (!$mod->visible) {
                /// Hold mail notification for hidden assignments until later
                continue;
            }
            $strassignments = get_string("modulenameplural", "assignment");
            $strassignment = get_string("modulename", "assignment");
            $assignmentinfo = new object();
            $assignmentinfo->teacher = fullname($teacher);
            $assignmentinfo->assignment = format_string($submission->name, true);
            $assignmentinfo->url = "{$CFG->wwwroot}/mod/assignment/view.php?id={$mod->id}";
            $postsubject = "{$course->shortname}: {$strassignments}: " . format_string($submission->name, true);
            $posttext = "{$course->shortname} -> {$strassignments} -> " . format_string($submission->name, true) . "\n";
            $posttext .= "---------------------------------------------------------------------\n";
            $posttext .= get_string("assignmentmail", "assignment", $assignmentinfo) . "\n";
            $posttext .= "---------------------------------------------------------------------\n";
            if ($user->mailformat == 1) {
                // HTML
                $posthtml = "<p><font face=\"sans-serif\">" . "<a href=\"{$CFG->wwwroot}/course/view.php?id={$course->id}\">{$course->shortname}</a> ->" . "<a href=\"{$CFG->wwwroot}/mod/assignment/index.php?id={$course->id}\">{$strassignments}</a> ->" . "<a href=\"{$CFG->wwwroot}/mod/assignment/view.php?id={$mod->id}\">" . format_string($submission->name, true) . "</a></font></p>";
                $posthtml .= "<hr /><font face=\"sans-serif\">";
                $posthtml .= "<p>" . get_string("assignmentmailhtml", "assignment", $assignmentinfo) . "</p>";
                $posthtml .= "</font><hr />";
            } else {
                $posthtml = "";
            }
            $eventdata = new object();
            $eventdata->modulename = 'assignment';
            $eventdata->userfrom = $teacher;
            $eventdata->userto = $user;
            $eventdata->subject = $postsubject;
            $eventdata->fullmessage = $posttext;
            $eventdata->fullmessageformat = FORMAT_PLAIN;
            $eventdata->fullmessagehtml = $posthtml;
            $eventdata->smallmessage = '';
            if (events_trigger('message_send', $eventdata) > 0) {
                echo "Error: assignment cron: Could not send out mail for id {$submission->id} to user {$user->id} ({$user->email})\n";
            }
        }
        cron_setup_user();
    }
    return true;
}
Exemple #20
0
 /**
  * Runs a scheduled task immediately, given full class name.
  *
  * This is faster and more reliable than running cron (running cron won't
  * work more than once in the same test, for instance). However it is
  * a little less 'realistic'.
  *
  * While the task is running, we suppress mtrace output because it makes
  * the Behat result look ugly.
  *
  * Note: Most of the code relating to running a task is based on
  * admin/tool/task/cli/schedule_task.php.
  *
  * @Given /^I run the scheduled task "(?P<task_name>[^"]+)"$/
  * @param string $taskname Name of task e.g. 'mod_whatever\task\do_something'
  */
 public function i_run_the_scheduled_task($taskname)
 {
     $task = \core\task\manager::get_scheduled_task($taskname);
     if (!$task) {
         throw new DriverException('The "' . $taskname . '" scheduled task does not exist');
     }
     // Do setup for cron task.
     raise_memory_limit(MEMORY_EXTRA);
     cron_setup_user();
     // Get lock.
     $cronlockfactory = \core\lock\lock_config::get_lock_factory('cron');
     if (!($cronlock = $cronlockfactory->get_lock('core_cron', 10))) {
         throw new DriverException('Unable to obtain core_cron lock for scheduled task');
     }
     if (!($lock = $cronlockfactory->get_lock('\\' . get_class($task), 10))) {
         $cronlock->release();
         throw new DriverException('Unable to obtain task lock for scheduled task');
     }
     $task->set_lock($lock);
     if (!$task->is_blocking()) {
         $cronlock->release();
     } else {
         $task->set_cron_lock($cronlock);
     }
     try {
         // Discard task output as not appropriate for Behat output!
         ob_start();
         $task->execute();
         ob_end_clean();
         // Mark task complete.
         \core\task\manager::scheduled_task_complete($task);
     } catch (Exception $e) {
         // Mark task failed and throw exception.
         \core\task\manager::scheduled_task_failed($task);
         throw new DriverException('The "' . $taskname . '" scheduled task failed', 0, $e);
     }
 }
Exemple #21
0
 /**
  * Finds all assignment notifications that have yet to be mailed out, and mails them.
  *
  * Cron function to be run periodically according to the moodle cron.
  *
  * @return bool
  */
 public static function cron()
 {
     global $DB;
     // Only ever send a max of one days worth of updates.
     $yesterday = time() - 24 * 3600;
     $timenow = time();
     // Collect all submissions from the past 24 hours that require mailing.
     $sql = 'SELECT g.id as gradeid, a.course, a.name, a.blindmarking, a.revealidentities,
                    g.*, g.timemodified as lastmodified
              FROM {assign} a
              JOIN {assign_grades} g ON g.assignment = a.id
              LEFT JOIN {assign_user_flags} uf ON uf.assignment = a.id AND uf.userid = g.userid
             WHERE g.timemodified >= :yesterday AND
                   g.timemodified <= :today AND
                   uf.mailed = 0';
     $params = array('yesterday' => $yesterday, 'today' => $timenow);
     $submissions = $DB->get_records_sql($sql, $params);
     if (empty($submissions)) {
         return true;
     }
     mtrace('Processing ' . count($submissions) . ' assignment submissions ...');
     // Preload courses we are going to need those.
     $courseids = array();
     foreach ($submissions as $submission) {
         $courseids[] = $submission->course;
     }
     // Filter out duplicates.
     $courseids = array_unique($courseids);
     $ctxselect = context_helper::get_preload_record_columns_sql('ctx');
     list($courseidsql, $params) = $DB->get_in_or_equal($courseids, SQL_PARAMS_NAMED);
     $sql = 'SELECT c.*, ' . $ctxselect . ' FROM {course} c
          LEFT JOIN {context} ctx ON ctx.instanceid = c.id AND ctx.contextlevel = :contextlevel
              WHERE c.id ' . $courseidsql;
     $params['contextlevel'] = CONTEXT_COURSE;
     $courses = $DB->get_records_sql($sql, $params);
     // Clean up... this could go on for a while.
     unset($courseids);
     unset($ctxselect);
     unset($courseidsql);
     unset($params);
     // Simple array we'll use for caching modules.
     $modcache = array();
     // Message students about new feedback.
     foreach ($submissions as $submission) {
         mtrace("Processing assignment submission {$submission->id} ...");
         // Do not cache user lookups - could be too many.
         if (!($user = $DB->get_record('user', array('id' => $submission->userid)))) {
             mtrace('Could not find user ' . $submission->userid);
             continue;
         }
         // Use a cache to prevent the same DB queries happening over and over.
         if (!array_key_exists($submission->course, $courses)) {
             mtrace('Could not find course ' . $submission->course);
             continue;
         }
         $course = $courses[$submission->course];
         if (isset($course->ctxid)) {
             // Context has not yet been preloaded. Do so now.
             context_helper::preload_from_record($course);
         }
         // Override the language and timezone of the "current" user, so that
         // mail is customised for the receiver.
         cron_setup_user($user, $course);
         // Context lookups are already cached.
         $coursecontext = context_course::instance($course->id);
         if (!is_enrolled($coursecontext, $user->id)) {
             $courseshortname = format_string($course->shortname, true, array('context' => $coursecontext));
             mtrace(fullname($user) . ' not an active participant in ' . $courseshortname);
             continue;
         }
         if (!($grader = $DB->get_record('user', array('id' => $submission->grader)))) {
             mtrace('Could not find grader ' . $submission->grader);
             continue;
         }
         if (!array_key_exists($submission->assignment, $modcache)) {
             $mod = get_coursemodule_from_instance('assign', $submission->assignment, $course->id);
             if (empty($mod)) {
                 mtrace('Could not find course module for assignment id ' . $submission->assignment);
                 continue;
             }
             $modcache[$submission->assignment] = $mod;
         } else {
             $mod = $modcache[$submission->assignment];
         }
         // Context lookups are already cached.
         $contextmodule = context_module::instance($mod->id);
         if (!$mod->visible) {
             // Hold mail notification for hidden assignments until later.
             continue;
         }
         // Need to send this to the student.
         $messagetype = 'feedbackavailable';
         $eventtype = 'assign_notification';
         $updatetime = $submission->lastmodified;
         $modulename = get_string('modulename', 'assign');
         $uniqueid = 0;
         if ($submission->blindmarking && !$submission->revealidentities) {
             $uniqueid = self::get_uniqueid_for_user_static($submission->assignment, $user->id);
         }
         $showusers = $submission->blindmarking && !$submission->revealidentities;
         self::send_assignment_notification($grader, $user, $messagetype, $eventtype, $updatetime, $mod, $contextmodule, $course, $modulename, $submission->name, $showusers, $uniqueid);
         $flags = $DB->get_record('assign_user_flags', array('userid' => $user->id, 'assignment' => $submission->assignment));
         if ($flags) {
             $flags->mailed = 1;
             $DB->update_record('assign_user_flags', $flags);
         } else {
             $flags = new stdClass();
             $flags->userid = $user->id;
             $flags->assignment = $submission->assignment;
             $flags->mailed = 1;
             $DB->insert_record('assign_user_flags', $flags);
         }
         mtrace('Done');
     }
     mtrace('Done processing ' . count($submissions) . ' assignment submissions');
     cron_setup_user();
     // Free up memory just to be sure.
     unset($courses);
     unset($modcache);
     return true;
 }
Exemple #22
0
<?php

/**
 * Entirely enables the Web Services.
 */
define('CLI_SCRIPT', true);
require dirname(__FILE__) . '/config.php';
require_once $CFG->libdir . '/testing/generator/data_generator.php';
require_once $CFG->libdir . '/accesslib.php';
require_once $CFG->libdir . '/externallib.php';
require_once $CFG->dirroot . '/webservice/lib.php';
// We don't really need to be admin, except to be able to see the generated tokens
// in the admin settings page, while logged in as admin.
cron_setup_user();
// Enable the Web Services.
set_config('enablewebservices', 1);
// Enable Web Services documentation.
set_config('enablewsdocumentation', 1);
// Enable each protocol.
set_config('webserviceprotocols', 'amf,rest,soap,xmlrpc');
// Create the Web Service user.
$user = $DB->get_record('user', array('username' => 'testtete'));
if (!$user) {
    $user = new stdClass();
    $user->username = '******';
    $user->firstname = 'Web';
    $user->lastname = 'Service';
    $user->password = '******';
    $dg = new testing_data_generator();
    $user = $dg->create_user($user);
}
Exemple #23
0
/**
 * Function to be run periodically according to the moodle cron
 * Finds all posts that have yet to be mailed out, and mails them
 * out to all subscribers
 *
 * @global object
 * @global object
 * @global object
 * @uses CONTEXT_MODULE
 * @uses CONTEXT_COURSE
 * @uses SITEID
 * @uses FORMAT_PLAIN
 * @return void
 */
function forum_cron() {
    global $CFG, $USER, $DB;

    $site = get_site();

    // All users that are subscribed to any post that needs sending,
    // please increase $CFG->extramemorylimit on large sites that
    // send notifications to a large number of users.
    $users = array();
    $userscount = 0; // Cached user counter - count($users) in PHP is horribly slow!!!

    // status arrays
    $mailcount  = array();
    $errorcount = array();

    // caches
    $discussions     = array();
    $forums          = array();
    $courses         = array();
    $coursemodules   = array();
    $subscribedusers = array();


    // Posts older than 2 days will not be mailed.  This is to avoid the problem where
    // cron has not been running for a long time, and then suddenly people are flooded
    // with mail from the past few weeks or months
    $timenow   = time();
    $endtime   = $timenow - $CFG->maxeditingtime;
    $starttime = $endtime - 48 * 3600;   // Two days earlier

    if ($posts = forum_get_unmailed_posts($starttime, $endtime, $timenow)) {
        // Mark them all now as being mailed.  It's unlikely but possible there
        // might be an error later so that a post is NOT actually mailed out,
        // but since mail isn't crucial, we can accept this risk.  Doing it now
        // prevents the risk of duplicated mails, which is a worse problem.

        if (!forum_mark_old_posts_as_mailed($endtime)) {
            mtrace('Errors occurred while trying to mark some posts as being mailed.');
            return false;  // Don't continue trying to mail them, in case we are in a cron loop
        }

        // checking post validity, and adding users to loop through later
        foreach ($posts as $pid => $post) {

            $discussionid = $post->discussion;
            if (!isset($discussions[$discussionid])) {
                if ($discussion = $DB->get_record('forum_discussions', array('id'=> $post->discussion))) {
                    $discussions[$discussionid] = $discussion;
                } else {
                    mtrace('Could not find discussion '.$discussionid);
                    unset($posts[$pid]);
                    continue;
                }
            }
            $forumid = $discussions[$discussionid]->forum;
            if (!isset($forums[$forumid])) {
                if ($forum = $DB->get_record('forum', array('id' => $forumid))) {
                    $forums[$forumid] = $forum;
                } else {
                    mtrace('Could not find forum '.$forumid);
                    unset($posts[$pid]);
                    continue;
                }
            }
            $courseid = $forums[$forumid]->course;
            if (!isset($courses[$courseid])) {
                if ($course = $DB->get_record('course', array('id' => $courseid))) {
                    $courses[$courseid] = $course;
                } else {
                    mtrace('Could not find course '.$courseid);
                    unset($posts[$pid]);
                    continue;
                }
            }
            if (!isset($coursemodules[$forumid])) {
                if ($cm = get_coursemodule_from_instance('forum', $forumid, $courseid)) {
                    $coursemodules[$forumid] = $cm;
                } else {
                    mtrace('Could not find course module for forum '.$forumid);
                    unset($posts[$pid]);
                    continue;
                }
            }


            // caching subscribed users of each forum
            if (!isset($subscribedusers[$forumid])) {
                $modcontext = context_module::instance($coursemodules[$forumid]->id);
                if ($subusers = forum_subscribed_users($courses[$courseid], $forums[$forumid], 0, $modcontext, "u.*")) {
                    foreach ($subusers as $postuser) {
                        // this user is subscribed to this forum
                        $subscribedusers[$forumid][$postuser->id] = $postuser->id;
                        $userscount++;
                        if ($userscount > FORUM_CRON_USER_CACHE) {
                            // Store minimal user info.
                            $minuser = new stdClass();
                            $minuser->id = $postuser->id;
                            $users[$postuser->id] = $minuser;
                        } else {
                            // Cache full user record.
                            forum_cron_minimise_user_record($postuser);
                            $users[$postuser->id] = $postuser;
                        }
                    }
                    // Release memory.
                    unset($subusers);
                    unset($postuser);
                }
            }

            $mailcount[$pid] = 0;
            $errorcount[$pid] = 0;
        }
    }

    if ($users && $posts) {

        $urlinfo = parse_url($CFG->wwwroot);
        $hostname = $urlinfo['host'];

        foreach ($users as $userto) {

            @set_time_limit(120); // terminate if processing of any account takes longer than 2 minutes

            mtrace('Processing user '.$userto->id);

            // Init user caches - we keep the cache for one cycle only,
            // otherwise it could consume too much memory.
            if (isset($userto->username)) {
                $userto = clone($userto);
            } else {
                $userto = $DB->get_record('user', array('id' => $userto->id));
                forum_cron_minimise_user_record($userto);
            }
            $userto->viewfullnames = array();
            $userto->canpost       = array();
            $userto->markposts     = array();

            // set this so that the capabilities are cached, and environment matches receiving user
            cron_setup_user($userto);

            // reset the caches
            foreach ($coursemodules as $forumid=>$unused) {
                $coursemodules[$forumid]->cache       = new stdClass();
                $coursemodules[$forumid]->cache->caps = array();
                unset($coursemodules[$forumid]->uservisible);
            }

            foreach ($posts as $pid => $post) {

                // Set up the environment for the post, discussion, forum, course
                $discussion = $discussions[$post->discussion];
                $forum      = $forums[$discussion->forum];
                $course     = $courses[$forum->course];
                $cm         =& $coursemodules[$forum->id];

                // Do some checks  to see if we can bail out now
                // Only active enrolled users are in the list of subscribers
                if (!isset($subscribedusers[$forum->id][$userto->id])) {
                    continue; // user does not subscribe to this forum
                }

                // Don't send email if the forum is Q&A and the user has not posted
                // Initial topics are still mailed
                if ($forum->type == 'qanda' && !forum_get_user_posted_time($discussion->id, $userto->id) && $pid != $discussion->firstpost) {
                    mtrace('Did not email '.$userto->id.' because user has not posted in discussion');
                    continue;
                }

                // Get info about the sending user
                if (array_key_exists($post->userid, $users)) { // we might know him/her already
                    $userfrom = $users[$post->userid];
                    if (!isset($userfrom->idnumber)) {
                        // Minimalised user info, fetch full record.
                        $userfrom = $DB->get_record('user', array('id' => $userfrom->id));
                        forum_cron_minimise_user_record($userfrom);
                    }

                } else if ($userfrom = $DB->get_record('user', array('id' => $post->userid))) {
                    forum_cron_minimise_user_record($userfrom);
                    // Fetch only once if possible, we can add it to user list, it will be skipped anyway.
                    if ($userscount <= FORUM_CRON_USER_CACHE) {
                        $userscount++;
                        $users[$userfrom->id] = $userfrom;
                    }

                } else {
                    mtrace('Could not find user '.$post->userid);
                    continue;
                }

                //if we want to check that userto and userfrom are not the same person this is probably the spot to do it

                // setup global $COURSE properly - needed for roles and languages
                cron_setup_user($userto, $course);

                // Fill caches
                if (!isset($userto->viewfullnames[$forum->id])) {
                    $modcontext = context_module::instance($cm->id);
                    $userto->viewfullnames[$forum->id] = has_capability('moodle/site:viewfullnames', $modcontext);
                }
                if (!isset($userto->canpost[$discussion->id])) {
                    $modcontext = context_module::instance($cm->id);
                    $userto->canpost[$discussion->id] = forum_user_can_post($forum, $discussion, $userto, $cm, $course, $modcontext);
                }
                if (!isset($userfrom->groups[$forum->id])) {
                    if (!isset($userfrom->groups)) {
                        $userfrom->groups = array();
                        if (isset($users[$userfrom->id])) {
                            $users[$userfrom->id]->groups = array();
                        }
                    }
                    $userfrom->groups[$forum->id] = groups_get_all_groups($course->id, $userfrom->id, $cm->groupingid);
                    if (isset($users[$userfrom->id])) {
                        $users[$userfrom->id]->groups[$forum->id] = $userfrom->groups[$forum->id];
                    }
                }

                // Make sure groups allow this user to see this email
                if ($discussion->groupid > 0 and $groupmode = groups_get_activity_groupmode($cm, $course)) {   // Groups are being used
                    if (!groups_group_exists($discussion->groupid)) { // Can't find group
                        continue;                           // Be safe and don't send it to anyone
                    }

                    if (!groups_is_member($discussion->groupid) and !has_capability('moodle/site:accessallgroups', $modcontext)) {
                        // do not send posts from other groups when in SEPARATEGROUPS or VISIBLEGROUPS
                        continue;
                    }
                }

                // Make sure we're allowed to see it...
                if (!forum_user_can_see_post($forum, $discussion, $post, NULL, $cm)) {
                    mtrace('user '.$userto->id. ' can not see '.$post->id);
                    continue;
                }

                // OK so we need to send the email.

                // Does the user want this post in a digest?  If so postpone it for now.
                if ($userto->maildigest > 0) {
                    // This user wants the mails to be in digest form
                    $queue = new stdClass();
                    $queue->userid       = $userto->id;
                    $queue->discussionid = $discussion->id;
                    $queue->postid       = $post->id;
                    $queue->timemodified = $post->created;
                    $DB->insert_record('forum_queue', $queue);
                    continue;
                }


                // Prepare to actually send the post now, and build up the content

                $cleanforumname = str_replace('"', "'", strip_tags(format_string($forum->name)));

                $userfrom->customheaders = array (  // Headers to make emails easier to track
                           'Precedence: Bulk',
                           'List-Id: "'.$cleanforumname.'" <moodleforum'.$forum->id.'@'.$hostname.'>',
                           'List-Help: '.$CFG->wwwroot.'/mod/forum/view.php?f='.$forum->id,
                           'Message-ID: '.forum_get_email_message_id($post->id, $userto->id, $hostname),
                           'X-Course-Id: '.$course->id,
                           'X-Course-Name: '.format_string($course->fullname, true)
                );

                if ($post->parent) {  // This post is a reply, so add headers for threading (see MDL-22551)
                    $userfrom->customheaders[] = 'In-Reply-To: '.forum_get_email_message_id($post->parent, $userto->id, $hostname);
                    $userfrom->customheaders[] = 'References: '.forum_get_email_message_id($post->parent, $userto->id, $hostname);
                }

                $shortname = format_string($course->shortname, true, array('context' => context_course::instance($course->id)));

                $postsubject = html_to_text("$shortname: ".format_string($post->subject, true));
                $posttext = forum_make_mail_text($course, $cm, $forum, $discussion, $post, $userfrom, $userto);
                $posthtml = forum_make_mail_html($course, $cm, $forum, $discussion, $post, $userfrom, $userto);

                // Send the post now!

                mtrace('Sending ', '');

                $eventdata = new stdClass();
                $eventdata->component        = 'mod_forum';
                $eventdata->name             = 'posts';
                $eventdata->userfrom         = $userfrom;
                $eventdata->userto           = $userto;
                $eventdata->subject          = $postsubject;
                $eventdata->fullmessage      = $posttext;
                $eventdata->fullmessageformat = FORMAT_PLAIN;
                $eventdata->fullmessagehtml  = $posthtml;
                $eventdata->notification = 1;

                // If forum_replytouser is not set then send mail using the noreplyaddress.
                if (empty($CFG->forum_replytouser)) {
                    // Clone userfrom as it is referenced by $users.
                    $cloneduserfrom = clone($userfrom);
                    $cloneduserfrom->email = $CFG->noreplyaddress;
                    $eventdata->userfrom = $cloneduserfrom;
                }

                $smallmessagestrings = new stdClass();
                $smallmessagestrings->user = fullname($userfrom);
                $smallmessagestrings->forumname = "$shortname: ".format_string($forum->name,true).": ".$discussion->name;
                $smallmessagestrings->message = $post->message;
                //make sure strings are in message recipients language
                $eventdata->smallmessage = get_string_manager()->get_string('smallmessage', 'forum', $smallmessagestrings, $userto->lang);

                $eventdata->contexturl = "{$CFG->wwwroot}/mod/forum/discuss.php?d={$discussion->id}#p{$post->id}";
                $eventdata->contexturlname = $discussion->name;

                $mailresult = message_send($eventdata);
                if (!$mailresult){
                    mtrace("Error: mod/forum/lib.php forum_cron(): Could not send out mail for id $post->id to user $userto->id".
                         " ($userto->email) .. not trying again.");
                    add_to_log($course->id, 'forum', 'mail error', "discuss.php?d=$discussion->id#p$post->id",
                               substr(format_string($post->subject,true),0,30), $cm->id, $userto->id);
                    $errorcount[$post->id]++;
                } else {
                    $mailcount[$post->id]++;

                // Mark post as read if forum_usermarksread is set off
                    if (!$CFG->forum_usermarksread) {
                        $userto->markposts[$post->id] = $post->id;
                    }
                }

                mtrace('post '.$post->id. ': '.$post->subject);
            }

            // mark processed posts as read
            forum_tp_mark_posts_read($userto, $userto->markposts);
            unset($userto);
        }
    }

    if ($posts) {
        foreach ($posts as $post) {
            mtrace($mailcount[$post->id]." users were sent post $post->id, '$post->subject'");
            if ($errorcount[$post->id]) {
                $DB->set_field('forum_posts', 'mailed', FORUM_MAILED_ERROR, array('id' => $post->id));
            }
        }
    }

    // release some memory
    unset($subscribedusers);
    unset($mailcount);
    unset($errorcount);

    cron_setup_user();

    $sitetimezone = $CFG->timezone;

    // Now see if there are any digest mails waiting to be sent, and if we should send them

    mtrace('Starting digest processing...');

    @set_time_limit(300); // terminate if not able to fetch all digests in 5 minutes

    if (!isset($CFG->digestmailtimelast)) {    // To catch the first time
        set_config('digestmailtimelast', 0);
    }

    $timenow = time();
    $digesttime = usergetmidnight($timenow, $sitetimezone) + ($CFG->digestmailtime * 3600);

    // Delete any really old ones (normally there shouldn't be any)
    $weekago = $timenow - (7 * 24 * 3600);
    $DB->delete_records_select('forum_queue', "timemodified < ?", array($weekago));
    mtrace ('Cleaned old digest records');

    if ($CFG->digestmailtimelast < $digesttime and $timenow > $digesttime) {

        mtrace('Sending forum digests: '.userdate($timenow, '', $sitetimezone));

        $digestposts_rs = $DB->get_recordset_select('forum_queue', "timemodified < ?", array($digesttime));

        if ($digestposts_rs->valid()) {

            // We have work to do
            $usermailcount = 0;

            //caches - reuse the those filled before too
            $discussionposts = array();
            $userdiscussions = array();

            foreach ($digestposts_rs as $digestpost) {
                if (!isset($posts[$digestpost->postid])) {
                    if ($post = $DB->get_record('forum_posts', array('id' => $digestpost->postid))) {
                        $posts[$digestpost->postid] = $post;
                    } else {
                        continue;
                    }
                }
                $discussionid = $digestpost->discussionid;
                if (!isset($discussions[$discussionid])) {
                    if ($discussion = $DB->get_record('forum_discussions', array('id' => $discussionid))) {
                        $discussions[$discussionid] = $discussion;
                    } else {
                        continue;
                    }
                }
                $forumid = $discussions[$discussionid]->forum;
                if (!isset($forums[$forumid])) {
                    if ($forum = $DB->get_record('forum', array('id' => $forumid))) {
                        $forums[$forumid] = $forum;
                    } else {
                        continue;
                    }
                }

                $courseid = $forums[$forumid]->course;
                if (!isset($courses[$courseid])) {
                    if ($course = $DB->get_record('course', array('id' => $courseid))) {
                        $courses[$courseid] = $course;
                    } else {
                        continue;
                    }
                }

                if (!isset($coursemodules[$forumid])) {
                    if ($cm = get_coursemodule_from_instance('forum', $forumid, $courseid)) {
                        $coursemodules[$forumid] = $cm;
                    } else {
                        continue;
                    }
                }
                $userdiscussions[$digestpost->userid][$digestpost->discussionid] = $digestpost->discussionid;
                $discussionposts[$digestpost->discussionid][$digestpost->postid] = $digestpost->postid;
            }
            $digestposts_rs->close(); /// Finished iteration, let's close the resultset

            // Data collected, start sending out emails to each user
            foreach ($userdiscussions as $userid => $thesediscussions) {

                @set_time_limit(120); // terminate if processing of any account takes longer than 2 minutes

                cron_setup_user();

                mtrace(get_string('processingdigest', 'forum', $userid), '... ');

                // First of all delete all the queue entries for this user
                $DB->delete_records_select('forum_queue', "userid = ? AND timemodified < ?", array($userid, $digesttime));

                // Init user caches - we keep the cache for one cycle only,
                // otherwise it would unnecessarily consume memory.
                if (array_key_exists($userid, $users) and isset($users[$userid]->username)) {
                    $userto = clone($users[$userid]);
                } else {
                    $userto = $DB->get_record('user', array('id' => $userid));
                    forum_cron_minimise_user_record($userto);
                }
                $userto->viewfullnames = array();
                $userto->canpost       = array();
                $userto->markposts     = array();

                // Override the language and timezone of the "current" user, so that
                // mail is customised for the receiver.
                cron_setup_user($userto);

                $postsubject = get_string('digestmailsubject', 'forum', format_string($site->shortname, true));

                $headerdata = new stdClass();
                $headerdata->sitename = format_string($site->fullname, true);
                $headerdata->userprefs = $CFG->wwwroot.'/user/edit.php?id='.$userid.'&amp;course='.$site->id;

                $posttext = get_string('digestmailheader', 'forum', $headerdata)."\n\n";
                $headerdata->userprefs = '<a target="_blank" href="'.$headerdata->userprefs.'">'.get_string('digestmailprefs', 'forum').'</a>';

                $posthtml = "<head>";
/*                foreach ($CFG->stylesheets as $stylesheet) {
                    //TODO: MDL-21120
                    $posthtml .= '<link rel="stylesheet" type="text/css" href="'.$stylesheet.'" />'."\n";
                }*/
                $posthtml .= "</head>\n<body id=\"email\">\n";
                $posthtml .= '<p>'.get_string('digestmailheader', 'forum', $headerdata).'</p><br /><hr size="1" noshade="noshade" />';

                foreach ($thesediscussions as $discussionid) {

                    @set_time_limit(120);   // to be reset for each post

                    $discussion = $discussions[$discussionid];
                    $forum      = $forums[$discussion->forum];
                    $course     = $courses[$forum->course];
                    $cm         = $coursemodules[$forum->id];

                    //override language
                    cron_setup_user($userto, $course);

                    // Fill caches
                    if (!isset($userto->viewfullnames[$forum->id])) {
                        $modcontext = context_module::instance($cm->id);
                        $userto->viewfullnames[$forum->id] = has_capability('moodle/site:viewfullnames', $modcontext);
                    }
                    if (!isset($userto->canpost[$discussion->id])) {
                        $modcontext = context_module::instance($cm->id);
                        $userto->canpost[$discussion->id] = forum_user_can_post($forum, $discussion, $userto, $cm, $course, $modcontext);
                    }

                    $strforums      = get_string('forums', 'forum');
                    $canunsubscribe = ! forum_is_forcesubscribed($forum);
                    $canreply       = $userto->canpost[$discussion->id];
                    $shortname = format_string($course->shortname, true, array('context' => context_course::instance($course->id)));

                    $posttext .= "\n \n";
                    $posttext .= '=====================================================================';
                    $posttext .= "\n \n";
                    $posttext .= "$shortname -> $strforums -> ".format_string($forum->name,true);
                    if ($discussion->name != $forum->name) {
                        $posttext  .= " -> ".format_string($discussion->name,true);
                    }
                    $posttext .= "\n";

                    $posthtml .= "<p><font face=\"sans-serif\">".
                    "<a target=\"_blank\" href=\"$CFG->wwwroot/course/view.php?id=$course->id\">$shortname</a> -> ".
                    "<a target=\"_blank\" href=\"$CFG->wwwroot/mod/forum/index.php?id=$course->id\">$strforums</a> -> ".
                    "<a target=\"_blank\" href=\"$CFG->wwwroot/mod/forum/view.php?f=$forum->id\">".format_string($forum->name,true)."</a>";
                    if ($discussion->name == $forum->name) {
                        $posthtml .= "</font></p>";
                    } else {
                        $posthtml .= " -> <a target=\"_blank\" href=\"$CFG->wwwroot/mod/forum/discuss.php?d=$discussion->id\">".format_string($discussion->name,true)."</a></font></p>";
                    }
                    $posthtml .= '<p>';

                    $postsarray = $discussionposts[$discussionid];
                    sort($postsarray);

                    foreach ($postsarray as $postid) {
                        $post = $posts[$postid];

                        if (array_key_exists($post->userid, $users)) { // we might know him/her already
                            $userfrom = $users[$post->userid];
                            if (!isset($userfrom->idnumber)) {
                                $userfrom = $DB->get_record('user', array('id' => $userfrom->id));
                                forum_cron_minimise_user_record($userfrom);
                            }

                        } else if ($userfrom = $DB->get_record('user', array('id' => $post->userid))) {
                            forum_cron_minimise_user_record($userfrom);
                            if ($userscount <= FORUM_CRON_USER_CACHE) {
                                $userscount++;
                                $users[$userfrom->id] = $userfrom;
                            }

                        } else {
                            mtrace('Could not find user '.$post->userid);
                            continue;
                        }

                        if (!isset($userfrom->groups[$forum->id])) {
                            if (!isset($userfrom->groups)) {
                                $userfrom->groups = array();
                                if (isset($users[$userfrom->id])) {
                                    $users[$userfrom->id]->groups = array();
                                }
                            }
                            $userfrom->groups[$forum->id] = groups_get_all_groups($course->id, $userfrom->id, $cm->groupingid);
                            if (isset($users[$userfrom->id])) {
                                $users[$userfrom->id]->groups[$forum->id] = $userfrom->groups[$forum->id];
                            }
                        }

                        $userfrom->customheaders = array ("Precedence: Bulk");

                        if ($userto->maildigest == 2) {
                            // Subjects only
                            $by = new stdClass();
                            $by->name = fullname($userfrom);
                            $by->date = userdate($post->modified);
                            $posttext .= "\n".format_string($post->subject,true).' '.get_string("bynameondate", "forum", $by);
                            $posttext .= "\n---------------------------------------------------------------------";

                            $by->name = "<a target=\"_blank\" href=\"$CFG->wwwroot/user/view.php?id=$userfrom->id&amp;course=$course->id\">$by->name</a>";
                            $posthtml .= '<div><a target="_blank" href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.$discussion->id.'#p'.$post->id.'">'.format_string($post->subject,true).'</a> '.get_string("bynameondate", "forum", $by).'</div>';

                        } else {
                            // The full treatment
                            $posttext .= forum_make_mail_text($course, $cm, $forum, $discussion, $post, $userfrom, $userto, true);
                            $posthtml .= forum_make_mail_post($course, $cm, $forum, $discussion, $post, $userfrom, $userto, false, $canreply, true, false);

                        // Create an array of postid's for this user to mark as read.
                            if (!$CFG->forum_usermarksread) {
                                $userto->markposts[$post->id] = $post->id;
                            }
                        }
                    }
                    if ($canunsubscribe) {
                        $posthtml .= "\n<div class='mdl-right'><font size=\"1\"><a href=\"$CFG->wwwroot/mod/forum/subscribe.php?id=$forum->id\">".get_string("unsubscribe", "forum")."</a></font></div>";
                    } else {
                        $posthtml .= "\n<div class='mdl-right'><font size=\"1\">".get_string("everyoneissubscribed", "forum")."</font></div>";
                    }
                    $posthtml .= '<hr size="1" noshade="noshade" /></p>';
                }
                $posthtml .= '</body>';

                if (empty($userto->mailformat) || $userto->mailformat != 1) {
                    // This user DOESN'T want to receive HTML
                    $posthtml = '';
                }

                $attachment = $attachname='';
                // Directly email forum digests rather than sending them via messaging, use the
                // site shortname as 'from name', the noreply address will be used by email_to_user.
                $mailresult = email_to_user($userto, $site->shortname, $postsubject, $posttext, $posthtml, $attachment, $attachname);

                if (!$mailresult) {
                    mtrace("ERROR!");
                    echo "Error: mod/forum/cron.php: Could not send out digest mail to user $userto->id ($userto->email)... not trying again.\n";
                    add_to_log($course->id, 'forum', 'mail digest error', '', '', $cm->id, $userto->id);
                } else {
                    mtrace("success.");
                    $usermailcount++;

                    // Mark post as read if forum_usermarksread is set off
                    forum_tp_mark_posts_read($userto, $userto->markposts);
                }
            }
        }
    /// We have finishied all digest emails, update $CFG->digestmailtimelast
        set_config('digestmailtimelast', $timenow);
    }

    cron_setup_user();

    if (!empty($usermailcount)) {
        mtrace(get_string('digestsentusers', 'forum', $usermailcount));
    }

    if (!empty($CFG->forum_lastreadclean)) {
        $timenow = time();
        if ($CFG->forum_lastreadclean + (24*3600) < $timenow) {
            set_config('forum_lastreadclean', $timenow);
            mtrace('Removing old forum read tracking info...');
            forum_tp_clean_read_records();
        }
    } else {
        set_config('forum_lastreadclean', time());
    }


    return true;
}
Exemple #24
0
function referentiel_cron_taches()
{
    global $CFG, $USER;
    global $DB;
    $cronuser = clone $USER;
    $site = get_site();
    // all users that are subscribed to any post that needs sending
    $users = array();
    // status arrays
    $mailcount = array();
    $errorcount = array();
    // caches
    $tasks = array();
    $courses = array();
    $coursemodules = array();
    $subscribedusers = array();
    // Posts older than 2 days will not be mailed.  This is to avoid the problem where
    // cron has not been running for a long time, and then suddenly people are flooded
    // with mail from the past few weeks or months
    $timenow = time();
    if (NOTIFICATION_DELAI) {
        $endtime = $timenow - $CFG->maxeditingtime;
    } else {
        $endtime = $timenow;
    }
    $starttime = $endtime - NOTIFICATION_INTERVALLE_JOUR * 24 * 3600;
    // Two days earlier
    // JF
    // DEBUG : cron_lib.php :
    mtrace("DEBUT CRON REFERENTIEL TACHES");
    $taches = referentiel_get_unmailed_tasks($starttime, $endtime);
    if ($taches) {
        if (REFERENTIEL_DEBUG) {
            mtrace("\nlib_cron.php :: 1675 :: TACHES\n");
            print_r($taches);
            mtrace("\n");
        }
        // Mark them all now as being mailed.  It's unlikely but possible there
        // might be an error later so that a post is NOT actually mailed out,
        // but since mail isn't crucial, we can accept this risk.  Doing it now
        // prevents the risk of duplicated mails, which is a worse problem.
        if (!referentiel_mark_old_tasks_as_mailed($endtime)) {
            mtrace('Errors occurred while trying to mark some referentiel tasks as being mailed.');
            return false;
            // Don't continue trying to mail them, in case we are in a cron loop
        }
        // checking task validity, and adding users to loop through later
        foreach ($taches as $tid => $task) {
            // DEBUG : cron_lib.php :
            // mtrace('task '.$task->id.' '.$tid);
            if (REFERENTIEL_DEBUG) {
                mtrace("\nlib_cron.php :: 1694 :: TASK\n");
                print_r($task);
                mtrace("\n");
            }
            if (!isset($tasks[$tid])) {
                $tasks[$tid] = $task;
            }
            // cours
            $courseid = $tasks[$tid]->ref_course;
            if (!isset($courses[$courseid])) {
                if ($course = $DB->get_record("course", array("id" => "{$courseid}"))) {
                    $courses[$courseid] = $course;
                } else {
                    mtrace('Could not find course ' . $courseid);
                    unset($tasks[$tid]);
                    continue;
                }
            }
            // modules
            $instanceid = $task->ref_instance;
            if (!isset($coursemodules[$instanceid])) {
                if ($cm = get_coursemodule_from_instance('referentiel', $instanceid, $courseid)) {
                    $coursemodules[$instanceid] = $cm;
                } else {
                    mtrace('./mod/referentiel/lib.php : 1355 :  Could not load course module for referentiel instance ' . $instanceid);
                    unset($tasks[$tid]);
                    continue;
                }
            }
            // caching subscribed students of each task
            $students = array();
            $teachers = referentiel_get_teachers_course($courseid);
            // DEBUG : cron_lib.php :
            if (!isset($subscribedusers[$tid])) {
                // notifier tous les etudiants
                // ICI MODIFIER LA COLLECTE
                // NE PRENDRE QUE LES ETUDIANTS ACCOMPAGNES
                // $students=referentiel_get_students_accompagne($courseid,$task->auteurid);
                $students = referentiel_get_accompagnements_teacher($task->ref_instance, $task->ref_course, $task->auteurid);
                if (empty($students)) {
                    $students = referentiel_get_students_course($courseid, 0, 0, false);
                }
                if ($students) {
                    foreach ($students as $student) {
                        $user = referentiel_get_user($student->userid);
                        if ($user->emailstop) {
                            if (!empty($CFG->forum_logblocked)) {
                                // add_to_log(SITEID, 'referentiel', 'mail blocked', '', '', 0, $user->id);
                            }
                        } else {
                            // this user is subscribed to this notification
                            $subscribedusers[$tid][$student->userid] = $student->userid;
                            // this user is a user we have to process later
                            $users[$student->userid] = $user;
                            if (REFERENTIEL_DEBUG) {
                                mtrace("\nDEBUG :: lib_cron:: 1758 :: \nDESTINATAIRE ETUDIANT ID: " . $user->id . "\n");
                                //print_r($user);
                            }
                        }
                    }
                }
                unset($students);
                // release memory
                if (NOTIFICATION_TACHES_AUX_REFERENTS) {
                    //
                    if ($teachers) {
                        foreach ($teachers as $teacher) {
                            $user = referentiel_get_user($teacher->userid);
                            if ($user->emailstop) {
                                if (!empty($CFG->forum_logblocked)) {
                                    // add_to_log(SITEID, 'referentiel', 'mail blocked', '', '', 0, $teacher->userid);
                                }
                            } else {
                                // this user is subscribed to this notification
                                $subscribedusers[$tid][$user->id] = $user->id;
                                // this user is a user we have to process later
                                $users[$user->id] = $user;
                                // DEBUG : cron_lib.php :
                                if (REFERENTIEL_DEBUG) {
                                    mtrace("\nDEBUG :: lib_cron:: 1782 :: \nDESTINATAIRE TEACHER: " . $user->id . " \n");
                                    //print_r($user);
                                }
                            }
                        }
                    }
                }
                unset($teachers);
                // release memory
                $mailcount[$tid] = 0;
                $errorcount[$tid] = 0;
            }
        }
    }
    if ($users && $tasks) {
        // DEBUG : cron_lib.php :
        if (REFERENTIEL_DEBUG) {
            mtrace("TRAITEMENT DES MESSAGES TACHES\n");
        }
        $urlinfo = parse_url($CFG->wwwroot);
        $hostname = $urlinfo['host'];
        foreach ($users as $userto) {
            @set_time_limit(TIME_LIMIT);
            // terminate if processing of any account takes longer than 2 minutes
            // set this so that the capabilities are cached, and environment matches receiving user
            $USER = $userto;
            // mtrace('./mod/referentiel/lib.php :: Line 253 : Processing user '.$userto->id);
            // init caches
            $userto->viewfullnames = array();
            // $userto->canpost       = array();
            // $userto->markposts     = array();
            $userto->enrolledin = array();
            // reset the caches
            foreach ($coursemodules as $coursemoduleid => $unused) {
                $coursemodules[$coursemoduleid]->cache = new object();
                $coursemodules[$coursemoduleid]->cache->caps = array();
                unset($coursemodules[$coursemoduleid]->uservisible);
            }
            foreach ($tasks as $tid => $task) {
                // Set up the environment for activity, course
                $course = $courses[$task->ref_course];
                $cm =& $coursemodules[$task->ref_instance];
                // Do some checks  to see if we can mail out now
                if (!isset($subscribedusers[$tid][$userto->id])) {
                    continue;
                    // user does not subscribe to this activity
                }
                // Get info about the author user
                if (array_key_exists($task->auteurid, $users)) {
                    // we might know him/her already but we need a really complete user object, so.. get it again!
                    if ($userfrom = $DB->get_record("user", array("id" => "{$task->auteurid}"))) {
                        $users[$task->auteurid] = $userfrom;
                    } else {
                        // error
                        mtrace('Could not find user ' . $task->auteurid);
                        continue;
                    }
                } else {
                    if ($userfrom = $DB->get_record("user", array("id" => "{$task->auteurid}"))) {
                        $users[$userfrom->id] = $userfrom;
                        // fetch only once, we can add it to user list, it will be skipped anyway
                    } else {
                        mtrace('Could not find user ' . $task->auteurid);
                        continue;
                    }
                }
                // setup global $COURSE properly - needed for roles and languages
                // course_setup($course);   // More environment
                // setup global $COURSE properly - needed for roles and languages
                cron_setup_user($userto, $course);
                // Fill caches
                if (!isset($userto->viewfullnames[$tid])) {
                    $modcontext = context_module::instance($cm->id);
                    $userto->viewfullnames[$tid] = has_capability('moodle/site:viewfullnames', $modcontext);
                }
                if (!isset($userfrom->groups[$tid])) {
                    if (!isset($userfrom->groups)) {
                        $userfrom->groups = array();
                        $users[$userfrom->id]->groups = array();
                    }
                    $userfrom->groups[$tid] = groups_get_all_groups($course->id, $userfrom->id, $cm->groupingid);
                    $users[$userfrom->id]->groups[$tid] = $userfrom->groups[$tid];
                }
                // OK so we need to send the email.
                // Does the user want this post in a digest?  If so postpone it for now.
                if ($userto->maildigest > 0) {
                    $queue = new object();
                    $queue->userid = $userto->id;
                    $queue->activiteid = $task->id;
                    $queue->timemodified = $task->date_modif;
                    $queue->type = TYPE_TACHE;
                    // 1
                    if (!$DB->insert_record('referentiel_notification', $queue)) {
                        mtrace("Error: mod/referentiel/lib.php : Line 1510 : Could not queue for digest mail for id {$task->id} to user {$userto->id} ({$userto->email}) .. not trying again.");
                    }
                    continue;
                }
                // Prepare to actually send the post now, and build up the content
                $strreferentielname = get_string('referentiel', 'referentiel') . ': ' . referentiel_get_instance_name($task->ref_instance);
                $cleanactivityname = str_replace('"', "'", strip_tags(format_string($strreferentielname . ' -> ' . $task->type_task)));
                $userfrom->customheaders = array('Precedence: Bulk', 'List-Id: "' . $cleanactivityname . '" <moodle_referentiel_activity_' . $task->id . '@' . $hostname . '>', 'List-Help: ' . $CFG->wwwroot . '/mod/referentiel/task.php?d=' . $task->ref_instance . '&task_id=' . $task->id . '&amp;mode=listtaskall', 'Message-ID: <moodle_referentiel_task_' . $task->id . '@' . $hostname . '>', 'X-Course-Id: ' . $course->id, 'X-Course-Name: ' . format_string($course->fullname, true));
                if (!($cm = get_coursemodule_from_instance('referentiel', $task->ref_instance, $course->id))) {
                    print_error('Course Module ID was incorrect');
                }
                $postsubject = "{$course->shortname}: " . format_string($strreferentielname . ' ' . $task->type_task, true);
                $context = context_module::instance($cm->id);
                $posttext = referentiel_make_mail_text(TYPE_TACHE, $context, $course, $task, $userfrom, $userto);
                $posthtml = referentiel_make_mail_html(TYPE_TACHE, $context, $course, $task, $userfrom, $userto);
                // Send the post now!
                // mtrace('Sending ', '');
                if (!($mailresult = email_to_user($userto, $userfrom, $postsubject, $posttext, $posthtml, '', '', $CFG->forum_replytouser))) {
                    mtrace("Error: Could not send out mail for id {$task->id} to user {$userto->id}" . " ({$userto->email}) .. not trying again.");
                    // add_to_log($course->id, 'referentiel', 'mail error', "task $task->id to $userto->id ($userto->email)","", $cm->id, $userto->id);
                    $errorcount[$tid]++;
                } else {
                    if ($mailresult === 'emailstop') {
                        // should not be reached anymore - see check above
                    } else {
                        $mailcount[$tid]++;
                    }
                }
            }
        }
    }
    if ($tasks) {
        foreach ($tasks as $task) {
            mtrace($mailcount[$task->id] . " users were sent task {$task->id}, {$task->type_task}");
            if ($errorcount[$task->id]) {
                $DB->set_field("referentiel_task", "mailed", "2", array("id" => "{$task->id}"));
            }
        }
    }
    // release some memory
    unset($subscribedusers);
    unset($mailcount);
    unset($errorcount);
    $USER = clone $cronuser;
    // course_setup(SITEID);
    cron_setup_user();
    $sitetimezone = $CFG->timezone;
    // Now see if there are any digest mails waiting to be sent, and if we should send them
    mtrace('Starting digest processing...');
    @set_time_limit(TIME_LIMIT * 2);
    // terminate if not able to fetch all digests in 5 minutes
    if (!isset($CFG->digesttasktimelast)) {
        // To catch the first time
        set_config('digesttasktimelast', 0);
    }
    $timenow = time();
    $digesttime = usergetmidnight($timenow, $sitetimezone) + $CFG->digestmailtime * 3600;
    // Delete any really old ones (normally there shouldn't be any)
    $weekago = $timenow - 7 * 24 * 3600;
    $DB->delete_records_select('referentiel_notification', "timemodified < {$weekago} AND type='" . TYPE_TACHE . "'", NULL);
    mtrace('Cleaned old digest records');
    if ($CFG->digesttasktimelast < $digesttime and $timenow > $digesttime) {
        mtrace("Sending task digests: " . userdate($timenow, '', $sitetimezone));
        $params = array("digesttime" => "{$digesttime}", "type" => TYPE_TACHE);
        $sql = " (timemodified < :digesttime) AND (type=:type) ";
        $digestposts_rs = $DB->get_recordset_select('referentiel_notification', $sql, $params);
        // if (!rs_EOF($digestposts_rs)) {     // deprecated
        if ($digestposts_rs->valid()) {
            // We have work to do
            $usermailcount = 0;
            //caches - reuse the those filled before too
            $userposts = array();
            //             while ($digestpost = rs_fetch_next_record($digestposts_rs)) {
            foreach ($digestposts_rs as $digestpost) {
                if (!isset($users[$digestpost->userid])) {
                    if ($user = $DB->get_record("user", array("id" => "{$digestpost->userid}"))) {
                        $users[$digestpost->userid] = $user;
                    } else {
                        continue;
                    }
                }
                $postuser = $users[$digestpost->userid];
                if ($postuser->emailstop) {
                    if (!empty($CFG->forum_logblocked)) {
                        // add_to_log(SITEID, 'referentiel', 'mail blocked', '', '', 0, $postuser->id);
                    }
                    continue;
                }
                // contenu activite
                if (!isset($taches[$digestpost->activiteid])) {
                    if ($tache = $DB->get_record("referentiel_task", array("id" => "{$digestpost->activiteid}"))) {
                        $taches[$digestpost->activiteid] = $tache;
                    } else {
                        continue;
                    }
                }
                $courseid = $taches[$digestpost->activiteid]->ref_course;
                if (!isset($courses[$courseid])) {
                    if ($course = $DB->get_record("course", array("id" => "{$courseid}"))) {
                        $courses[$courseid] = $course;
                    } else {
                        continue;
                    }
                }
                if (!isset($coursemodules[$taches[$digestpost->activiteid]->ref_instance]) && $taches[$digestpost->activiteid]) {
                    if ($cm = get_coursemodule_from_instance('referentiel', $taches[$digestpost->activiteid]->ref_instance, $courseid)) {
                        $coursemodules[$taches[$digestpost->activiteid]->ref_instance] = $cm;
                    } else {
                        continue;
                    }
                }
                $userposts[$digestpost->userid][$digestpost->activiteid] = $digestpost->activiteid;
            }
            $digestposts_rs->close();
            /// Finished iteration, let's close the resultset
            // Data collected, start sending out emails to each user
            // foreach ($userdiscussions as $userid => $thesediscussions) {
            foreach ($userposts as $userid => $theseactivities) {
                @set_time_limit(TIME_LIMIT);
                // terminate if processing of any account takes longer than 2 minutes
                $USER = $cronuser;
                // course_setup(SITEID); // reset cron user language, theme and timezone settings
                cron_setup_user();
                mtrace(get_string('processingdigest', 'referentiel', $userid), '... ');
                // First of all delete all the queue entries for this user
                $DB->delete_records_select('referentiel_notification', "userid = {$userid} AND timemodified < {$digesttime} AND type='" . TYPE_TACHE . "'", NULL);
                $userto = $users[$userid];
                // Override the language and timezone of the "current" user, so that
                // mail is customised for the receiver.
                $USER = $userto;
                // course_setup(SITEID);
                cron_setup_user();
                // init caches
                $userto->viewfullnames = array();
                // $userto->canpost       = array();
                // $userto->markposts     = array();
                $postsubject = get_string('digestmailsubject', 'referentiel', format_string($site->shortname, true));
                $headerdata = new object();
                $headerdata->sitename = format_string($site->fullname, true);
                $headerdata->userprefs = $CFG->wwwroot . '/user/edit.php?id=' . $userid . '&amp;course=' . $site->id;
                $posttext = get_string('digestmailheader', 'referentiel', $headerdata) . "\n\n";
                $headerdata->userprefs = '<a target="_blank" href="' . $headerdata->userprefs . '">' . get_string('digestmailprefs', 'referentiel') . '</a>';
                $posthtml = "<head>";
                /*
                                foreach ($CFG->stylesheets as $stylesheet) {
                                    $posthtml .= '<link rel="stylesheet" type="text/css" href="'.$stylesheet.'" />'."\n";
                                }
                */
                $posthtml .= "</head>\n<body id=\"email\">\n";
                $posthtml .= '<p>' . get_string('digestmailheader', 'referentiel', $headerdata) . '</p><br /><hr size="1" noshade="noshade" />';
                foreach ($theseactivities as $tid) {
                    @set_time_limit(TIME_LIMIT);
                    // to be reset for each post
                    $type_notification = TYPE_TACHE;
                    $tache = $taches[$tid];
                    $course = $courses[$taches[$tid]->ref_course];
                    $cm = $coursemodules[$taches[$tid]->ref_instance];
                    //override language
                    // course_setup($course);
                    // setup global $COURSE properly - needed for roles and languages
                    cron_setup_user($userto, $course);
                    // Fill caches
                    if (!isset($userto->viewfullnames[$tache->id])) {
                        $modcontext = context_module::instance($cm->id);
                        $userto->viewfullnames[$tache->id] = has_capability('moodle/site:viewfullnames', $modcontext);
                    }
                    $strreferentiels = get_string('referentiels', 'referentiel');
                    $posttext .= "\n \n";
                    $posttext .= '=====================================================================';
                    $posttext .= "\n \n";
                    $posttext .= "{$course->shortname} -> {$strreferentiels} -> " . format_string($tache->type_task, true);
                    $posttext .= "\n";
                    $posthtml .= "<p><font face=\"sans-serif\">" . "<a target=\"_blank\" href=\"{$CFG->wwwroot}/course/view.php?id={$course->id}\">{$course->shortname}</a> -> " . "<a target=\"_blank\" href=\"{$CFG->wwwroot}/mod/referentiel/index.php?id={$course->id}\">{$strreferentiels}</a> -> " . "<a target=\"_blank\" href=\"{$CFG->wwwroot}/mod/referentiel/tache.php?id={$tache->ref_instance}&activite_id={$tache->id}\">" . format_string($tache->type_task, true) . "</a>";
                    $posthtml .= "</font></p>";
                    $posthtml .= '<p>';
                    //$postsarray = $userposts[$tache->id];
                    $postsarray = $userposts[$userid];
                    sort($postsarray);
                    foreach ($postsarray as $activiteid) {
                        $post = $taches[$activiteid];
                        if (array_key_exists($post->auteurid, $users)) {
                            // we might know him/her already
                            $userfrom = $users[$post->auteurid];
                        } else {
                            if ($userfrom = $DB->get_record("user", array("id" => "{$post->auteurid}"))) {
                                $users[$userfrom->id] = $userfrom;
                                // fetch only once, we can add it to user list, it will be skipped anyway
                            } else {
                                mtrace('Could not find user ' . $post->auteurid);
                                continue;
                            }
                        }
                        if (!isset($userfrom->groups[$post->id])) {
                            if (!isset($userfrom->groups)) {
                                $userfrom->groups = array();
                                $users[$userfrom->id]->groups = array();
                            }
                            $userfrom->groups[$post->id] = groups_get_all_groups($course->id, $userfrom->id, $cm->groupingid);
                            $users[$userfrom->id]->groups[$post->id] = $userfrom->groups[$post->id];
                        }
                        $userfrom->customheaders = array("Precedence: Bulk");
                        if ($userto->maildigest == 2) {
                            // Subjects only
                            $by = new object();
                            $by->name = fullname($userfrom);
                            $by->date = userdate($post->date_modif);
                            $posttext .= "\n" . format_string($post->type_task, true) . ' ' . get_string("bynameondate", "referentiel", $by);
                            $posttext .= "\n---------------------------------------------------------------------";
                            $by->name = "<a target=\"_blank\" href=\"{$CFG->wwwroot}/user/view.php?id={$userfrom->id}&amp;course={$course->id}\">{$by->name}</a>";
                            $posthtml .= '<div><a target="_blank" href="' . $CFG->wwwroot . '/mod/referentiel/task.php?d=' . $post->ref_instance . '&activite_id=' . $post->id . '">' . format_string($post->type_activite, true) . '</a> ' . get_string("bynameondate", "referentiel", $by) . '</div>';
                        } else {
                            $context = context_module::instance($cm->id);
                            $posttext = referentiel_make_mail_text(TYPE_TACHE, $context, $course, $post, $userfrom, $userto, true);
                            $posthtml = referentiel_make_mail_post(TYPE_TACHE, $context, $course, $post, $userfrom, $userto, false, true, false);
                        }
                    }
                    $posthtml .= '<hr size="1" noshade="noshade" /></p>';
                }
                $posthtml .= '</body>';
                if ($userto->mailformat != 1) {
                    // This user DOESN'T want to receive HTML
                    $posthtml = '';
                }
                if (!($mailresult = email_to_user($userto, $site->shortname, $postsubject, $posttext, $posthtml, '', '', $CFG->forum_replytouser))) {
                    mtrace("ERROR!");
                    mtrace("Error: Could not send out digest mail to user {$userto->id} ({$userto->email})... not trying again.\n");
                    // add_to_log($course->id, 'referentiel', 'mail digest error', '', '', $cm->id, $userto->id);
                } else {
                    if ($mailresult === 'emailstop') {
                        // should not happen anymore - see check above
                    } else {
                        mtrace("success.");
                        $usermailcount++;
                    }
                }
            }
        }
        /// We have finishied all digest emails activities, update $CFG->digestactivitytimelast
        set_config('digesttasktimelast', $timenow);
    }
    $USER = $cronuser;
    // course_setup(SITEID); // reset cron user language, theme and timezone settings
    cron_setup_user();
    if (!empty($usermailcount)) {
        mtrace(get_string('digestsentusers', 'referentiel', $usermailcount));
    }
    mtrace("FIN CRON REFERENTIEL TACHE.\n");
    return true;
}