moodle-local_replication/import_cli.php

328 lines
9.9 KiB
PHP
Raw Permalink Normal View History

2022-08-22 09:52:55 +00:00
<?php
define('CLI_SCRIPT', true);
require_once("../../config.php");
require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php');
require_once($CFG->dirroot . "/backup/util/includes/restore_includes.php");
require_once($CFG->libdir . '/clilib.php');
function generateRandomString($length = 10) {
$characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
$charactersLength = strlen($characters);
$randomString = '';
for ($i = 0; $i < $length; $i++) {
$randomString .= $characters[rand(0, $charactersLength - 1)];
}
return $randomString;
}
function removePath($path) {
if(is_dir($path)) {
$handler = opendir($path);
if (!$handler) {
trigger_error('File Error: Failed to open the directory ' . $path, E_USER_ERROR);
return;
}
// list the files in the directory
while ($file = readdir($handler)) {
if ($file != '.' && $file != '..')
removePath($path.DIRECTORY_SEPARATOR.$file);
}
// tidy up: close the handler
closedir($handler);
if (!rmdir($path)) {
#trigger_error('File Error: Failed to remove folder ' . $path, E_USER_ERROR);
}
}
else {
// delete it
if (!unlink($path)) {
#trigger_error('File Error: Failed to remove file ' . $path, E_USER_ERROR);
}
}
}
function 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
];
$existing_category = \core_course_category::get($oldid, MUST_EXIST, true);
$existing_category->update($new_category_data);
}
// Process sub-categories
foreach ($category->category as $sub_category) {
process_category($sub_category, $oldid);
}
}
2022-08-22 09:52:55 +00:00
$usage = "Import a course from ContentMonster
Usage:
# php import_cli.php --courseid=<value> --categoryid=<value> --timestamp=<value> [--source=<value>] [--categories]
2022-08-22 09:52:55 +00:00
# php import_cli.php [--help|-h]
Options:
-h --help Print this help.
--paramname=<value> Describe the parameter and the meaning of its values.
";
list($options, $unrecognised) = cli_get_params([
'help' => false,
'categories' => false,
2022-08-22 09:52:55 +00:00
'courseid' => null,
'categoryid' => null,
'timestamp' => null,
'source' => null,
], [
'h' => 'help',
'c' => 'categories'
2022-08-22 09:52:55 +00:00
]);
if ($unrecognised) {
$unrecognised = implode(PHP_EOL . ' ', $unrecognised);
cli_error(get_string('cliunknowoption', 'core_admin', $unrecognised));
}
if ($options['help']) {
cli_writeln($usage);
exit(2);
}
$replicationconfig = get_config('local_replication');
$directory = $replicationconfig->directory;
2022-08-22 09:52:55 +00:00
if (empty($options['timestamp'])) {
cli_error('Missing mandatory argument timestamp.', 2);
}
$timestamp = $options["timestamp"];
2022-09-12 16:00:09 +00:00
if (!empty($options['categories'])) {
$path = $directory . DIRECTORY_SEPARATOR . "categories_$timestamp.xml";
$xml = simplexml_load_file($path);
foreach ($xml->category as $category) {
process_category($category);
}
removePath($path);
echo("OK");
exit();
}
2022-09-12 16:00:09 +00:00
if (empty($options['courseid'])) {
cli_error('Missing mandatory argument courseid.', 2);
}
2022-09-12 16:00:09 +00:00
if (empty($options['categoryid'])) {
cli_error('Missing mandatory argument categoryid.', 2);
2022-09-12 16:00:09 +00:00
}
2022-08-22 09:52:55 +00:00
$courseid = $options['courseid'];
$categoryid = $options["categoryid"];
try {
$course = get_course($courseid);
} catch (Exception $ex) {
$course = null;
}
try {
$category = $DB->get_record('course_categories', array('id' => $categoryid), '*', MUST_EXIST);
} catch (Exception $ex) {
cli_error('Category " . $categoryid . " does not exist.');
}
if (!$course) {
while (!$DB->get_record('course', array('id' => ((int) $courseid) - 1), '*')) {
$name = generateRandomString();
2022-09-12 16:00:09 +00:00
$newcourse = restore_dbops::create_new_course("Placeholder " . $name, $name, $category->id);
if ((int) $newcourse > (int) $courseid) {
2022-09-12 16:00:09 +00:00
cli_error('Did you delete courses by any chance? Cannot create course ' . $courseid . '.');
}
2022-08-22 09:52:55 +00:00
}
}
$infile = $directory . DIRECTORY_SEPARATOR . "course_" . $courseid . "_" . $categoryid . "_" . $timestamp . ".mbz";
if (empty($CFG->tempdir)) {
$CFG->tempdir = $CFG->dataroot . DIRECTORY_SEPARATOR . 'temp';
}
if (!file_exists($infile)) {
cli_error("Backup file '" . $infile . "' does not exist.");
}
if (!is_readable($infile)) {
cli_error("Backup file '" . $infile . "' is not readable.");
}
$backupdir = "restore_" . uniqid();
2022-09-12 16:00:09 +00:00
if (isset($CFG->backuptempdir)){
2022-08-22 09:52:55 +00:00
$path = $CFG->backuptempdir . DIRECTORY_SEPARATOR . $backupdir;
2022-09-12 16:00:09 +00:00
}
else{
2022-08-22 09:52:55 +00:00
$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;
}
$questionfile = $path . DIRECTORY_SEPARATOR . "questions.xml";
if (file_exists($questionfile)) {
$questions = new DOMDocument();
$questions->load($questionfile);
$questions->save($questionfile . ".bak");
2022-09-12 16:00:09 +00:00
foreach ($questions->getElementsByTagName("question") as $question) {
$questiontext = $question->getElementsByTagName("questiontext")->item(0);
$newquestiontext = strip_tags(html_entity_decode($questiontext->textContent));
2022-09-12 16:00:09 +00:00
$questiontext->removeChild($questiontext->firstChild);
$questiontext->appendChild(new DOMText($newquestiontext));
foreach ($question->getElementsByTagName("answer") as $answer) {
$answertext = $answer->getElementsByTagName("answertext")->item(0);
$newanswertext = strip_tags(html_entity_decode($answertext->textContent));
2022-09-12 16:00:09 +00:00
$answertext->removeChild($answertext->firstChild);
$answertext->appendChild(new DOMText($newanswertext));
}
}
$questions->save($questionfile);
}
2022-08-22 09:52:55 +00:00
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) {
cli_writeln("Overwriting current content of existing course -> Course ID: $courseid");
2022-09-12 16:00:09 +00:00
$rc = new restore_controller($backupdir, $courseid, backup::INTERACTIVE_NO,
backup::MODE_GENERAL, 2, 0);
$rc->get_plan()->get_setting('overwrite_conf')->set_value(true);
2023-01-27 13:16:18 +00:00
$rc->get_plan()->get_setting('keep_roles_and_enrolments')->set_value(true);
$rc->get_plan()->get_setting('keep_groups_and_groupings')->set_value(true);
2022-09-12 16:00:09 +00:00
2022-08-22 09:52:55 +00:00
} else {
cli_writeln("Creating new course to restore backup");
$courseid = restore_dbops::create_new_course($fullname, $shortname, $category->id);
2022-09-12 16:00:09 +00:00
$rc = new restore_controller($backupdir, $courseid, backup::INTERACTIVE_NO,
backup::MODE_GENERAL, 2, backup::TARGET_NEW_COURSE);
2022-08-22 09:52:55 +00:00
}
if ($rc->get_status() == backup::STATUS_REQUIRE_CONV) {
$rc->convert();
}
$plan = $rc->get_plan();
2022-09-12 16:00:09 +00:00
if (!$rc->execute_precheck()){
2022-08-22 09:52:55 +00:00
$check = $rc->get_precheck_results();
cli_error("Restore pre-check failed!");
}
$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();
$course = get_course($courseid);
$course->category = $categoryid;
$course->fullname = $fullname;
$course->shortname = $shortname;
$DB->update_record('course', $course);
try {
removePath($path);
} catch (\Throwable $th) {
cli_writeln("Could not remove temporary files. Please remove them manually.");
}
2022-09-12 16:00:09 +00:00
2022-08-22 09:52:55 +00:00
cli_writeln("New course ID for '$shortname': $courseid in category {$category->id}");
cli_writeln("OK");