/** * Do the job. * Throw exceptions on errors (the job will be retried). */ public function execute() { global $CFG; $timenow = time(); // 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 ($timenow > $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(); } } } \core_php_time_limit::raise(); } else { mtrace('Next stats run after:' . userdate($timetocheck)); } } }
/** * Tests for basic use with simple numeric progress. */ public function test_basic() { $progress = new core_mock_progress(); // Check values of empty progress things. $this->assertFalse($progress->is_in_progress_section()); // Start progress counting, check basic values and check that update // gets called. $progress->start_progress('hello', 10); $this->assertTrue($progress->was_update_called()); $this->assertTrue($progress->is_in_progress_section()); $this->assertEquals('hello', $progress->get_current_description()); // Check numeric position and indeterminate count. $this->assert_min_max(0.0, 0.0, $progress); $this->assertEquals(0, $progress->get_progress_count()); // Make some progress and check that the time limit gets added. $progress->step_time(); core_php_time_limit::get_and_clear_unit_test_data(); $progress->progress(2); $this->assertTrue($progress->was_update_called()); $this->assertEquals(array(\core\progress\base::TIME_LIMIT_WITHOUT_PROGRESS), core_php_time_limit::get_and_clear_unit_test_data()); // Check the new value. $this->assert_min_max(0.2, 0.2, $progress); // Do another progress run at same time, it should be ignored. $progress->progress(3); $this->assertFalse($progress->was_update_called()); $this->assert_min_max(0.3, 0.3, $progress); // End the section. This should cause an update. $progress->end_progress(); $this->assertTrue($progress->was_update_called()); // Because there are no sections left open, it thinks we finished. $this->assert_min_max(1.0, 1.0, $progress); // There was 1 progress call. $this->assertEquals(1, $progress->get_progress_count()); }
/** * Initiate database transfer. * @param moodle_database $sourcedb * @param moodle_database $targetdb * @param progress_trace $feedback * @return void */ function tool_dbtransfer_transfer_database(moodle_database $sourcedb, moodle_database $targetdb, progress_trace $feedback = null) { core_php_time_limit::raise(); \core\session\manager::write_close(); // Release session. $var = new database_mover($sourcedb, $targetdb, true, $feedback); $var->export_database(null); tool_dbtransfer_rebuild_target_log_actions($targetdb, $feedback); }
protected function prevent_timeout() { core_php_time_limit::raise(300); if ($this->doingbackup) { return; } echo '.'; $this->dotcounter += 1; if ($this->dotcounter % 100 == 0) { echo '<br />'; } }
/** * Do the job. * Throw exceptions on errors (the job will be retried). */ public function execute() { global $CFG; // 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'; // 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(); } } } \core_php_time_limit::raise(); } }
public function test_create_instance() { global $DB, $USER; core_php_time_limit::raise(); $this->resetAfterTest(); $this->setAdminUser(); $course = $this->getDataGenerator()->create_course(); // There should not be any module for that course first $this->assertFalse($DB->record_exists('ratingallocate', array('course' => $course->id))); $records = $DB->get_records('ratingallocate_choices', array(), 'id'); $this->assertEquals(0, count($records)); // create activity $mod = $this->getDataGenerator()->create_module('ratingallocate', array('course' => $course)); $records = $DB->get_records('ratingallocate', array('course' => $course->id), 'id'); $this->assertEquals(1, count($records)); $this->assertTrue(array_key_exists($mod->id, $records)); $expected_values_db = array('id' => $mod->id, 'course' => $course->id, 'name' => 'Rating Allocation', 'intro' => 'Test ratingallocate 1', 'introformat' => '0', 'timecreated' => reset($records)->{'timecreated'}, 'timemodified' => '0', 'accesstimestart' => reset($records)->{'accesstimestart'}, 'accesstimestop' => reset($records)->{'accesstimestop'}, 'setting' => '{"strategy_yesno":{"maxcrossout":"1"}}', 'strategy' => 'strategy_yesno', 'publishdate' => reset($records)->{'publishdate'}, 'published' => '0', 'notificationsend' => '0', 'algorithmstarttime' => null, 'algorithmstatus' => '0', 'runalgorithmbycron' => '1'); $this->assertEquals(json_decode(json_encode($expected_values_db, false)), reset($records)); // must have two choices $records = $DB->get_records('ratingallocate_choices', array('ratingallocateid' => $mod->id), 'title'); $this->assertEquals(2, count($records)); $choice_ids = array_keys($records); $expected_choices = array($choice_ids[0] => (object) array('title' => 'Choice 1', 'id' => $choice_ids[0], 'ratingallocateid' => $mod->id, 'explanation' => 'Some explanatory text for choice 1', 'maxsize' => '10', 'active' => '1'), $choice_ids[1] => (object) array('title' => 'Choice 2', 'id' => $choice_ids[1], 'ratingallocateid' => $mod->id, 'explanation' => 'Some explanatory text for choice 2', 'maxsize' => '5', 'active' => '0')); $this->assertEquals($expected_choices, $records); // Create an other mod_ratingallocate within the course $params = array('course' => $course->id, 'name' => 'Another mod_ratingallocate'); $mod = $this->getDataGenerator()->create_module('ratingallocate', $params); $records = $DB->get_records('ratingallocate', array('course' => $course->id), 'id'); // are there 2 modules within the course $this->assertEquals(2, count($records)); // is the name correct $this->assertEquals('Another mod_ratingallocate', $records[$mod->id]->name); $records = $DB->get_records('ratingallocate_choices', array(), 'id'); $this->assertEquals(4, count($records)); // other tables $records = $DB->get_records('ratingallocate_ratings', array(), 'id'); $this->assertEquals(0, count($records)); $records = $DB->get_records('ratingallocate_allocations', array(), 'id'); $this->assertEquals(0, count($records)); }
/** * Runs the automated backups if required * * @global moodle_database $DB */ public static function run_automated_backup($rundirective = self::RUN_ON_SCHEDULE) { global $CFG, $DB; $status = true; $emailpending = false; $now = time(); $config = get_config('backup'); mtrace("Checking automated backup status", '...'); $state = backup_cron_automated_helper::get_automated_backup_state($rundirective); if ($state === backup_cron_automated_helper::STATE_DISABLED) { mtrace('INACTIVE'); return $state; } else { if ($state === backup_cron_automated_helper::STATE_RUNNING) { mtrace('RUNNING'); if ($rundirective == self::RUN_IMMEDIATELY) { mtrace('Automated backups are already running. If this script is being run by cron this constitues an error. You will need to increase the time between executions within cron.'); } else { mtrace("automated backup are already running. Execution delayed"); } return $state; } else { mtrace('OK'); } } backup_cron_automated_helper::set_state_running(); mtrace("Getting admin info"); $admin = get_admin(); if (!$admin) { mtrace("Error: No admin account was found"); $state = false; } if ($status) { mtrace("Checking courses"); mtrace("Skipping deleted courses", '...'); mtrace(sprintf("%d courses", backup_cron_automated_helper::remove_deleted_courses_from_schedule())); } if ($status) { mtrace('Running required automated backups...'); cron_trace_time_and_memory(); // This could take a while! core_php_time_limit::raise(); raise_memory_limit(MEMORY_EXTRA); $nextstarttime = backup_cron_automated_helper::calculate_next_automated_backup($admin->timezone, $now); $showtime = "undefined"; if ($nextstarttime > 0) { $showtime = date('r', $nextstarttime); } $rs = $DB->get_recordset('course'); foreach ($rs as $course) { $backupcourse = $DB->get_record('backup_courses', array('courseid' => $course->id)); if (!$backupcourse) { $backupcourse = new stdClass(); $backupcourse->courseid = $course->id; $backupcourse->laststatus = self::BACKUP_STATUS_NOTYETRUN; $DB->insert_record('backup_courses', $backupcourse); $backupcourse = $DB->get_record('backup_courses', array('courseid' => $course->id)); } // The last backup is considered as successful when OK or SKIPPED. $lastbackupwassuccessful = ($backupcourse->laststatus == self::BACKUP_STATUS_SKIPPED || $backupcourse->laststatus == self::BACKUP_STATUS_OK) && ($backupcourse->laststarttime > 0 && $backupcourse->lastendtime > 0); // Assume that we are not skipping anything. $skipped = false; $skippedmessage = ''; // Check if we are going to be running the backup now. $shouldrunnow = $backupcourse->nextstarttime > 0 && $backupcourse->nextstarttime < $now || $rundirective == self::RUN_IMMEDIATELY; // If config backup_auto_skip_hidden is set to true, skip courses that are not visible. if ($shouldrunnow && $config->backup_auto_skip_hidden) { $skipped = $config->backup_auto_skip_hidden && !$course->visible; $skippedmessage = 'Not visible'; } // If config backup_auto_skip_modif_days is set to true, skip courses // that have not been modified since the number of days defined. if ($shouldrunnow && !$skipped && $lastbackupwassuccessful && $config->backup_auto_skip_modif_days) { $timenotmodifsincedays = $now - $config->backup_auto_skip_modif_days * DAYSECS; // Check log if there were any modifications to the course content. $logexists = self::is_course_modified($course->id, $timenotmodifsincedays); $skipped = $course->timemodified <= $timenotmodifsincedays && !$logexists; $skippedmessage = 'Not modified in the past ' . $config->backup_auto_skip_modif_days . ' days'; } // If config backup_auto_skip_modif_prev is set to true, skip courses // that have not been modified since previous backup. if ($shouldrunnow && !$skipped && $lastbackupwassuccessful && $config->backup_auto_skip_modif_prev) { // Check log if there were any modifications to the course content. $logexists = self::is_course_modified($course->id, $backupcourse->laststarttime); $skipped = $course->timemodified <= $backupcourse->laststarttime && !$logexists; $skippedmessage = 'Not modified since previous backup'; } // Check if the course is not scheduled to run right now. if (!$shouldrunnow) { $backupcourse->nextstarttime = $nextstarttime; $DB->update_record('backup_courses', $backupcourse); mtrace('Skipping ' . $course->fullname . ' (Not scheduled for backup until ' . $showtime . ')'); } else { if ($skipped) { // Must have been skipped for a reason. $backupcourse->laststatus = self::BACKUP_STATUS_SKIPPED; $backupcourse->nextstarttime = $nextstarttime; $DB->update_record('backup_courses', $backupcourse); mtrace('Skipping ' . $course->fullname . ' (' . $skippedmessage . ')'); mtrace('Backup of \'' . $course->fullname . '\' is scheduled on ' . $showtime); } else { // Backup every non-skipped courses. mtrace('Backing up ' . $course->fullname . '...'); // We have to send an email because we have included at least one backup. $emailpending = true; // Only make the backup if laststatus isn't 2-UNFINISHED (uncontrolled error). if ($backupcourse->laststatus != self::BACKUP_STATUS_UNFINISHED) { // Set laststarttime. $starttime = time(); $backupcourse->laststarttime = time(); $backupcourse->laststatus = self::BACKUP_STATUS_UNFINISHED; $DB->update_record('backup_courses', $backupcourse); $backupcourse->laststatus = backup_cron_automated_helper::launch_automated_backup($course, $backupcourse->laststarttime, $admin->id); $backupcourse->lastendtime = time(); $backupcourse->nextstarttime = $nextstarttime; $DB->update_record('backup_courses', $backupcourse); if ($backupcourse->laststatus === self::BACKUP_STATUS_OK) { // Clean up any excess course backups now that we have // taken a successful backup. $removedcount = backup_cron_automated_helper::remove_excess_backups($course); } } mtrace("complete - next execution: {$showtime}"); } } } $rs->close(); } //Send email to admin if necessary if ($emailpending) { mtrace("Sending email to admin"); $message = ""; $count = backup_cron_automated_helper::get_backup_status_array(); $haserrors = $count[self::BACKUP_STATUS_ERROR] != 0 || $count[self::BACKUP_STATUS_UNFINISHED] != 0; // Build the message text. // Summary. $message .= get_string('summary') . "\n"; $message .= "==================================================\n"; $message .= ' ' . get_string('courses') . '; ' . array_sum($count) . "\n"; $message .= ' ' . get_string('ok') . '; ' . $count[self::BACKUP_STATUS_OK] . "\n"; $message .= ' ' . get_string('skipped') . '; ' . $count[self::BACKUP_STATUS_SKIPPED] . "\n"; $message .= ' ' . get_string('error') . '; ' . $count[self::BACKUP_STATUS_ERROR] . "\n"; $message .= ' ' . get_string('unfinished') . '; ' . $count[self::BACKUP_STATUS_UNFINISHED] . "\n"; $message .= ' ' . get_string('warning') . '; ' . $count[self::BACKUP_STATUS_WARNING] . "\n"; $message .= ' ' . get_string('backupnotyetrun') . '; ' . $count[self::BACKUP_STATUS_NOTYETRUN] . "\n\n"; //Reference if ($haserrors) { $message .= " " . get_string('backupfailed') . "\n\n"; $dest_url = "{$CFG->wwwroot}/report/backups/index.php"; $message .= " " . get_string('backuptakealook', '', $dest_url) . "\n\n"; //Set message priority $admin->priority = 1; //Reset unfinished to error $DB->set_field('backup_courses', 'laststatus', '0', array('laststatus' => '2')); } else { $message .= " " . get_string('backupfinished') . "\n"; } //Build the message subject $site = get_site(); $prefix = format_string($site->shortname, true, array('context' => context_course::instance(SITEID))) . ": "; if ($haserrors) { $prefix .= "[" . strtoupper(get_string('error')) . "] "; } $subject = $prefix . get_string('automatedbackupstatus', 'backup'); //Send the message $eventdata = new stdClass(); $eventdata->modulename = 'moodle'; $eventdata->userfrom = $admin; $eventdata->userto = $admin; $eventdata->subject = $subject; $eventdata->fullmessage = $message; $eventdata->fullmessageformat = FORMAT_PLAIN; $eventdata->fullmessagehtml = ''; $eventdata->smallmessage = ''; $eventdata->component = 'moodle'; $eventdata->name = 'backup'; message_send($eventdata); } //Everything is finished stop backup_auto_running backup_cron_automated_helper::set_state_running(false); mtrace('Automated backups complete.'); return $status; }
/** * Moved from admin/replace.php so that we can use this in cron * * @param string $search string to look for * @param string $replace string to replace * @return bool success or fail */ function db_replace($search, $replace) { global $DB, $CFG, $OUTPUT; // TODO: this is horrible hack, we should do whitelisting and each plugin should be responsible for proper replacing... $skiptables = array('config', 'config_plugins', 'config_log', 'upgrade_log', 'log', 'filter_config', 'sessions', 'events_queue', 'repository_instance_config', 'block_instances', ''); // Turn off time limits, sometimes upgrades can be slow. core_php_time_limit::raise(); if (!($tables = $DB->get_tables())) { // No tables yet at all. return false; } foreach ($tables as $table) { if (in_array($table, $skiptables)) { // Don't process these continue; } if ($columns = $DB->get_columns($table)) { $DB->set_debug(true); foreach ($columns as $column) { $DB->replace_all_text($table, $column, $search, $replace); } $DB->set_debug(false); } } // delete modinfo caches rebuild_course_cache(0, true); // TODO: we should ask all plugins to do the search&replace, for now let's do only blocks... $blocks = core_component::get_plugin_list('block'); foreach ($blocks as $blockname => $fullblock) { if ($blockname === 'NEWBLOCK') { // Someone has unzipped the template, ignore it continue; } if (!is_readable($fullblock . '/lib.php')) { continue; } $function = 'block_' . $blockname . '_global_db_replace'; include_once $fullblock . '/lib.php'; if (!function_exists($function)) { continue; } echo $OUTPUT->notification("Replacing in {$blockname} blocks...", 'notifysuccess'); $function($search, $replace); echo $OUTPUT->notification("...finished", 'notifysuccess'); } purge_all_caches(); return true; }
public function test_simple() { global $DB, $USER; core_php_time_limit::raise(); $this->resetAfterTest(); $this->setAdminUser(); $course = $this->getDataGenerator()->create_course(); $teacher = mod_ratingallocate_generator::create_user_and_enrol($this, $course, true); $this->setUser($teacher); // There should not be any module for that course first $this->assertFalse($DB->record_exists(this_db\ratingallocate::TABLE, array(this_db\ratingallocate::COURSE => $course->id))); //set default data for category $data = mod_ratingallocate_generator::get_default_values(); $data['course'] = $course; foreach ($data as $name => $value) { if (subStr($name, strlen($name) - 7, 7) === 'maxsize') { $data[$name] = 2; } if (subStr($name, strlen($name) - 6, 6) === 'active') { $data[$name] = true; } } // create activity $mod = $this->getDataGenerator()->create_module(ratingallocate_MOD_NAME, $data); $this->assertEquals(2, $DB->count_records(this_db\ratingallocate_choices::TABLE), array(this_db\ratingallocate_choices::ID => $mod->id)); $student_1 = mod_ratingallocate_generator::create_user_and_enrol($this, $course); $student_2 = mod_ratingallocate_generator::create_user_and_enrol($this, $course); $student_3 = mod_ratingallocate_generator::create_user_and_enrol($this, $course); $student_4 = mod_ratingallocate_generator::create_user_and_enrol($this, $course); $ratingallocate = mod_ratingallocate_generator::get_ratingallocate_for_user($this, $mod, $teacher); $choices = $ratingallocate->get_rateable_choices(); $choice1 = reset($choices); $choice2 = end($choices); //Create preferences $prefers_non = array(); foreach ($choices as $choice) { $prefers_non[$choice->{this_db\ratingallocate_choices::ID}] = array(this_db\ratingallocate_ratings::CHOICEID => $choice->{this_db\ratingallocate_choices::ID}, this_db\ratingallocate_ratings::RATING => 0); } $prefers_first = json_decode(json_encode($prefers_non), true); $prefers_first[$choice1->{this_db\ratingallocate_choices::ID}][this_db\ratingallocate_ratings::RATING] = true; $prefers_second = json_decode(json_encode($prefers_non), true); $prefers_second[$choice2->{this_db\ratingallocate_choices::ID}][this_db\ratingallocate_ratings::RATING] = true; //assign preferences mod_ratingallocate_generator::save_rating_for_user($this, $mod, $student_1, $prefers_first); mod_ratingallocate_generator::save_rating_for_user($this, $mod, $student_2, $prefers_first); mod_ratingallocate_generator::save_rating_for_user($this, $mod, $student_3, $prefers_second); mod_ratingallocate_generator::save_rating_for_user($this, $mod, $student_4, $prefers_second); // allocate choices $ratingallocate = mod_ratingallocate_generator::get_ratingallocate_for_user($this, $mod, $teacher); $time_needed = $ratingallocate->distrubute_choices(); $this->assertGreaterThan(0, $time_needed); $this->assertLessThan(0.1, $time_needed, 'Allocation is very slow'); $allocation_count = $ratingallocate->get_choices_with_allocationcount(); $this->assertCount(2, $allocation_count); //Test allocations $num_allocations = $DB->count_records(this_db\ratingallocate_allocations::TABLE); $this->assertEquals(4, $num_allocations, 'There should be only 4 allocations, since there are only 4 choices.'); $allocations = $DB->get_records(this_db\ratingallocate_allocations::TABLE, array(this_db\ratingallocate_allocations::RATINGALLOCATEID => $mod->{this_db\ratingallocate::ID}), ''); // '' /*sort*/, /*fields*/ this_db\ratingallocate_allocations::USERID . ',' . this_db\ratingallocate_allocations::CHOICEID ); $map_user_id = function ($elem) { return $elem->{this_db\ratingallocate_allocations::USERID}; }; $alloc1 = self::filter_allocations_by_choice($allocations, $choice1->{this_db\ratingallocate_choices::ID}); $alloc2 = self::filter_allocations_by_choice($allocations, $choice2->{this_db\ratingallocate_choices::ID}); //Assert, that student 1 was allocated to choice 1 $this->assertContains($student_1->id, array_map($map_user_id, $alloc1)); //Assert, that student 2 was allocated to choice 1 $this->assertContains($student_2->id, array_map($map_user_id, $alloc1)); //Assert, that student 3 was allocated to choice 2 $this->assertContains($student_3->id, array_map($map_user_id, $alloc2)); //Assert, that student 4 was allocated to choice 2 $this->assertContains($student_4->id, array_map($map_user_id, $alloc2)); }
/** * Download a zip file of all assignment submissions. * * @return string - If an error occurs, this will contain the error page. */ protected function download_submissions() { global $CFG, $DB; // More efficient to load this here. require_once $CFG->libdir . '/filelib.php'; // Increase the server timeout to handle the creation and sending of large zip files. core_php_time_limit::raise(); $this->require_view_grades(); // Load all users with submit. $students = get_enrolled_users($this->context, "mod/assign:submit", null, 'u.*', null, null, null, $this->show_only_active_users()); // Build a list of files to zip. $filesforzipping = array(); $fs = get_file_storage(); $groupmode = groups_get_activity_groupmode($this->get_course_module()); // All users. $groupid = 0; $groupname = ''; if ($groupmode) { $groupid = groups_get_activity_group($this->get_course_module(), true); $groupname = groups_get_group_name($groupid) . '-'; } // Construct the zip file name. $filename = clean_filename($this->get_course()->shortname . '-' . $this->get_instance()->name . '-' . $groupname . $this->get_course_module()->id . '.zip'); // Get all the files for each student. foreach ($students as $student) { $userid = $student->id; if (groups_is_member($groupid, $userid) or !$groupmode or !$groupid) { // Get the plugins to add their own files to the zip. $submissiongroup = false; $groupname = ''; if ($this->get_instance()->teamsubmission) { $submission = $this->get_group_submission($userid, 0, false); $submissiongroup = $this->get_submission_group($userid); if ($submissiongroup) { $groupname = $submissiongroup->name . '-'; } else { $groupname = get_string('defaultteam', 'assign') . '-'; } } else { $submission = $this->get_user_submission($userid, false); } if ($this->is_blind_marking()) { $prefix = str_replace('_', ' ', $groupname . get_string('participant', 'assign')); $prefix = clean_filename($prefix . '_' . $this->get_uniqueid_for_user($userid) . '_'); } else { $prefix = str_replace('_', ' ', $groupname . fullname($student)); $prefix = clean_filename($prefix . '_' . $this->get_uniqueid_for_user($userid) . '_'); } if ($submission) { foreach ($this->submissionplugins as $plugin) { if ($plugin->is_enabled() && $plugin->is_visible()) { $pluginfiles = $plugin->get_files($submission, $student); foreach ($pluginfiles as $zipfilename => $file) { $subtype = $plugin->get_subtype(); $type = $plugin->get_type(); $prefixedfilename = clean_filename($prefix . $subtype . '_' . $type . '_' . $zipfilename); $filesforzipping[$prefixedfilename] = $file; } } } } } } $result = ''; if (count($filesforzipping) == 0) { $header = new assign_header($this->get_instance(), $this->get_context(), '', $this->get_course_module()->id, get_string('downloadall', 'assign')); $result .= $this->get_renderer()->render($header); $result .= $this->get_renderer()->notification(get_string('nosubmission', 'assign')); $url = new moodle_url('/mod/assign/view.php', array('id' => $this->get_course_module()->id, 'action' => 'grading')); $result .= $this->get_renderer()->continue_button($url); $result .= $this->view_footer(); } else { if ($zipfile = $this->pack_files($filesforzipping)) { \mod_assign\event\all_submissions_downloaded::create_from_assign($this)->trigger(); // Send file and delete after sending. send_temp_file($zipfile, $filename); // We will not get here - send_temp_file calls exit. } } return $result; }
/** * Send requested byterange of file. * * @param resource $handle A file handle * @param string $mimetype The mimetype for the output * @param array $ranges An array of ranges to send * @param string $filesize The size of the content if only one range is used */ function byteserving_send_file($handle, $mimetype, $ranges, $filesize) { // better turn off any kind of compression and buffering ini_set('zlib.output_compression', 'Off'); $chunksize = 1 * (1024 * 1024); // 1MB chunks - must be less than 2MB! if ($handle === false) { die; } if (count($ranges) == 1) { //only one range requested $length = $ranges[0][2] - $ranges[0][1] + 1; header('HTTP/1.1 206 Partial content'); header('Content-Length: ' . $length); header('Content-Range: bytes ' . $ranges[0][1] . '-' . $ranges[0][2] . '/' . $filesize); header('Content-Type: ' . $mimetype); while (@ob_get_level()) { if (!@ob_end_flush()) { break; } } fseek($handle, $ranges[0][1]); while (!feof($handle) && $length > 0) { core_php_time_limit::raise(60 * 60); //reset time limit to 60 min - should be enough for 1 MB chunk $buffer = fread($handle, $chunksize < $length ? $chunksize : $length); echo $buffer; flush(); $length -= strlen($buffer); } fclose($handle); die; } else { // multiple ranges requested - not tested much $totallength = 0; foreach ($ranges as $range) { $totallength += strlen($range[0]) + $range[2] - $range[1] + 1; } $totallength += strlen("\r\n--" . BYTESERVING_BOUNDARY . "--\r\n"); header('HTTP/1.1 206 Partial content'); header('Content-Length: ' . $totallength); header('Content-Type: multipart/byteranges; boundary=' . BYTESERVING_BOUNDARY); while (@ob_get_level()) { if (!@ob_end_flush()) { break; } } foreach ($ranges as $range) { $length = $range[2] - $range[1] + 1; echo $range[0]; fseek($handle, $range[1]); while (!feof($handle) && $length > 0) { core_php_time_limit::raise(60 * 60); //reset time limit to 60 min - should be enough for 1 MB chunk $buffer = fread($handle, $chunksize < $length ? $chunksize : $length); echo $buffer; flush(); $length -= strlen($buffer); } } echo "\r\n--" . BYTESERVING_BOUNDARY . "--\r\n"; fclose($handle); die; } }
/** * Sync all meta course links. * * @param progress_trace $trace * @param int $courseid one course, empty mean all * @return int 0 means ok, 1 means error, 2 means plugin disabled */ public function sync(progress_trace $trace, $courseid = null) { global $DB; if (!enrol_is_enabled('manual')) { $trace->finished(); return 2; } // Unfortunately this may take a long time, execution can be interrupted safely here. core_php_time_limit::raise(); raise_memory_limit(MEMORY_HUGE); $trace->output('Verifying manual enrolment expiration...'); $params = array('now' => time(), 'useractive' => ENROL_USER_ACTIVE, 'courselevel' => CONTEXT_COURSE); $coursesql = ""; if ($courseid) { $coursesql = "AND e.courseid = :courseid"; $params['courseid'] = $courseid; } // Deal with expired accounts. $action = $this->get_config('expiredaction', ENROL_EXT_REMOVED_KEEP); if ($action == ENROL_EXT_REMOVED_UNENROL) { $instances = array(); $sql = "SELECT ue.*, e.courseid, c.id AS contextid\n FROM {user_enrolments} ue\n JOIN {enrol} e ON (e.id = ue.enrolid AND e.enrol = 'manual')\n JOIN {context} c ON (c.instanceid = e.courseid AND c.contextlevel = :courselevel)\n WHERE ue.timeend > 0 AND ue.timeend < :now\n {$coursesql}"; $rs = $DB->get_recordset_sql($sql, $params); foreach ($rs as $ue) { if (empty($instances[$ue->enrolid])) { $instances[$ue->enrolid] = $DB->get_record('enrol', array('id' => $ue->enrolid)); } $instance = $instances[$ue->enrolid]; // Always remove all manually assigned roles here, this may break enrol_self roles but we do not want hardcoded hacks here. role_unassign_all(array('userid' => $ue->userid, 'contextid' => $ue->contextid, 'component' => '', 'itemid' => 0), true); $this->unenrol_user($instance, $ue->userid); $trace->output("unenrolling expired user {$ue->userid} from course {$instance->courseid}", 1); } $rs->close(); unset($instances); } else { if ($action == ENROL_EXT_REMOVED_SUSPENDNOROLES or $action == ENROL_EXT_REMOVED_SUSPEND) { $instances = array(); $sql = "SELECT ue.*, e.courseid, c.id AS contextid\n FROM {user_enrolments} ue\n JOIN {enrol} e ON (e.id = ue.enrolid AND e.enrol = 'manual')\n JOIN {context} c ON (c.instanceid = e.courseid AND c.contextlevel = :courselevel)\n WHERE ue.timeend > 0 AND ue.timeend < :now\n AND ue.status = :useractive\n {$coursesql}"; $rs = $DB->get_recordset_sql($sql, $params); foreach ($rs as $ue) { if (empty($instances[$ue->enrolid])) { $instances[$ue->enrolid] = $DB->get_record('enrol', array('id' => $ue->enrolid)); } $instance = $instances[$ue->enrolid]; if ($action == ENROL_EXT_REMOVED_SUSPENDNOROLES) { // Remove all manually assigned roles here, this may break enrol_self roles but we do not want hardcoded hacks here. role_unassign_all(array('userid' => $ue->userid, 'contextid' => $ue->contextid, 'component' => '', 'itemid' => 0), true); $this->update_user_enrol($instance, $ue->userid, ENROL_USER_SUSPENDED); $trace->output("suspending expired user {$ue->userid} in course {$instance->courseid}, roles unassigned", 1); } else { $this->update_user_enrol($instance, $ue->userid, ENROL_USER_SUSPENDED); $trace->output("suspending expired user {$ue->userid} in course {$instance->courseid}, roles kept", 1); } } $rs->close(); unset($instances); } else { // ENROL_EXT_REMOVED_KEEP means no changes. } } $trace->output('...manual enrolment updates finished.'); $trace->finished(); return 0; }
/** * Recursively delete category including all subcategories and courses * * Function {@link coursecat::can_delete_full()} MUST be called prior * to calling this function because there is no capability check * inside this function * * @param boolean $showfeedback display some notices * @return array return deleted courses * @throws moodle_exception */ public function delete_full($showfeedback = true) { global $CFG, $DB; require_once $CFG->libdir . '/gradelib.php'; require_once $CFG->libdir . '/questionlib.php'; require_once $CFG->dirroot . '/cohort/lib.php'; // Make sure we won't timeout when deleting a lot of courses. $settimeout = core_php_time_limit::raise(); $deletedcourses = array(); // Get children. Note, we don't want to use cache here because it would be rebuilt too often. $children = $DB->get_records('course_categories', array('parent' => $this->id), 'sortorder ASC'); foreach ($children as $record) { $coursecat = new coursecat($record); $deletedcourses += $coursecat->delete_full($showfeedback); } if ($courses = $DB->get_records('course', array('category' => $this->id), 'sortorder ASC')) { foreach ($courses as $course) { if (!delete_course($course, false)) { throw new moodle_exception('cannotdeletecategorycourse', '', '', $course->shortname); } $deletedcourses[] = $course; } } // Move or delete cohorts in this context. cohort_delete_category($this); // Now delete anything that may depend on course category context. grade_course_category_delete($this->id, 0, $showfeedback); if (!question_delete_course_category($this, 0, $showfeedback)) { throw new moodle_exception('cannotdeletecategoryquestions', '', '', $this->get_formatted_name()); } // Finally delete the category and it's context. $DB->delete_records('course_categories', array('id' => $this->id)); $coursecatcontext = context_coursecat::instance($this->id); $coursecatcontext->delete(); cache_helper::purge_by_event('changesincoursecat'); // Trigger a course category deleted event. /* @var \core\event\course_category_deleted $event */ $event = \core\event\course_category_deleted::create(array('objectid' => $this->id, 'context' => $coursecatcontext, 'other' => array('name' => $this->name))); $event->set_coursecat($this); $event->trigger(); // If we deleted $CFG->defaultrequestcategory, make it point somewhere else. if ($this->id == $CFG->defaultrequestcategory) { set_config('defaultrequestcategory', $DB->get_field('course_categories', 'MIN(id)', array('parent' => 0))); } return $deletedcourses; }
/** * * @param object $glossary * @param string $ignored invalid parameter * @param int|string $hook * @return string */ function glossary_generate_export_file($glossary, $ignored = "", $hook = 0) { global $CFG, $DB; // Large exports are likely to take their time and memory. core_php_time_limit::raise(); raise_memory_limit(MEMORY_EXTRA); $cm = get_coursemodule_from_instance('glossary', $glossary->id, $glossary->course); $context = context_module::instance($cm->id); $co = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"; $co .= glossary_start_tag("GLOSSARY", 0, true); $co .= glossary_start_tag("INFO", 1, true); $co .= glossary_full_tag("NAME", 2, false, $glossary->name); $co .= glossary_full_tag("INTRO", 2, false, $glossary->intro); $co .= glossary_full_tag("INTROFORMAT", 2, false, $glossary->introformat); $co .= glossary_full_tag("ALLOWDUPLICATEDENTRIES", 2, false, $glossary->allowduplicatedentries); $co .= glossary_full_tag("DISPLAYFORMAT", 2, false, $glossary->displayformat); $co .= glossary_full_tag("SHOWSPECIAL", 2, false, $glossary->showspecial); $co .= glossary_full_tag("SHOWALPHABET", 2, false, $glossary->showalphabet); $co .= glossary_full_tag("SHOWALL", 2, false, $glossary->showall); $co .= glossary_full_tag("ALLOWCOMMENTS", 2, false, $glossary->allowcomments); $co .= glossary_full_tag("USEDYNALINK", 2, false, $glossary->usedynalink); $co .= glossary_full_tag("DEFAULTAPPROVAL", 2, false, $glossary->defaultapproval); $co .= glossary_full_tag("GLOBALGLOSSARY", 2, false, $glossary->globalglossary); $co .= glossary_full_tag("ENTBYPAGE", 2, false, $glossary->entbypage); $co .= glossary_xml_export_files('INTROFILES', 2, $context->id, 'intro', 0); if ($entries = $DB->get_records("glossary_entries", array("glossaryid" => $glossary->id))) { $co .= glossary_start_tag("ENTRIES", 2, true); foreach ($entries as $entry) { $permissiongranted = 1; if ($hook) { switch ($hook) { case "ALL": case "SPECIAL": break; default: $permissiongranted = $entry->concept[strlen($hook) - 1] == $hook; break; } } if ($hook) { switch ($hook) { case GLOSSARY_SHOW_ALL_CATEGORIES: break; case GLOSSARY_SHOW_NOT_CATEGORISED: $permissiongranted = !$DB->record_exists("glossary_entries_categories", array("entryid" => $entry->id)); break; default: $permissiongranted = $DB->record_exists("glossary_entries_categories", array("entryid" => $entry->id, "categoryid" => $hook)); break; } } if ($entry->approved and $permissiongranted) { $co .= glossary_start_tag("ENTRY", 3, true); $co .= glossary_full_tag("CONCEPT", 4, false, trim($entry->concept)); $co .= glossary_full_tag("DEFINITION", 4, false, $entry->definition); $co .= glossary_full_tag("FORMAT", 4, false, $entry->definitionformat); // note: use old name for BC reasons $co .= glossary_full_tag("USEDYNALINK", 4, false, $entry->usedynalink); $co .= glossary_full_tag("CASESENSITIVE", 4, false, $entry->casesensitive); $co .= glossary_full_tag("FULLMATCH", 4, false, $entry->fullmatch); $co .= glossary_full_tag("TEACHERENTRY", 4, false, $entry->teacherentry); if ($aliases = $DB->get_records("glossary_alias", array("entryid" => $entry->id))) { $co .= glossary_start_tag("ALIASES", 4, true); foreach ($aliases as $alias) { $co .= glossary_start_tag("ALIAS", 5, true); $co .= glossary_full_tag("NAME", 6, false, trim($alias->alias)); $co .= glossary_end_tag("ALIAS", 5, true); } $co .= glossary_end_tag("ALIASES", 4, true); } if ($catentries = $DB->get_records("glossary_entries_categories", array("entryid" => $entry->id))) { $co .= glossary_start_tag("CATEGORIES", 4, true); foreach ($catentries as $catentry) { $category = $DB->get_record("glossary_categories", array("id" => $catentry->categoryid)); $co .= glossary_start_tag("CATEGORY", 5, true); $co .= glossary_full_tag("NAME", 6, false, $category->name); $co .= glossary_full_tag("USEDYNALINK", 6, false, $category->usedynalink); $co .= glossary_end_tag("CATEGORY", 5, true); } $co .= glossary_end_tag("CATEGORIES", 4, true); } // Export files embedded in entries. $co .= glossary_xml_export_files('ENTRYFILES', 4, $context->id, 'entry', $entry->id); // Export attachments. $co .= glossary_xml_export_files('ATTACHMENTFILES', 4, $context->id, 'attachment', $entry->id); $co .= glossary_end_tag("ENTRY", 3, true); } } $co .= glossary_end_tag("ENTRIES", 2, true); } $co .= glossary_end_tag("INFO", 1, true); $co .= glossary_end_tag("GLOSSARY", 0, true); return $co; }
/** * Execute monthly statistics gathering * @return boolean success */ function stats_cron_monthly() { global $CFG, $DB; require_once $CFG->libdir . '/adminlib.php'; $now = time(); // read last execution date from db if (!($timestart = get_config(NULL, 'statslastmonthly'))) { $timestart = stats_get_base_monthly(stats_get_start_from('monthly')); set_config('statslastmonthly', $timestart); } $nextstartmonth = stats_get_next_month_start($timestart); // are there any months that need to be processed? if ($now < $nextstartmonth) { return true; // everything ok and up-to-date } $timeout = empty($CFG->statsmaxruntime) ? 60 * 60 * 24 : $CFG->statsmaxruntime; if (!set_cron_lock('statsrunning', $now + $timeout)) { return false; } // fisr delete entries that should not be there yet $DB->delete_records_select('stats_monthly', "timeend > {$timestart}"); $DB->delete_records_select('stats_user_monthly', "timeend > {$timestart}"); $startmonth = stats_get_base_monthly($now); mtrace("Running monthly statistics gathering, starting at {$timestart}:"); cron_trace_time_and_memory(); $months = 0; while ($now > $nextstartmonth) { core_php_time_limit::raise($timeout - 200); $months++; if ($months > 1) { // move the lock set_cron_lock('statsrunning', time() + $timeout, true); } $stattimesql = "timeend > {$timestart} AND timeend <= {$nextstartmonth}"; $monthstart = time(); stats_progress('init'); /// process login info first $sql = "INSERT INTO {stats_user_monthly} (stattype, timeend, courseid, userid, statsreads)\n\n SELECT 'logins', timeend, courseid, userid, SUM(statsreads)\n FROM (\n SELECT {$nextstartmonth} AS timeend, courseid, userid, statsreads\n FROM {stats_user_daily} sd\n WHERE stattype = 'logins' AND {$stattimesql}\n ) inline_view\n GROUP BY timeend, courseid, userid\n HAVING SUM(statsreads) > 0"; $DB->execute($sql); stats_progress('1'); $sql = "INSERT INTO {stats_monthly} (stattype, timeend, courseid, roleid, stat1, stat2)\n\n SELECT 'logins' AS stattype, {$nextstartmonth} AS timeend, " . SITEID . " as courseid, 0,\n COALESCE((SELECT SUM(statsreads)\n FROM {stats_user_monthly} s1\n WHERE s1.stattype = 'logins' AND timeend = {$nextstartmonth}), 0) AS nstat1,\n (SELECT COUNT('x')\n FROM {stats_user_monthly} s2\n WHERE s2.stattype = 'logins' AND timeend = {$nextstartmonth}) AS nstat2" . $DB->sql_null_from_clause(); $DB->execute($sql); stats_progress('2'); /// now enrolments averages $sql = "INSERT INTO {stats_monthly} (stattype, timeend, courseid, roleid, stat1, stat2)\n\n SELECT 'enrolments', ntimeend, courseid, roleid, " . $DB->sql_ceil('AVG(stat1)') . ", " . $DB->sql_ceil('AVG(stat2)') . "\n FROM (\n SELECT {$nextstartmonth} AS ntimeend, courseid, roleid, stat1, stat2\n FROM {stats_daily} sd\n WHERE stattype = 'enrolments' AND {$stattimesql}\n ) inline_view\n GROUP BY ntimeend, courseid, roleid"; $DB->execute($sql); stats_progress('3'); /// activity read/write averages $sql = "INSERT INTO {stats_monthly} (stattype, timeend, courseid, roleid, stat1, stat2)\n\n SELECT 'activity', ntimeend, courseid, roleid, SUM(stat1), SUM(stat2)\n FROM (\n SELECT {$nextstartmonth} AS ntimeend, courseid, roleid, stat1, stat2\n FROM {stats_daily}\n WHERE stattype = 'activity' AND {$stattimesql}\n ) inline_view\n GROUP BY ntimeend, courseid, roleid"; $DB->execute($sql); stats_progress('4'); /// user read/write averages $sql = "INSERT INTO {stats_user_monthly} (stattype, timeend, courseid, userid, statsreads, statswrites)\n\n SELECT 'activity', ntimeend, courseid, userid, SUM(statsreads), SUM(statswrites)\n FROM (\n SELECT {$nextstartmonth} AS ntimeend, courseid, userid, statsreads, statswrites\n FROM {stats_user_daily}\n WHERE stattype = 'activity' AND {$stattimesql}\n ) inline_view\n GROUP BY ntimeend, courseid, userid"; $DB->execute($sql); stats_progress('5'); set_config('statslastmonthly', $nextstartmonth); $elapsed = time() - $monthstart; mtrace(" finished until {$nextstartmonth}: " . userdate($nextstartmonth) . " (in {$elapsed} s)"); $timestart = $nextstartmonth; $nextstartmonth = stats_get_next_month_start($nextstartmonth); } set_cron_lock('statsrunning', null); mtrace("...completed {$months} months of statistics."); return true; }
* @subpackage uploaduser * @copyright 2004 onwards Martin Dougiamas (http://dougiamas.com) * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ require '../../../config.php'; require_once $CFG->libdir . '/adminlib.php'; require_once $CFG->libdir . '/csvlib.class.php'; require_once $CFG->dirroot . '/user/profile/lib.php'; require_once $CFG->dirroot . '/user/lib.php'; require_once $CFG->dirroot . '/group/lib.php'; require_once $CFG->dirroot . '/cohort/lib.php'; require_once 'locallib.php'; require_once 'user_form.php'; $iid = optional_param('iid', '', PARAM_INT); $previewrows = optional_param('previewrows', 10, PARAM_INT); core_php_time_limit::raise(60 * 60); // 1 hour should be enough raise_memory_limit(MEMORY_HUGE); require_login(); admin_externalpage_setup('tooluploaduser'); require_capability('moodle/site:uploadusers', context_system::instance()); $struserrenamed = get_string('userrenamed', 'tool_uploaduser'); $strusernotrenamedexists = get_string('usernotrenamedexists', 'error'); $strusernotrenamedmissing = get_string('usernotrenamedmissing', 'error'); $strusernotrenamedoff = get_string('usernotrenamedoff', 'error'); $strusernotrenamedadmin = get_string('usernotrenamedadmin', 'error'); $struserupdated = get_string('useraccountupdated', 'tool_uploaduser'); $strusernotupdated = get_string('usernotupdatederror', 'error'); $strusernotupdatednotexists = get_string('usernotupdatednotexists', 'error'); $strusernotupdatedadmin = get_string('usernotupdatedadmin', 'error'); $struseruptodate = get_string('useraccountuptodate', 'tool_uploaduser');
/** * Forces synchronisation of all enrolments with LDAP server. * It creates courses if the plugin is configured to do so. * * @param progress_trace $trace * @param int|null $onecourse limit sync to one course->id, null if all courses * @return void */ public function sync_enrolments(progress_trace $trace, $onecourse = null) { global $CFG, $DB; if (!$this->ldap_connect($trace)) { $trace->finished(); return; } $ldap_pagedresults = ldap_paged_results_supported($this->get_config('ldap_version')); // we may need a lot of memory here core_php_time_limit::raise(); raise_memory_limit(MEMORY_HUGE); $oneidnumber = null; if ($onecourse) { if (!($course = $DB->get_record('course', array('id' => $onecourse), 'id,' . $this->enrol_localcoursefield))) { // Course does not exist, nothing to do. $trace->output("Requested course {$onecourse} does not exist, no sync performed."); $trace->finished(); return; } if (empty($course->{$this->enrol_localcoursefield})) { $trace->output("Requested course {$onecourse} does not have {$this->enrol_localcoursefield}, no sync performed."); $trace->finished(); return; } $oneidnumber = ldap_filter_addslashes(core_text::convert($course->idnumber, 'utf-8', $this->get_config('ldapencoding'))); } // Get enrolments for each type of role. $roles = get_all_roles(); $enrolments = array(); foreach ($roles as $role) { // Get all contexts $ldap_contexts = explode(';', $this->config->{'contexts_role' . $role->id}); // Get all the fields we will want for the potential course creation // as they are light. Don't get membership -- potentially a lot of data. $ldap_fields_wanted = array('dn', $this->config->course_idnumber); if (!empty($this->config->course_fullname)) { array_push($ldap_fields_wanted, $this->config->course_fullname); } if (!empty($this->config->course_shortname)) { array_push($ldap_fields_wanted, $this->config->course_shortname); } if (!empty($this->config->course_summary)) { array_push($ldap_fields_wanted, $this->config->course_summary); } array_push($ldap_fields_wanted, $this->config->{'memberattribute_role' . $role->id}); // Define the search pattern $ldap_search_pattern = $this->config->objectclass; if ($oneidnumber !== null) { $ldap_search_pattern = "(&{$ldap_search_pattern}({$this->config->course_idnumber}={$oneidnumber}))"; } $ldap_cookie = ''; foreach ($ldap_contexts as $ldap_context) { $ldap_context = trim($ldap_context); if (empty($ldap_context)) { continue; // Next; } $flat_records = array(); do { if ($ldap_pagedresults) { ldap_control_paged_result($this->ldapconnection, $this->config->pagesize, true, $ldap_cookie); } if ($this->config->course_search_sub) { // Use ldap_search to find first user from subtree $ldap_result = @ldap_search($this->ldapconnection, $ldap_context, $ldap_search_pattern, $ldap_fields_wanted); } else { // Search only in this context $ldap_result = @ldap_list($this->ldapconnection, $ldap_context, $ldap_search_pattern, $ldap_fields_wanted); } if (!$ldap_result) { continue; // Next } if ($ldap_pagedresults) { ldap_control_paged_result_response($this->ldapconnection, $ldap_result, $ldap_cookie); } // Check and push results $records = ldap_get_entries($this->ldapconnection, $ldap_result); // LDAP libraries return an odd array, really. fix it: for ($c = 0; $c < $records['count']; $c++) { array_push($flat_records, $records[$c]); } // Free some mem unset($records); } while ($ldap_pagedresults && !empty($ldap_cookie)); // If LDAP paged results were used, the current connection must be completely // closed and a new one created, to work without paged results from here on. if ($ldap_pagedresults) { $this->ldap_close(); $this->ldap_connect($trace); } if (count($flat_records)) { $ignorehidden = $this->get_config('ignorehiddencourses'); foreach ($flat_records as $course) { $course = array_change_key_case($course, CASE_LOWER); $idnumber = $course[$this->config->course_idnumber][0]; $trace->output(get_string('synccourserole', 'enrol_ldap', array('idnumber' => $idnumber, 'role_shortname' => $role->shortname))); // Does the course exist in moodle already? $course_obj = $DB->get_record('course', array($this->enrol_localcoursefield => $idnumber)); if (empty($course_obj)) { // Course doesn't exist if ($this->get_config('autocreate')) { // Autocreate $trace->output(get_string('createcourseextid', 'enrol_ldap', array('courseextid' => $idnumber))); if (!($newcourseid = $this->create_course($course, $trace))) { continue; } $course_obj = $DB->get_record('course', array('id' => $newcourseid)); } else { $trace->output(get_string('createnotcourseextid', 'enrol_ldap', array('courseextid' => $idnumber))); continue; // Next; skip this one! } } else { // Check if course needs update & update as needed. $this->update_course($course_obj, $course, $trace); } // Enrol & unenrol // Pull the ldap membership into a nice array // this is an odd array -- mix of hash and array -- $ldapmembers = array(); if (array_key_exists('memberattribute_role' . $role->id, $this->config) && !empty($this->config->{'memberattribute_role' . $role->id}) && !empty($course[$this->config->{'memberattribute_role' . $role->id}])) { // May have no membership! $ldapmembers = $course[$this->config->{'memberattribute_role' . $role->id}]; unset($ldapmembers['count']); // Remove oddity ;) // If we have enabled nested groups, we need to expand // the groups to get the real user list. We need to do // this before dealing with 'memberattribute_isdn'. if ($this->config->nested_groups) { $users = array(); foreach ($ldapmembers as $ldapmember) { $grpusers = $this->ldap_explode_group($ldapmember, $this->config->{'memberattribute_role' . $role->id}); $users = array_merge($users, $grpusers); } $ldapmembers = array_unique($users); // There might be duplicates. } // Deal with the case where the member attribute holds distinguished names, // but only if the user attribute is not a distinguished name itself. if ($this->config->memberattribute_isdn && $this->config->idnumber_attribute !== 'dn' && $this->config->idnumber_attribute !== 'distinguishedname') { // We need to retrieve the idnumber for all the users in $ldapmembers, // as the idnumber does not match their dn and we get dn's from membership. $memberidnumbers = array(); foreach ($ldapmembers as $ldapmember) { $result = ldap_read($this->ldapconnection, $ldapmember, '(objectClass=*)', array($this->config->idnumber_attribute)); $entry = ldap_first_entry($this->ldapconnection, $result); $values = ldap_get_values($this->ldapconnection, $entry, $this->config->idnumber_attribute); array_push($memberidnumbers, $values[0]); } $ldapmembers = $memberidnumbers; } } // Prune old ldap enrolments // hopefully they'll fit in the max buffer size for the RDBMS $sql = "SELECT u.id as userid, u.username, ue.status,\n ra.contextid, ra.itemid as instanceid\n FROM {user} u\n JOIN {role_assignments} ra ON (ra.userid = u.id AND ra.component = 'enrol_ldap' AND ra.roleid = :roleid)\n JOIN {user_enrolments} ue ON (ue.userid = u.id AND ue.enrolid = ra.itemid)\n JOIN {enrol} e ON (e.id = ue.enrolid)\n WHERE u.deleted = 0 AND e.courseid = :courseid "; $params = array('roleid' => $role->id, 'courseid' => $course_obj->id); $context = context_course::instance($course_obj->id); if (!empty($ldapmembers)) { list($ldapml, $params2) = $DB->get_in_or_equal($ldapmembers, SQL_PARAMS_NAMED, 'm', false); $sql .= "AND u.idnumber {$ldapml}"; $params = array_merge($params, $params2); unset($params2); } else { $shortname = format_string($course_obj->shortname, true, array('context' => $context)); $trace->output(get_string('emptyenrolment', 'enrol_ldap', array('role_shortname' => $role->shortname, 'course_shortname' => $shortname))); } $todelete = $DB->get_records_sql($sql, $params); if (!empty($todelete)) { $transaction = $DB->start_delegated_transaction(); foreach ($todelete as $row) { $instance = $DB->get_record('enrol', array('id' => $row->instanceid)); switch ($this->get_config('unenrolaction')) { case ENROL_EXT_REMOVED_UNENROL: $this->unenrol_user($instance, $row->userid); $trace->output(get_string('extremovedunenrol', 'enrol_ldap', array('user_username' => $row->username, 'course_shortname' => $course_obj->shortname, 'course_id' => $course_obj->id))); break; case ENROL_EXT_REMOVED_KEEP: // Keep - only adding enrolments break; case ENROL_EXT_REMOVED_SUSPEND: if ($row->status != ENROL_USER_SUSPENDED) { $DB->set_field('user_enrolments', 'status', ENROL_USER_SUSPENDED, array('enrolid' => $instance->id, 'userid' => $row->userid)); $trace->output(get_string('extremovedsuspend', 'enrol_ldap', array('user_username' => $row->username, 'course_shortname' => $course_obj->shortname, 'course_id' => $course_obj->id))); } break; case ENROL_EXT_REMOVED_SUSPENDNOROLES: if ($row->status != ENROL_USER_SUSPENDED) { $DB->set_field('user_enrolments', 'status', ENROL_USER_SUSPENDED, array('enrolid' => $instance->id, 'userid' => $row->userid)); } role_unassign_all(array('contextid' => $row->contextid, 'userid' => $row->userid, 'component' => 'enrol_ldap', 'itemid' => $instance->id)); $trace->output(get_string('extremovedsuspendnoroles', 'enrol_ldap', array('user_username' => $row->username, 'course_shortname' => $course_obj->shortname, 'course_id' => $course_obj->id))); break; } } $transaction->allow_commit(); } // Insert current enrolments // bad we can't do INSERT IGNORE with postgres... // Add necessary enrol instance if not present yet; $sql = "SELECT c.id, c.visible, e.id as enrolid\n FROM {course} c\n JOIN {enrol} e ON (e.courseid = c.id AND e.enrol = 'ldap')\n WHERE c.id = :courseid"; $params = array('courseid' => $course_obj->id); if (!($course_instance = $DB->get_record_sql($sql, $params, IGNORE_MULTIPLE))) { $course_instance = new stdClass(); $course_instance->id = $course_obj->id; $course_instance->visible = $course_obj->visible; $course_instance->enrolid = $this->add_instance($course_instance); } if (!($instance = $DB->get_record('enrol', array('id' => $course_instance->enrolid)))) { continue; // Weird; skip this one. } if ($ignorehidden && !$course_instance->visible) { continue; } $transaction = $DB->start_delegated_transaction(); foreach ($ldapmembers as $ldapmember) { $sql = 'SELECT id,username,1 FROM {user} WHERE idnumber = ? AND deleted = 0'; $member = $DB->get_record_sql($sql, array($ldapmember)); if (empty($member) || empty($member->id)) { $trace->output(get_string('couldnotfinduser', 'enrol_ldap', $ldapmember)); continue; } $sql = "SELECT ue.status\n FROM {user_enrolments} ue\n JOIN {enrol} e ON (e.id = ue.enrolid AND e.enrol = 'ldap')\n WHERE e.courseid = :courseid AND ue.userid = :userid"; $params = array('courseid' => $course_obj->id, 'userid' => $member->id); $userenrolment = $DB->get_record_sql($sql, $params, IGNORE_MULTIPLE); if (empty($userenrolment)) { $this->enrol_user($instance, $member->id, $role->id); // Make sure we set the enrolment status to active. If the user wasn't // previously enrolled to the course, enrol_user() sets it. But if we // configured the plugin to suspend the user enrolments _AND_ remove // the role assignments on external unenrol, then enrol_user() doesn't // set it back to active on external re-enrolment. So set it // unconditionally to cover both cases. $DB->set_field('user_enrolments', 'status', ENROL_USER_ACTIVE, array('enrolid' => $instance->id, 'userid' => $member->id)); $trace->output(get_string('enroluser', 'enrol_ldap', array('user_username' => $member->username, 'course_shortname' => $course_obj->shortname, 'course_id' => $course_obj->id))); } else { if (!$DB->record_exists('role_assignments', array('roleid' => $role->id, 'userid' => $member->id, 'contextid' => $context->id, 'component' => 'enrol_ldap', 'itemid' => $instance->id))) { // This happens when reviving users or when user has multiple roles in one course. $context = context_course::instance($course_obj->id); role_assign($role->id, $member->id, $context->id, 'enrol_ldap', $instance->id); $trace->output("Assign role to user '{$member->username}' in course '{$course_obj->shortname} ({$course_obj->id})'"); } if ($userenrolment->status == ENROL_USER_SUSPENDED) { // Reenable enrolment that was previously disabled. Enrolment refreshed $DB->set_field('user_enrolments', 'status', ENROL_USER_ACTIVE, array('enrolid' => $instance->id, 'userid' => $member->id)); $trace->output(get_string('enroluserenable', 'enrol_ldap', array('user_username' => $member->username, 'course_shortname' => $course_obj->shortname, 'course_id' => $course_obj->id))); } } } $transaction->allow_commit(); } } } } @$this->ldap_close(); $trace->finished(); }
/** * Resets the page customisations for all users. * * @param int $private Either MY_PAGE_PRIVATE or MY_PAGE_PUBLIC. * @param string $pagetype Either my-index or user-profile. * @return void */ function my_reset_page_for_all_users($private = MY_PAGE_PRIVATE, $pagetype = 'my-index') { global $DB; // This may take a while. Raise the execution time limit. core_php_time_limit::raise(); // Find all the user pages. $where = 'userid IS NOT NULL AND private = :private'; $params = array('private' => $private); $pages = $DB->get_recordset_select('my_pages', $where, $params, 'id, userid'); $blockids = array(); foreach ($pages as $page) { $usercontext = context_user::instance($page->userid); // Find all block instances in that page. $blockswhere = 'parentcontextid = :parentcontextid AND pagetypepattern = :pagetypepattern AND (subpagepattern IS NULL OR subpagepattern = :subpagepattern)'; $blockswhereparams = [ 'parentcontextid' => $usercontext->id, 'pagetypepattern' => $pagetype, 'subpagepattern' => $page->id ]; if ($pageblockids = $DB->get_fieldset_select('block_instances', 'id', $blockswhere, $blockswhereparams)) { $blockids = array_merge($blockids, $pageblockids); } } $pages->close(); // Wrap the SQL queries in a transaction. $transaction = $DB->start_delegated_transaction(); // Delete the block instances. if (!empty($blockids)) { blocks_delete_instances($blockids); } // Finally delete the pages. if (!empty($pages)) { $DB->delete_records_select('my_pages', $where, $params); } // We should be good to go now. $transaction->allow_commit(); }
/** * Upgrade moodle core * @param float $version target version * @param bool $verbose * @return void, may throw exception */ function upgrade_core($version, $verbose) { global $CFG, $SITE, $DB, $COURSE; raise_memory_limit(MEMORY_EXTRA); require_once($CFG->libdir.'/db/upgrade.php'); // Defines upgrades try { // Reset caches before any output. cache_helper::purge_all(true); purge_all_caches(); // Upgrade current language pack if we can upgrade_language_pack(); print_upgrade_part_start('moodle', false, $verbose); // Pre-upgrade scripts for local hack workarounds. $preupgradefile = "$CFG->dirroot/local/preupgrade.php"; if (file_exists($preupgradefile)) { core_php_time_limit::raise(); require($preupgradefile); // Reset upgrade timeout to default. upgrade_set_timeout(); } $result = xmldb_main_upgrade($CFG->version); if ($version > $CFG->version) { // store version if not already there upgrade_main_savepoint($result, $version, false); } // In case structure of 'course' table has been changed and we forgot to update $SITE, re-read it from db. $SITE = $DB->get_record('course', array('id' => $SITE->id)); $COURSE = clone($SITE); // perform all other component upgrade routines update_capabilities('moodle'); log_update_descriptions('moodle'); external_update_descriptions('moodle'); events_update_definition('moodle'); \core\task\manager::reset_scheduled_tasks_for_component('moodle'); message_update_providers('moodle'); \core\message\inbound\manager::update_handlers_for_component('moodle'); // Update core definitions. cache_helper::update_definitions(true); // Purge caches again, just to be sure we arn't holding onto old stuff now. cache_helper::purge_all(true); purge_all_caches(); // Clean up contexts - more and more stuff depends on existence of paths and contexts context_helper::cleanup_instances(); context_helper::create_instances(null, false); context_helper::build_all_paths(false); $syscontext = context_system::instance(); $syscontext->mark_dirty(); print_upgrade_part_end('moodle', false, $verbose); } catch (Exception $ex) { upgrade_handle_exception($ex); } }
/** * Rebuilds or resets the cached list of course activities stored in MUC. * * rebuild_course_cache() must NEVER be called from lib/db/upgrade.php. * At the same time course cache may ONLY be cleared using this function in * upgrade scripts of plugins. * * During the bulk operations if it is necessary to reset cache of multiple * courses it is enough to call {@link increment_revision_number()} for the * table 'course' and field 'cacherev' specifying affected courses in select. * * Cached course information is stored in MUC core/coursemodinfo and is * validated with the DB field {course}.cacherev * * @global moodle_database $DB * @param int $courseid id of course to rebuild, empty means all * @param boolean $clearonly only clear the cache, gets rebuild automatically on the fly. * Recommended to set to true to avoid unnecessary multiple rebuilding. */ function rebuild_course_cache($courseid=0, $clearonly=false) { global $COURSE, $SITE, $DB, $CFG; // Function rebuild_course_cache() can not be called during upgrade unless it's clear only. if (!$clearonly && !upgrade_ensure_not_running(true)) { $clearonly = true; } // Destroy navigation caches navigation_cache::destroy_volatile_caches(); if (class_exists('format_base')) { // if file containing class is not loaded, there is no cache there anyway format_base::reset_course_cache($courseid); } $cachecoursemodinfo = cache::make('core', 'coursemodinfo'); if (empty($courseid)) { // Clearing caches for all courses. increment_revision_number('course', 'cacherev', ''); $cachecoursemodinfo->purge(); course_modinfo::clear_instance_cache(); // Update global values too. $sitecacherev = $DB->get_field('course', 'cacherev', array('id' => SITEID)); $SITE->cachrev = $sitecacherev; if ($COURSE->id == SITEID) { $COURSE->cacherev = $sitecacherev; } else { $COURSE->cacherev = $DB->get_field('course', 'cacherev', array('id' => $COURSE->id)); } } else { // Clearing cache for one course, make sure it is deleted from user request cache as well. increment_revision_number('course', 'cacherev', 'id = :id', array('id' => $courseid)); $cachecoursemodinfo->delete($courseid); course_modinfo::clear_instance_cache($courseid); // Update global values too. if ($courseid == $COURSE->id || $courseid == $SITE->id) { $cacherev = $DB->get_field('course', 'cacherev', array('id' => $courseid)); if ($courseid == $COURSE->id) { $COURSE->cacherev = $cacherev; } if ($courseid == $SITE->id) { $SITE->cachrev = $cacherev; } } } if ($clearonly) { return; } if ($courseid) { $select = array('id'=>$courseid); } else { $select = array(); core_php_time_limit::raise(); // this could take a while! MDL-10954 } $rs = $DB->get_recordset("course", $select,'','id,'.join(',', course_modinfo::$cachedfields)); // Rebuild cache for each course. foreach ($rs as $course) { course_modinfo::build_course_cache($course); } $rs->close(); }
/** * This function converts all of the base settings for an instance of * the old setaskment to the new format. Then it calls each of the plugins * to see if they can help upgrade this setaskment. * @param int $oldsetaskmentid (don't rely on the old setaskment type even being installed) * @param string $log This string gets appended to during the conversion process * @return bool true or false */ public function upgrade_setaskment($oldsetaskmentid, &$log) { global $DB, $CFG, $USER; // Steps to upgrade an setaskment. core_php_time_limit::raise(ASSIGN_MAX_UPGRADE_TIME_SECS); // Get the module details. $oldmodule = $DB->get_record('modules', array('name' => 'setaskment'), '*', MUST_EXIST); $params = array('module' => $oldmodule->id, 'instance' => $oldsetaskmentid); $oldcoursemodule = $DB->get_record('course_modules', $params, '*', MUST_EXIST); $oldcontext = context_module::instance($oldcoursemodule->id); // We used to check for admin capability, but since Moodle 2.7 this is called // during restore of a mod_setaskment module. // Also note that we do not check for any mod_setaskment capabilities, because they can // be removed so that users don't add new instances of the broken old thing. if (!has_capability('mod/setask:addinstance', $oldcontext)) { $log = get_string('couldnotcreatenewsetaskmentinstance', 'mod_setask'); return false; } // First insert an setask instance to get the id. $oldsetaskment = $DB->get_record('setaskment', array('id' => $oldsetaskmentid), '*', MUST_EXIST); $oldversion = get_config('setaskment_' . $oldsetaskment->setaskmenttype, 'version'); $data = new stdClass(); $data->course = $oldsetaskment->course; $data->name = $oldsetaskment->name; $data->intro = $oldsetaskment->intro; $data->introformat = $oldsetaskment->introformat; $data->alwaysshowdescription = 1; $data->sendnotifications = $oldsetaskment->emailteachers; $data->sendlatenotifications = $oldsetaskment->emailteachers; $data->duedate = $oldsetaskment->timedue; $data->allowsubmissionsfromdate = $oldsetaskment->timeavailable; $data->grade = $oldsetaskment->grade; $data->submissiondrafts = $oldsetaskment->resubmit; $data->requiresubmissionstatement = 0; $data->markingworkflow = 0; $data->markingallocation = 0; $data->cutoffdate = 0; // New way to specify no late submissions. if ($oldsetaskment->preventlate) { $data->cutoffdate = $data->duedate; } $data->teamsubmission = 0; $data->requireallteammemberssubmit = 0; $data->teamsubmissiongroupingid = 0; $data->blindmarking = 0; $data->attemptreopenmethod = 'none'; $data->maxattempts = ASSIGN_UNLIMITED_ATTEMPTS; $newsetaskment = new setask(null, null, null); if (!$newsetaskment->add_instance($data, false)) { $log = get_string('couldnotcreatenewsetaskmentinstance', 'mod_setask'); return false; } // Now create a new coursemodule from the old one. $newmodule = $DB->get_record('modules', array('name' => 'setask'), '*', MUST_EXIST); $newcoursemodule = $this->duplicate_course_module($oldcoursemodule, $newmodule->id, $newsetaskment->get_instance()->id); if (!$newcoursemodule) { $log = get_string('couldnotcreatenewcoursemodule', 'mod_setask'); return false; } // Convert the base database tables (setaskment, submission, grade). // These are used to store information in case a rollback is required. $gradingarea = null; $gradingdefinitions = null; $gradeidmap = array(); $completiondone = false; $gradesdone = false; // From this point we want to rollback on failure. $rollback = false; try { $newsetaskment->set_context(context_module::instance($newcoursemodule->id)); // The course module has now been created - time to update the core tables. // Copy intro files. $newsetaskment->copy_area_files_for_upgrade($oldcontext->id, 'mod_setaskment', 'intro', 0, $newsetaskment->get_context()->id, 'mod_setask', 'intro', 0); // Get the plugins to do their bit. foreach ($newsetaskment->get_submission_plugins() as $plugin) { if ($plugin->can_upgrade($oldsetaskment->setaskmenttype, $oldversion)) { $plugin->enable(); if (!$plugin->upgrade_settings($oldcontext, $oldsetaskment, $log)) { $rollback = true; } } else { $plugin->disable(); } } foreach ($newsetaskment->get_feedback_plugins() as $plugin) { if ($plugin->can_upgrade($oldsetaskment->setaskmenttype, $oldversion)) { $plugin->enable(); if (!$plugin->upgrade_settings($oldcontext, $oldsetaskment, $log)) { $rollback = true; } } else { $plugin->disable(); } } // See if there is advanced grading upgrades required. $gradingarea = $DB->get_record('grading_areas', array('contextid' => $oldcontext->id, 'areaname' => 'submission'), '*', IGNORE_MISSING); if ($gradingarea) { $params = array('id' => $gradingarea->id, 'contextid' => $newsetaskment->get_context()->id, 'component' => 'mod_setask', 'areaname' => 'submissions'); $DB->update_record('grading_areas', $params); $gradingdefinitions = $DB->get_records('grading_definitions', array('areaid' => $gradingarea->id)); } // Upgrade availability data. \core_availability\info::update_dependency_id_across_course($newcoursemodule->course, 'course_modules', $oldcoursemodule->id, $newcoursemodule->id); // Upgrade completion data. $DB->set_field('course_modules_completion', 'coursemoduleid', $newcoursemodule->id, array('coursemoduleid' => $oldcoursemodule->id)); $allcriteria = $DB->get_records('course_completion_criteria', array('moduleinstance' => $oldcoursemodule->id)); foreach ($allcriteria as $criteria) { $criteria->module = 'setask'; $criteria->moduleinstance = $newcoursemodule->id; $DB->update_record('course_completion_criteria', $criteria); } $completiondone = true; // Migrate log entries so we don't lose them. $logparams = array('cmid' => $oldcoursemodule->id, 'course' => $oldcoursemodule->course); $DB->set_field('log', 'module', 'setask', $logparams); $DB->set_field('log', 'cmid', $newcoursemodule->id, $logparams); // Copy all the submission data (and get plugins to do their bit). $oldsubmissions = $DB->get_records('setaskment_submissions', array('setaskment' => $oldsetaskmentid)); foreach ($oldsubmissions as $oldsubmission) { $submission = new stdClass(); $submission->setaskment = $newsetaskment->get_instance()->id; $submission->userid = $oldsubmission->userid; $submission->timecreated = $oldsubmission->timecreated; $submission->timemodified = $oldsubmission->timemodified; $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED; // Because in mod_setaskment there could only be one submission per student, it is always the latest. $submission->latest = 1; $submission->id = $DB->insert_record('setask_submission', $submission); if (!$submission->id) { $log .= get_string('couldnotinsertsubmission', 'mod_setask', $submission->userid); $rollback = true; } foreach ($newsetaskment->get_submission_plugins() as $plugin) { if ($plugin->can_upgrade($oldsetaskment->setaskmenttype, $oldversion)) { if (!$plugin->upgrade($oldcontext, $oldsetaskment, $oldsubmission, $submission, $log)) { $rollback = true; } } } if ($oldsubmission->timemarked) { // Submission has been graded - create a grade record. $grade = new stdClass(); $grade->setaskment = $newsetaskment->get_instance()->id; $grade->userid = $oldsubmission->userid; $grade->grader = $oldsubmission->teacher; $grade->timemodified = $oldsubmission->timemarked; $grade->timecreated = $oldsubmission->timecreated; $grade->grade = $oldsubmission->grade; if ($oldsubmission->mailed) { // The mailed flag goes in the flags table. $flags = new stdClass(); $flags->userid = $oldsubmission->userid; $flags->setaskment = $newsetaskment->get_instance()->id; $flags->mailed = 1; $DB->insert_record('setask_user_flags', $flags); } $grade->id = $DB->insert_record('setask_grades', $grade); if (!$grade->id) { $log .= get_string('couldnotinsertgrade', 'mod_setask', $grade->userid); $rollback = true; } // Copy any grading instances. if ($gradingarea) { $gradeidmap[$grade->id] = $oldsubmission->id; foreach ($gradingdefinitions as $definition) { $params = array('definitionid' => $definition->id, 'itemid' => $oldsubmission->id); $DB->set_field('grading_instances', 'itemid', $grade->id, $params); } } foreach ($newsetaskment->get_feedback_plugins() as $plugin) { if ($plugin->can_upgrade($oldsetaskment->setaskmenttype, $oldversion)) { if (!$plugin->upgrade($oldcontext, $oldsetaskment, $oldsubmission, $grade, $log)) { $rollback = true; } } } } } $newsetaskment->update_calendar($newcoursemodule->id); // Reassociate grade_items from the old setaskment instance to the new setask instance. // This includes outcome linked grade_items. $params = array('setask', $newsetaskment->get_instance()->id, 'setaskment', $oldsetaskment->id); $sql = 'UPDATE {grade_items} SET itemmodule = ?, iteminstance = ? WHERE itemmodule = ? AND iteminstance = ?'; $DB->execute($sql, $params); // Create a mapping record to map urls from the old to the new setaskment. $mapping = new stdClass(); $mapping->oldcmid = $oldcoursemodule->id; $mapping->oldinstance = $oldsetaskment->id; $mapping->newcmid = $newcoursemodule->id; $mapping->newinstance = $newsetaskment->get_instance()->id; $mapping->timecreated = time(); $DB->insert_record('setaskment_upgrade', $mapping); $gradesdone = true; } catch (Exception $exception) { $rollback = true; $log .= get_string('conversionexception', 'mod_setask', $exception->getMessage()); } if ($rollback) { // Roll back the grades changes. if ($gradesdone) { // Reassociate grade_items from the new setask instance to the old setaskment instance. $params = array('setaskment', $oldsetaskment->id, 'setask', $newsetaskment->get_instance()->id); $sql = 'UPDATE {grade_items} SET itemmodule = ?, iteminstance = ? WHERE itemmodule = ? AND iteminstance = ?'; $DB->execute($sql, $params); } // Roll back the completion changes. if ($completiondone) { $DB->set_field('course_modules_completion', 'coursemoduleid', $oldcoursemodule->id, array('coursemoduleid' => $newcoursemodule->id)); $allcriteria = $DB->get_records('course_completion_criteria', array('moduleinstance' => $newcoursemodule->id)); foreach ($allcriteria as $criteria) { $criteria->module = 'setaskment'; $criteria->moduleinstance = $oldcoursemodule->id; $DB->update_record('course_completion_criteria', $criteria); } } // Roll back the log changes. $logparams = array('cmid' => $newcoursemodule->id, 'course' => $newcoursemodule->course); $DB->set_field('log', 'module', 'setaskment', $logparams); $DB->set_field('log', 'cmid', $oldcoursemodule->id, $logparams); // Roll back the advanced grading update. if ($gradingarea) { foreach ($gradeidmap as $newgradeid => $oldsubmissionid) { foreach ($gradingdefinitions as $definition) { $DB->set_field('grading_instances', 'itemid', $oldsubmissionid, array('definitionid' => $definition->id, 'itemid' => $newgradeid)); } } $params = array('id' => $gradingarea->id, 'contextid' => $oldcontext->id, 'component' => 'mod_setaskment', 'areaname' => 'submission'); $DB->update_record('grading_areas', $params); } $newsetaskment->delete_instance(); return false; } // Delete the old setaskment (use object delete). $cm = get_coursemodule_from_id('', $oldcoursemodule->id, $oldcoursemodule->course); if ($cm) { course_delete_module($cm->id); } rebuild_course_cache($oldcoursemodule->course); return true; }
/** * Process the file * This method should not normally be overidden * @param object $category * @return bool success */ public function importprocess($category) { global $USER, $CFG, $DB, $OUTPUT; // reset the timer in case file upload was slow core_php_time_limit::raise(); // STAGE 1: Parse the file echo $OUTPUT->notification(get_string('parsingquestions', 'question'), 'notifysuccess'); if (!($lines = $this->readdata($this->filename))) { echo $OUTPUT->notification(get_string('cannotread', 'question')); return false; } if (!($questions = $this->readquestions($lines))) { // Extract all the questions echo $OUTPUT->notification(get_string('noquestionsinfile', 'question')); return false; } // STAGE 2: Write data to database echo $OUTPUT->notification(get_string('importingquestions', 'question', $this->count_questions($questions)), 'notifysuccess'); // check for errors before we continue if ($this->stoponerror and $this->importerrors > 0) { echo $OUTPUT->notification(get_string('importparseerror', 'question')); return true; } // get list of valid answer grades $gradeoptionsfull = question_bank::fraction_options_full(); // check answer grades are valid // (now need to do this here because of 'stop on error': MDL-10689) $gradeerrors = 0; $goodquestions = array(); foreach ($questions as $question) { if (!empty($question->fraction) and is_array($question->fraction)) { $fractions = $question->fraction; $invalidfractions = array(); foreach ($fractions as $key => $fraction) { $newfraction = match_grade_options($gradeoptionsfull, $fraction, $this->matchgrades); if ($newfraction === false) { $invalidfractions[] = $fraction; } else { $fractions[$key] = $newfraction; } } if ($invalidfractions) { echo $OUTPUT->notification(get_string('invalidgrade', 'question', implode(', ', $invalidfractions))); ++$gradeerrors; continue; } else { $question->fraction = $fractions; } } $goodquestions[] = $question; } $questions = $goodquestions; // check for errors before we continue if ($this->stoponerror && $gradeerrors > 0) { return false; } // count number of questions processed $count = 0; foreach ($questions as $question) { // Process and store each question // reset the php timeout core_php_time_limit::raise(); // check for category modifiers if ($question->qtype == 'category') { if ($this->catfromfile) { // find/create category object $catpath = $question->category; $newcategory = $this->create_category_path($catpath); if (!empty($newcategory)) { $this->category = $newcategory; } } continue; } $question->context = $this->importcontext; $count++; echo "<hr /><p><b>{$count}</b>. " . $this->format_question_text($question) . "</p>"; $question->category = $this->category->id; $question->stamp = make_unique_id_code(); // Set the unique code (not to be changed) $question->createdby = $USER->id; $question->timecreated = time(); $question->modifiedby = $USER->id; $question->timemodified = time(); $fileoptions = array('subdirs' => false, 'maxfiles' => -1, 'maxbytes' => 0); $question->id = $DB->insert_record('question', $question); if (isset($question->questiontextitemid)) { $question->questiontext = file_save_draft_area_files($question->questiontextitemid, $this->importcontext->id, 'question', 'questiontext', $question->id, $fileoptions, $question->questiontext); } else { if (isset($question->questiontextfiles)) { foreach ($question->questiontextfiles as $file) { question_bank::get_qtype($question->qtype)->import_file($this->importcontext, 'question', 'questiontext', $question->id, $file); } } } if (isset($question->generalfeedbackitemid)) { $question->generalfeedback = file_save_draft_area_files($question->generalfeedbackitemid, $this->importcontext->id, 'question', 'generalfeedback', $question->id, $fileoptions, $question->generalfeedback); } else { if (isset($question->generalfeedbackfiles)) { foreach ($question->generalfeedbackfiles as $file) { question_bank::get_qtype($question->qtype)->import_file($this->importcontext, 'question', 'generalfeedback', $question->id, $file); } } } $DB->update_record('question', $question); $this->questionids[] = $question->id; // Now to save all the answers and type-specific options $result = question_bank::get_qtype($question->qtype)->save_question_options($question); if (!empty($CFG->usetags) && isset($question->tags)) { require_once $CFG->dirroot . '/tag/lib.php'; tag_set('question', $question->id, $question->tags); } if (!empty($result->error)) { echo $OUTPUT->notification($result->error); return false; } if (!empty($result->notice)) { echo $OUTPUT->notification($result->notice); return true; } // Give the question a unique version stamp determined by question_hash() $DB->set_field('question', 'version', question_hash($question), array('id' => $question->id)); } return true; }
/** * 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; $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 // 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(); 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; } } // 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) { $urlinfo = parse_url($CFG->wwwroot); $hostname = $urlinfo['host']; 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; } // 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('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))); $a = new stdClass(); $a->courseshortname = $shortname; $a->forumname = $cleanforumname; $a->subject = format_string($post->subject, true); $postsubject = html_to_text(get_string('postmailsubject', 'forum', $a)); $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); $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. 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...'); 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/edit.php?id=' . $userid . '&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) { 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); 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"); $maildigest = forum_get_user_maildigest_bulk($digests, $userto, $forum->id); if ($maildigest == 2) { // Subjects and link only $posttext .= "\n"; $posttext .= $CFG->wwwroot . '/mod/forum/discuss.php?d=' . $discussion->id; $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}&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; } } } $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(' ', $footerlinks) . '</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: 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 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; }
/** * Resets the page customisations for all users. * * @param int $private Either MY_PAGE_PRIVATE or MY_PAGE_PUBLIC. * @param string $pagetype Either my-index or user-profile. * @return void */ function my_reset_page_for_all_users($private = MY_PAGE_PRIVATE, $pagetype = 'my-index') { global $DB; // This may take a while. Raise the execution time limit. core_php_time_limit::raise(); // Find all the user pages and all block instances in them. $sql = "SELECT bi.id\n FROM {my_pages} p\n JOIN {context} ctx ON ctx.instanceid = p.userid AND ctx.contextlevel = :usercontextlevel\n JOIN {block_instances} bi ON bi.parentcontextid = ctx.id AND\n bi.pagetypepattern = :pagetypepattern AND\n (bi.subpagepattern IS NULL OR bi.subpagepattern = " . $DB->sql_concat("''", 'p.id') . ")\n WHERE p.private = :private"; $params = array('private' => $private, 'usercontextlevel' => CONTEXT_USER, 'pagetypepattern' => $pagetype); $blockids = $DB->get_fieldset_sql($sql, $params); // Wrap the SQL queries in a transaction. $transaction = $DB->start_delegated_transaction(); // Delete the block instances. if (!empty($blockids)) { blocks_delete_instances($blockids); } // Finally delete the pages. $DB->delete_records_select('my_pages', 'userid IS NOT NULL AND private = :private', ['private' => $private]); // We should be good to go now. $transaction->allow_commit(); // Trigger dashboard has been reset event. $eventparams = array('context' => context_system::instance(), 'other' => array('private' => $private, 'pagetype' => $pagetype)); $event = \core\event\dashboards_reset::create($eventparams); $event->trigger(); }
** The client connection is not forever though. Once we reach ** CHAT_MAX_CLIENT_UPDATES, it will force the client to re-fetch it. ** ** This buys us all the benefits that chatd has, minus the setup, ** as we are using apache to do the daemon handling. ** **/ define('CHAT_MAX_CLIENT_UPDATES', 1000); define('NO_MOODLE_COOKIES', true); // Session not used here. define('NO_OUTPUT_BUFFERING', true); require '../../../config.php'; require '../lib.php'; // We are going to run for a long time. // Avoid being terminated by php. core_php_time_limit::raise(); $chatsid = required_param('chat_sid', PARAM_ALPHANUM); $chatlasttime = optional_param('chat_lasttime', 0, PARAM_INT); $chatlastrow = optional_param('chat_lastrow', 1, PARAM_INT); $chatlastid = optional_param('chat_lastid', 0, PARAM_INT); $url = new moodle_url('/mod/chat/gui_header_js/jsupdated.php', array('chat_sid' => $chatsid)); if ($chatlasttime !== 0) { $url->param('chat_lasttime', $chatlasttime); } if ($chatlastrow !== 1) { $url->param('chat_lastrow', $chatlastrow); } if ($chatlastid !== 1) { $url->param('chat_lastid', $chatlastid); } $PAGE->set_url($url);
private function update_all_languages($lang) { // TODO: Use above $lang to update a single language pack. global $CFG; require_once $CFG->libdir . '/filelib.php'; require_once $CFG->libdir . '/componentlib.class.php'; \core_php_time_limit::raise(); $installer = new \lang_installer(); if (!($availablelangs = $installer->get_remote_list_of_languages())) { print_error('cannotdownloadlanguageupdatelist', 'error'); } $md5array = array(); // (string)langcode => (string)md5 foreach ($availablelangs as $alang) { $md5array[$alang[0]] = $alang[1]; } // filter out unofficial packs $currentlangs = array_keys(get_string_manager()->get_list_of_translations(true)); $updateablelangs = array(); foreach ($currentlangs as $clang) { if (!array_key_exists($clang, $md5array)) { $notice_ok[] = get_string('langpackupdateskipped', 'tool_langimport', $clang); continue; } $dest1 = $CFG->dataroot . '/lang/' . $clang; $dest2 = $CFG->dirroot . '/lang/' . $clang; if (file_exists($dest1 . '/langconfig.php') || file_exists($dest2 . '/langconfig.php')) { $updateablelangs[] = $clang; } } // then filter out packs that have the same md5 key $neededlangs = array(); // all the packs that needs updating foreach ($updateablelangs as $ulang) { if (!$this->is_installed_lang($ulang, $md5array[$ulang])) { $neededlangs[] = $ulang; } } make_temp_directory(''); make_upload_directory('lang'); // clean-up currently installed versions of the packs foreach ($neededlangs as $packindex => $pack) { if ($pack == 'en') { continue; } // delete old directories $dest1 = $CFG->dataroot . '/lang/' . $pack; $dest2 = $CFG->dirroot . '/lang/' . $pack; $rm1 = false; $rm2 = false; if (file_exists($dest1)) { if (!remove_dir($dest1)) { $notice_error[] = 'Could not delete old directory ' . $dest1 . ', update of ' . $pack . ' failed, please check permissions.'; unset($neededlangs[$packindex]); continue; } } if (file_exists($dest2)) { if (!remove_dir($dest2)) { $notice_error[] = 'Could not delete old directory ' . $dest2 . ', update of ' . $pack . ' failed, please check permissions.'; unset($neededlangs[$packindex]); continue; } } } // install all needed language packs $installer->set_queue($neededlangs); $results = $installer->run(); $updated = false; // any packs updated? foreach ($results as $langcode => $langstatus) { switch ($langstatus) { case \lang_installer::RESULT_DOWNLOADERROR: $a = new stdClass(); $a->url = $installer->lang_pack_url($langcode); $a->dest = $CFG->dataroot . '/lang'; print_error('remotedownloaderror', 'error', 'index.php', $a); break; case \lang_installer::RESULT_INSTALLED: $updated = true; $notice_ok[] = get_string('langpackinstalled', 'tool_langimport', $langcode); break; case \lang_installer::RESULT_UPTODATE: $notice_ok[] = get_string('langpackuptodate', 'tool_langimport', $langcode); break; } } if ($updated) { $notice_ok[] = get_string('langupdatecomplete', 'tool_langimport'); } else { $notice_ok[] = get_string('nolangupdateneeded', 'tool_langimport'); } unset($installer); get_string_manager()->reset_caches(); }
/** * Sync all meta course links. * * @param progress_trace $trace * @param int $courseid one course, empty mean all * @return int 0 means ok, 1 means error, 2 means plugin disabled */ public function sync(progress_trace $trace, $courseid = null) { global $DB; if (!enrol_is_enabled('self')) { $trace->finished(); return 2; } // Unfortunately this may take a long time, execution can be interrupted safely here. core_php_time_limit::raise(); raise_memory_limit(MEMORY_HUGE); $trace->output('Verifying self-enrolments...'); $params = array('now' => time(), 'useractive' => ENROL_USER_ACTIVE, 'courselevel' => CONTEXT_COURSE); $coursesql = ""; if ($courseid) { $coursesql = "AND e.courseid = :courseid"; $params['courseid'] = $courseid; } // Note: the logic of self enrolment guarantees that user logged in at least once (=== u.lastaccess set) // and that user accessed course at least once too (=== user_lastaccess record exists). // First deal with users that did not log in for a really long time - they do not have user_lastaccess records. $sql = "SELECT e.*, ue.userid\n FROM {user_enrolments} ue\n JOIN {enrol} e ON (e.id = ue.enrolid AND e.enrol = 'self' AND e.customint2 > 0)\n JOIN {user} u ON u.id = ue.userid\n WHERE :now - u.lastaccess > e.customint2\n {$coursesql}"; $rs = $DB->get_recordset_sql($sql, $params); foreach ($rs as $instance) { $userid = $instance->userid; unset($instance->userid); $this->unenrol_user($instance, $userid); $days = $instance->customint2 / 60 * 60 * 24; $trace->output("unenrolling user {$userid} from course {$instance->courseid} as they have did not log in for at least {$days} days", 1); } $rs->close(); // Now unenrol from course user did not visit for a long time. $sql = "SELECT e.*, ue.userid\n FROM {user_enrolments} ue\n JOIN {enrol} e ON (e.id = ue.enrolid AND e.enrol = 'self' AND e.customint2 > 0)\n JOIN {user_lastaccess} ul ON (ul.userid = ue.userid AND ul.courseid = e.courseid)\n WHERE :now - ul.timeaccess > e.customint2\n {$coursesql}"; $rs = $DB->get_recordset_sql($sql, $params); foreach ($rs as $instance) { $userid = $instance->userid; unset($instance->userid); $this->unenrol_user($instance, $userid); $days = $instance->customint2 / 60 * 60 * 24; $trace->output("unenrolling user {$userid} from course {$instance->courseid} as they have did not access course for at least {$days} days", 1); } $rs->close(); $trace->output('...user self-enrolment updates finished.'); $trace->finished(); $this->process_expirations($trace, $courseid); return 0; }
function update_field() { global $DB, $OUTPUT; // Get the old field data so that we can check whether the thumbnail dimensions have changed $oldfield = $DB->get_record('data_fields', array('id' => $this->field->id)); $DB->update_record('data_fields', $this->field); // Have the thumbnail dimensions changed? if ($oldfield && ($oldfield->param4 != $this->field->param4 || $oldfield->param5 != $this->field->param5)) { // Check through all existing records and update the thumbnail if ($contents = $DB->get_records('data_content', array('fieldid' => $this->field->id))) { $fs = get_file_storage(); if (count($contents) > 20) { echo $OUTPUT->notification(get_string('resizingimages', 'data'), 'notifysuccess'); echo "\n\n"; // To make sure that ob_flush() has the desired effect ob_flush(); } foreach ($contents as $content) { if (!($file = $fs->get_file($this->context->id, 'mod_data', 'content', $content->id, '/', $content->content))) { continue; } if ($thumbfile = $fs->get_file($this->context->id, 'mod_data', 'content', $content->id, '/', 'thumb_' . $content->content)) { $thumbfile->delete(); } core_php_time_limit::raise(300); // Might be slow! $this->update_thumbnail($content, $file); } } } return true; }
/** * Executes the backup * @return void Throws and exception of completes */ public function execute_plan() { // Basic/initial prevention against time/memory limits core_php_time_limit::raise(1 * 60 * 60); // 1 hour for 1 course initially granted raise_memory_limit(MEMORY_EXTRA); // If this is not a course backup, inform the plan we are not // including all the activities for sure. This will affect any // task/step executed conditionally to stop including information // for section and activity backup. MDL-28180. if ($this->get_type() !== backup::TYPE_1COURSE) { $this->log('notifying plan about excluded activities by type', backup::LOG_DEBUG); $this->plan->set_excluding_activities(); } return $this->plan->execute(); }
/** * Import events from an iCalendar object into a course calendar. * * @param stdClass $ical The iCalendar object. * @param int $courseid The course ID for the calendar. * @param int $subscriptionid The subscription ID. * @return string A log of the import progress, including errors. */ function calendar_import_icalendar_events($ical, $courseid, $subscriptionid = null) { global $DB; $return = ''; $eventcount = 0; $updatecount = 0; // Large calendars take a while... if (!CLI_SCRIPT) { core_php_time_limit::raise(300); } // Mark all events in a subscription with a zero timestamp. if (!empty($subscriptionid)) { $sql = "UPDATE {event} SET timemodified = :time WHERE subscriptionid = :id"; $DB->execute($sql, array('time' => 0, 'id' => $subscriptionid)); } // Grab the timezone from the iCalendar file to be used later. if (isset($ical->properties['X-WR-TIMEZONE'][0]->value)) { $timezone = $ical->properties['X-WR-TIMEZONE'][0]->value; } else { $timezone = 'UTC'; } foreach ($ical->components['VEVENT'] as $event) { $res = calendar_add_icalendar_event($event, $courseid, $subscriptionid, $timezone); switch ($res) { case CALENDAR_IMPORT_EVENT_UPDATED: $updatecount++; break; case CALENDAR_IMPORT_EVENT_INSERTED: $eventcount++; break; case 0: $return .= '<p>' . get_string('erroraddingevent', 'calendar') . ': ' . (empty($event->properties['SUMMARY']) ? '(' . get_string('notitle', 'calendar') . ')' : $event->properties['SUMMARY'][0]->value) . " </p>\n"; break; } } $return .= "<p> " . get_string('eventsimported', 'calendar', $eventcount) . "</p>"; $return .= "<p> " . get_string('eventsupdated', 'calendar', $updatecount) . "</p>"; // Delete remaining zero-marked events since they're not in remote calendar. if (!empty($subscriptionid)) { $deletecount = $DB->count_records('event', array('timemodified' => 0, 'subscriptionid' => $subscriptionid)); if (!empty($deletecount)) { $sql = "DELETE FROM {event} WHERE timemodified = :time AND subscriptionid = :id"; $DB->execute($sql, array('time' => 0, 'id' => $subscriptionid)); $return .= "<p> " . get_string('eventsdeleted', 'calendar') . ": {$deletecount} </p>\n"; } } return $return; }