Kumi
f9ed77bcde
Implement enhanced category and course replication, improving the handling of both by ensuring new and existing categories are processed correctly. Introduce CLI support for bulk category imports using a dedicated flag and provide helper functions like `generateRandomString` and `removePath` for file management. Add a new `trigger_all.php` script to trigger replication for all courses and an `unenroll.php` script for unenrolling users with cleanup of related data. Includes error handling improvements and directory structure checks.
202 lines
5.8 KiB
PHP
202 lines
5.8 KiB
PHP
<?php
|
|
require_once("../../config.php");
|
|
require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php');
|
|
require_once($CFG->dirroot . "/backup/util/includes/restore_includes.php");
|
|
|
|
$timestamp = $_GET["timestamp"];
|
|
|
|
$replicationconfig = get_config('local_replication');
|
|
$directory = $replicationconfig->directory;
|
|
|
|
function local_replication_process_category($category, $parent_id = 0) {
|
|
global $DB;
|
|
|
|
$oldid = (int)$category['oldid'];
|
|
$name = (string)$category['name'];
|
|
|
|
$existing_category = \core_course_category::get($oldid, IGNORE_MISSING);
|
|
|
|
if ($existing_category->id) {
|
|
// Update name and parent if necessary
|
|
$updated_data = [];
|
|
if ($existing_category->name !== $name) {
|
|
$updated_data['name'] = $name;
|
|
}
|
|
if ($parent_id !== 0 && $existing_category->parent != $parent_id) {
|
|
$updated_data['parent'] = $parent_id;
|
|
}
|
|
|
|
if (!empty($updated_data)) {
|
|
// Update category
|
|
$existing_category->update($updated_data);
|
|
}
|
|
} else {
|
|
// Create categories up to the required ID
|
|
$max_id = $DB->get_field_sql("SELECT MAX(id) FROM {course_categories}");
|
|
while ($max_id < $oldid) {
|
|
$new_category_data = [
|
|
'name' => "Placeholder " . ($max_id + 1),
|
|
'parent' => $parent_id,
|
|
'idnumber' => '',
|
|
'visible' => 1
|
|
];
|
|
|
|
// Create placeholder category
|
|
\core_course_category::create($new_category_data);
|
|
$max_id = $DB->get_field_sql("SELECT MAX(id) FROM {course_categories}");
|
|
}
|
|
|
|
// Create target category
|
|
$new_category_data = [
|
|
'id' => $oldid,
|
|
'name' => $name,
|
|
'parent' => $parent_id,
|
|
'idnumber' => '',
|
|
'visible' => 1
|
|
];
|
|
\core_course_category::update($new_category_data);
|
|
}
|
|
|
|
// Process sub-categories
|
|
foreach ($category->category as $sub_category) {
|
|
local_replication_process_category($sub_category, $oldid);
|
|
}
|
|
}
|
|
|
|
if (isset($_GET["categories"])) {
|
|
$infile = $directory . DIRECTORY_SEPARATOR . "categories_" . $timestamp . ".xml";
|
|
$xml = simplexml_load_file($infile);
|
|
foreach ($xml->category as $category) {
|
|
local_replication_process_category($category);
|
|
}
|
|
echo("OK");
|
|
exit();
|
|
}
|
|
|
|
$courseid = $_GET["courseid"];
|
|
$categoryid = $_GET["categoryid"];
|
|
|
|
try {
|
|
$course = get_course($courseid);
|
|
} catch (Exception $ex) {
|
|
$course = null;
|
|
}
|
|
|
|
if (!$course) {
|
|
if (!get_course(((int) $courseid) - 1)) {
|
|
die("Cannot recreate course with this ID!");
|
|
}
|
|
}
|
|
|
|
if ($course && !$categoryid) {
|
|
$categoryid = $course->category;
|
|
}
|
|
|
|
$category = $DB->get_record('course_categories', array('id' => $categoryid), '*', MUST_EXIST);
|
|
|
|
$infile = $directory . DIRECTORY_SEPARATOR . "course_" . $courseid . "_" . $categoryid . "_" . $timestamp . ".mbz";
|
|
|
|
if (empty($CFG->tempdir)) {
|
|
$CFG->tempdir = $CFG->dataroot . DIRECTORY_SEPARATOR . 'temp';
|
|
}
|
|
|
|
if (!file_exists($infile)) {
|
|
die("Backup file '" . $infile . "' does not exist.");
|
|
}
|
|
|
|
if (!is_readable($infile)) {
|
|
die("Backup file '" . $infile . "' is not readable.");
|
|
}
|
|
|
|
$backupdir = "restore_" . uniqid();
|
|
|
|
if (isset($CFG->backuptempdir)){
|
|
$path = $CFG->backuptempdir . DIRECTORY_SEPARATOR . $backupdir;
|
|
}
|
|
else{
|
|
$path = $CFG->tempdir . DIRECTORY_SEPARATOR . "backup" . DIRECTORY_SEPARATOR . $backupdir;
|
|
}
|
|
|
|
$fp = get_file_packer('application/vnd.moodle.backup');
|
|
$fp->extract_to_pathname($infile, $path);
|
|
|
|
$xmlfile = $path . DIRECTORY_SEPARATOR . "course" . DIRECTORY_SEPARATOR . "course.xml";
|
|
|
|
$xml = simplexml_load_file($xmlfile);
|
|
$fullname = $xml->xpath('/course/fullname');
|
|
if (!$fullname) {
|
|
$fullname = $xml->xpath('/MOODLE_BACKUP/COURSE/HEADER/FULLNAME');
|
|
}
|
|
$shortname = $xml->xpath('/course/shortname');
|
|
if (!$shortname) {
|
|
$shortname = $xml->xpath('/MOODLE_BACKUP/COURSE/HEADER/SHORTNAME');
|
|
}
|
|
|
|
$fullname = (string)($fullname[0]);
|
|
$shortname = (string)($shortname[0]);
|
|
|
|
if (!$fullname) {
|
|
$fullname = $shortname;
|
|
}
|
|
|
|
if (!$course && $DB->get_record('course', array('category' => $category->id, 'shortname' => $shortname))) {
|
|
$matches = NULL;
|
|
preg_match('/(.*)_(\d+)$/', $shortname, $matches);
|
|
if ($matches) {
|
|
$base = $matches[1];
|
|
$number = $matches[2];
|
|
} else {
|
|
$base = $shortname;
|
|
$number = 1;
|
|
}
|
|
$shortname = $base . '_' . $number;
|
|
while ($DB->get_record('course', array('category' => $category->id, 'shortname' => $shortname))) {
|
|
$number++;
|
|
$shortname = $base . '_' . $number;
|
|
}
|
|
}
|
|
|
|
if ($course) {
|
|
echo "Overwriting current content of existing course -> Course ID: $courseid\n";
|
|
$rc = new restore_controller($backupdir, $courseid, backup::INTERACTIVE_NO,
|
|
backup::MODE_GENERAL, $USER->id, 0);
|
|
} else {
|
|
echo "Creating new course to restore backup\n";
|
|
$courseid = restore_dbops::create_new_course($fullname, $shortname, $category->id);
|
|
$rc = new restore_controller($backupdir, $courseid, backup::INTERACTIVE_NO,
|
|
backup::MODE_GENERAL, $USER->id, backup::TARGET_NEW_COURSE);
|
|
}
|
|
|
|
if ($rc->get_status() == backup::STATUS_REQUIRE_CONV) {
|
|
$rc->convert();
|
|
}
|
|
|
|
$plan = $rc->get_plan();
|
|
|
|
if (!$rc->execute_precheck()){
|
|
$check = $rc->get_precheck_results();
|
|
echo("Restore pre-check failed!");
|
|
var_dump($check);
|
|
die();
|
|
}
|
|
|
|
$deletingoptions = array();
|
|
$deletingoptions['keep_roles_and_enrolments'] = 1;
|
|
$deletingoptions['keep_groups_and_groupings'] = 1;
|
|
restore_dbops::delete_course_content($courseid, $deletingoptions);
|
|
|
|
$rc->execute_plan();
|
|
$rc->destroy();
|
|
|
|
#unlink($infile);
|
|
|
|
$course = get_course($courseid);
|
|
|
|
$course->category = $categoryid;
|
|
$course->fullname = $fullname;
|
|
$course->shortname = $shortname;
|
|
|
|
$DB->update_record('course', $course);
|
|
|
|
echo "New course ID for '$shortname': $courseid in category {$category->id}\n";
|
|
echo "OK"
|