Upgrade 1-11.38

This commit is contained in:
xesmyd
2026-03-30 14:10:30 +02:00
parent f2a7e6d1fc
commit ac648ef29d
24665 changed files with 69682 additions and 2205004 deletions
+110 -104
View File
@@ -11,6 +11,9 @@ namespace moodleexport;
*/
class PageExport extends ActivityExport
{
public const INTRO_PAGE_MODULE_ID = 910000000;
private const CONTENT_REVISION = 1;
/**
* Export a page to the specified directory.
*
@@ -21,13 +24,21 @@ class PageExport extends ActivityExport
*/
public function export($activityId, $exportDir, $moduleId, $sectionId): void
{
// Prepare the directory where the page export will be saved
$pageDir = $this->prepareActivityDirectory($exportDir, 'page', $moduleId);
$effectiveModuleId = (int) $moduleId;
if ($effectiveModuleId <= 0 && (int) $activityId === 0) {
$effectiveModuleId = self::INTRO_PAGE_MODULE_ID;
}
if ($effectiveModuleId <= 0) {
$effectiveModuleId = (int) $activityId;
}
// Retrieve page data
$pageData = $this->getData($activityId, $sectionId);
$pageDir = $this->prepareActivityDirectory($exportDir, 'page', $effectiveModuleId);
$pageData = $this->getData((int) $activityId, (int) $sectionId, $effectiveModuleId);
if (empty($pageData)) {
return;
}
// Generate XML files
$this->createPageXml($pageData, $pageDir);
$this->createModuleXml($pageData, $pageDir);
$this->createGradesXml($pageData, $pageDir);
@@ -42,91 +53,87 @@ class PageExport extends ActivityExport
/**
* Get page data dynamically from the course.
*/
public function getData(int $pageId, int $sectionId): ?array
public function getData(int $pageId, int $sectionId, ?int $moduleId = null): ?array
{
$contextid = $this->course->info['real_id'];
if ($pageId === 0) {
$introText = trim($this->course->resources[RESOURCE_TOOL_INTRO]['course_homepage']->intro_text ?? '');
if (!empty($introText)) {
$files = [];
$resources = \DocumentManager::get_resources_from_source_html($introText);
$courseInfo = api_get_course_info($this->course->code);
$adminId = MoodleExport::getAdminUserData()['id'];
foreach ($resources as [$src]) {
if (preg_match('#/document(/[^"\']+)#', $src, $matches)) {
$path = $matches[1];
$docId = \DocumentManager::get_document_id($courseInfo, $path);
if ($docId) {
$this->course->used_page_doc_ids[] = $docId;
$document = \DocumentManager::get_document_data_by_id($docId, $this->course->code);
if ($document) {
$contenthash = hash('sha1', basename($document['path']));
$mimetype = (new FileExport($this->course))->getMimeType($document['path']);
$files[] = [
'id' => $document['id'],
'contenthash' => $contenthash,
'contextid' => $contextid,
'component' => 'mod_page',
'filearea' => 'content',
'itemid' => 1,
'filepath' => '/Documents/',
'documentpath' => 'document' . $document['path'],
'filename' => basename($document['path']),
'userid' => $adminId,
'filesize' => $document['size'],
'mimetype' => $mimetype,
'status' => 0,
'timecreated' => time() - 3600,
'timemodified' => time(),
'source' => $document['title'],
'author' => 'Unknown',
'license' => 'allrightsreserved',
];
}
}
}
}
return [
'id' => 0,
'moduleid' => 0,
'modulename' => 'page',
'contextid' => $contextid,
'name' => get_lang('Introduction'),
'intro' => '',
'content' => $this->normalizeContent($introText),
'sectionid' => $sectionId,
'sectionnumber' => 1,
'display' => 0,
'timemodified' => time(),
'users' => [],
'files' => $files,
];
$introText = trim((string) ($this->course->resources[RESOURCE_TOOL_INTRO]['course_homepage']->intro_text ?? ''));
if ($introText === '') {
return null;
}
$effectiveModuleId = (int) ($moduleId ?? self::INTRO_PAGE_MODULE_ID);
if ($effectiveModuleId <= 0) {
$effectiveModuleId = self::INTRO_PAGE_MODULE_ID;
}
$introResult = $this->extractEmbeddedFilesAndNormalizeContent(
$introText,
$effectiveModuleId,
'mod_page',
'content',
0,
fn (int $sequence): int => $this->buildPageFileId($effectiveModuleId, $effectiveModuleId, $sequence, true)
);
return [
'id' => $effectiveModuleId,
'moduleid' => $effectiveModuleId,
'modulename' => 'page',
'contextid' => $effectiveModuleId,
'name' => $this->sanitizeMoodleActivityName((string) get_lang('Introduction'), 255),
'intro' => '',
'content' => $introResult['content'],
'sectionid' => $sectionId,
'sectionnumber' => 1,
'display' => 5,
'timemodified' => time(),
'users' => [],
'files' => $introResult['files'],
];
}
$pageResources = $this->course->resources[RESOURCE_DOCUMENT] ?? [];
foreach ($pageResources as $page) {
if ($page->source_id == $pageId) {
return [
'id' => $page->source_id,
'moduleid' => $page->source_id,
'modulename' => 'page',
'contextid' => $contextid,
'name' => $page->title,
'intro' => $page->comment ?? '',
'content' => $this->normalizeContent($this->getPageContent($page)),
'sectionid' => $sectionId,
'sectionnumber' => 1,
'display' => 0,
'timemodified' => time(),
'users' => [],
'files' => [],
];
if ((int) $page->source_id !== $pageId) {
continue;
}
$effectiveModuleId = (int) ($moduleId ?? $page->source_id);
if ($effectiveModuleId <= 0) {
$effectiveModuleId = (int) $page->source_id;
}
$pageName = (string) ($page->title ?? '');
if ($sectionId > 0) {
$pageName = $this->lpItemTitle($sectionId, RESOURCE_DOCUMENT, $pageId, $pageName);
}
$pageName = $this->sanitizeMoodleActivityName($pageName, 255);
$rawContent = $this->getPageContent($page);
$pageResult = $this->extractEmbeddedFilesAndNormalizeContent(
$rawContent,
$effectiveModuleId,
'mod_page',
'content',
0,
fn (int $sequence): int => $this->buildPageFileId($effectiveModuleId, $effectiveModuleId, $sequence, false)
);
return [
'id' => (int) $page->source_id,
'moduleid' => $effectiveModuleId,
'modulename' => 'page',
'contextid' => $effectiveModuleId,
'name' => $pageName,
'intro' => (string) ($page->comment ?? ''),
'content' => $pageResult['content'],
'sectionid' => $sectionId,
'sectionnumber' => 1,
'display' => 5,
'timemodified' => time(),
'users' => [],
'files' => $pageResult['files'],
];
}
return null;
@@ -140,15 +147,15 @@ class PageExport extends ActivityExport
$xmlContent = '<?xml version="1.0" encoding="UTF-8"?>'.PHP_EOL;
$xmlContent .= '<activity id="'.$pageData['id'].'" moduleid="'.$pageData['moduleid'].'" modulename="page" contextid="'.$pageData['contextid'].'">'.PHP_EOL;
$xmlContent .= ' <page id="'.$pageData['id'].'">'.PHP_EOL;
$xmlContent .= ' <name>'.htmlspecialchars($pageData['name']).'</name>'.PHP_EOL;
$xmlContent .= ' <intro>'.htmlspecialchars($pageData['intro']).'</intro>'.PHP_EOL;
$xmlContent .= ' <name>'.htmlspecialchars((string) $pageData['name']).'</name>'.PHP_EOL;
$xmlContent .= ' <intro><![CDATA['.(string) $pageData['intro'].']]></intro>'.PHP_EOL;
$xmlContent .= ' <introformat>1</introformat>'.PHP_EOL;
$xmlContent .= ' <content>'.htmlspecialchars($pageData['content']).'</content>'.PHP_EOL;
$xmlContent .= ' <content><![CDATA['.(string) $pageData['content'].']]></content>'.PHP_EOL;
$xmlContent .= ' <contentformat>1</contentformat>'.PHP_EOL;
$xmlContent .= ' <legacyfiles>0</legacyfiles>'.PHP_EOL;
$xmlContent .= ' <display>5</display>'.PHP_EOL;
$xmlContent .= ' <display>'.(int) ($pageData['display'] ?? 5).'</display>'.PHP_EOL;
$xmlContent .= ' <displayoptions>a:3:{s:12:"printheading";s:1:"1";s:10:"printintro";s:1:"0";s:17:"printlastmodified";s:1:"1";}</displayoptions>'.PHP_EOL;
$xmlContent .= ' <revision>1</revision>'.PHP_EOL;
$xmlContent .= ' <revision>'.self::CONTENT_REVISION.'</revision>'.PHP_EOL;
$xmlContent .= ' <timemodified>'.$pageData['timemodified'].'</timemodified>'.PHP_EOL;
$xmlContent .= ' </page>'.PHP_EOL;
$xmlContent .= '</activity>';
@@ -156,22 +163,16 @@ class PageExport extends ActivityExport
$this->createXmlFile('page', $xmlContent, $pageDir);
}
private function normalizeContent(string $html): string
/**
* Build a unique files.xml id for page embedded files.
*/
private function buildPageFileId(int $moduleId, int $contextId, int $sequence, bool $isIntro): int
{
return preg_replace_callback(
'#<img[^>]+src=["\'](?<url>[^"\']+)["\']#i',
function ($match) {
$src = $match['url'];
if ($isIntro) {
return 1150000000 + max(0, $contextId) + max(1, $sequence);
}
if (preg_match('#/courses/[^/]+/document/(.+)$#', $src, $parts)) {
$filename = basename($parts[1]);
return str_replace($src, '@@PLUGINFILE@@/Documents/' . $filename, $match[0]);
}
return $match[0];
},
$html
);
return 1100000000 + max(0, $moduleId) + max(1, $sequence);
}
/**
@@ -179,10 +180,15 @@ class PageExport extends ActivityExport
*/
private function getPageContent(object $page): string
{
if ($page->file_type === 'file') {
return file_get_contents($this->course->path.$page->path);
if (($page->file_type ?? '') !== 'file') {
return '';
}
return '';
$absolutePath = $this->course->path.$page->path;
if (!is_file($absolutePath)) {
return '';
}
return (string) file_get_contents($absolutePath);
}
}