diff --git a/main/coursecopy/export_moodle.php b/main/coursecopy/export_moodle.php
index 50b632f2675..7ed6ec88f6b 100644
--- a/main/coursecopy/export_moodle.php
+++ b/main/coursecopy/export_moodle.php
@@ -138,7 +138,7 @@
$exportDir = 'moodle_export_'.$courseId;
try {
- $moodleVersion = isset($values['moodle_version']) ? $values['moodle_version'] : '3';
+ $moodleVersion = $values['moodle_version'] ?? '3';
$mbzFile = $exporter->export($courseId, $exportDir, $moodleVersion);
echo Display::return_message(get_lang('MoodleExportCreated'), 'confirm');
echo '
';
diff --git a/main/inc/lib/moodleexport/ActivityExport.php b/main/inc/lib/moodleexport/ActivityExport.php
index 2c054454fb4..e7cd8951326 100644
--- a/main/inc/lib/moodleexport/ActivityExport.php
+++ b/main/inc/lib/moodleexport/ActivityExport.php
@@ -33,6 +33,7 @@ public function getSectionIdForActivity(int $activityId, string $itemType): int
{
foreach ($this->course->resources[RESOURCE_LEARNPATH] as $learnpath) {
foreach ($learnpath->items as $item) {
+ $item['item_type'] = $item['item_type'] === 'student_publication' ? 'work' : $item['item_type'];
if ($item['item_type'] == $itemType && $item['path'] == $activityId) {
return $learnpath->source_id;
}
diff --git a/main/inc/lib/moodleexport/AssignExport.php b/main/inc/lib/moodleexport/AssignExport.php
index 72f6263ea34..77a3644244e 100644
--- a/main/inc/lib/moodleexport/AssignExport.php
+++ b/main/inc/lib/moodleexport/AssignExport.php
@@ -49,6 +49,12 @@ public function getData(int $assignId, int $sectionId): ?array
{
$work = $this->course->resources[RESOURCE_WORK][$assignId];
+ if (empty($work->params['id']) || empty($work->params['title'])) {
+ return null;
+ }
+
+ $sentDate = !empty($work->params['sent_date']) ? strtotime($work->params['sent_date']) : time();
+
$workFiles = getAllDocumentToWork($assignId, $this->course->info['real_id']);
$files = [];
if (!empty($workFiles)) {
@@ -73,11 +79,11 @@ public function getData(int $assignId, int $sectionId): ?array
'contextid' => $this->course->info['real_id'],
'sectionid' => $sectionId,
'sectionnumber' => 0,
- 'name' => htmlspecialchars($work->params['title']),
- 'intro' => $work->params['description'],
- 'duedate' => strtotime($work->params['sent_date']),
- 'gradingduedate' => strtotime($work->params['sent_date']) + 86400 * 7,
- 'allowsubmissionsfromdate' => strtotime($work->params['sent_date']),
+ 'name' => htmlspecialchars($work->params['title'], ENT_QUOTES),
+ 'intro' => htmlspecialchars($work->params['description'], ENT_QUOTES),
+ 'duedate' => $sentDate,
+ 'gradingduedate' => $sentDate + 7 * 86400,
+ 'allowsubmissionsfromdate' => $sentDate,
'timemodified' => time(),
'grade_item_id' => 0,
'files' => $files,
diff --git a/main/inc/lib/moodleexport/FileExport.php b/main/inc/lib/moodleexport/FileExport.php
index 77ff305d72a..4f982ec3628 100644
--- a/main/inc/lib/moodleexport/FileExport.php
+++ b/main/inc/lib/moodleexport/FileExport.php
@@ -186,6 +186,14 @@ private function createFileXmlEntry(array $file): string
*/
private function processDocument(array $filesData, object $document): array
{
+ if (
+ $document->file_type === 'file' &&
+ isset($this->course->used_page_doc_ids) &&
+ in_array($document->source_id, $this->course->used_page_doc_ids)
+ ) {
+ return $filesData;
+ }
+
if (
$document->file_type === 'file' &&
pathinfo($document->path, PATHINFO_EXTENSION) === 'html' &&
@@ -296,7 +304,7 @@ private function ensureTrailingSlash(string $path): string
/**
* Get MIME type based on the file extension.
*/
- private function getMimeType($filePath): string
+ public function getMimeType($filePath): string
{
$extension = pathinfo($filePath, PATHINFO_EXTENSION);
$mimeTypes = $this->getMimeTypes();
diff --git a/main/inc/lib/moodleexport/MoodleExport.php b/main/inc/lib/moodleexport/MoodleExport.php
index af26de5b19d..bc8d7cf6e80 100644
--- a/main/inc/lib/moodleexport/MoodleExport.php
+++ b/main/inc/lib/moodleexport/MoodleExport.php
@@ -69,8 +69,15 @@ public function export(string $courseId, string $exportDir, int $version)
$courseExport->exportCourse($tempDir);
// Export files-related data and actual files
+ $pageExport = new PageExport($this->course);
+ $pageFiles = [];
+ $pageData = $pageExport->getData(0, 1);
+ if (!empty($pageData['files'])) {
+ $pageFiles = $pageData['files'];
+ }
$fileExport = new FileExport($this->course);
$filesData = $fileExport->getFilesData();
+ $filesData['files'] = array_merge($filesData['files'], $pageFiles);
$fileExport->exportFiles($filesData, $tempDir);
// Export sections of the course
@@ -98,8 +105,12 @@ public function exportQuestionsXml(array $questionsData, string $exportDir): voi
$xmlContent .= ''.PHP_EOL;
foreach ($questionsData as $quiz) {
- $categoryId = $quiz['questions'][0]['questioncategoryid'] ?? '0';
-
+ $categoryId = $quiz['questions'][0]['questioncategoryid'] ?? '1';
+ $hash = md5($categoryId . $quiz['name']);
+ if (isset($categoryHashes[$hash])) {
+ continue;
+ }
+ $categoryHashes[$hash] = true;
$xmlContent .= ' '.PHP_EOL;
$xmlContent .= ' Default for '.htmlspecialchars($quiz['name'] ?? 'Unknown').''.PHP_EOL;
$xmlContent .= ' '.($quiz['contextid'] ?? '0').''.PHP_EOL;
diff --git a/main/inc/lib/moodleexport/PageExport.php b/main/inc/lib/moodleexport/PageExport.php
index b4bdeeae130..7e69deef9d1 100644
--- a/main/inc/lib/moodleexport/PageExport.php
+++ b/main/inc/lib/moodleexport/PageExport.php
@@ -46,11 +46,50 @@ public function getData(int $pageId, int $sectionId): ?array
{
$contextid = $this->course->info['real_id'];
if ($pageId === 0) {
- if (
- isset($this->course->resources[RESOURCE_TOOL_INTRO]['course_homepage']) &&
- is_object($this->course->resources[RESOURCE_TOOL_INTRO]['course_homepage']) &&
- !empty($this->course->resources[RESOURCE_TOOL_INTRO]['course_homepage']->intro_text)
- ) {
+ $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,
@@ -58,13 +97,13 @@ public function getData(int $pageId, int $sectionId): ?array
'contextid' => $contextid,
'name' => get_lang('Introduction'),
'intro' => '',
- 'content' => trim($this->course->resources[RESOURCE_TOOL_INTRO]['course_homepage']->intro_text),
+ 'content' => $this->normalizeContent($introText),
'sectionid' => $sectionId,
'sectionnumber' => 1,
'display' => 0,
'timemodified' => time(),
'users' => [],
- 'files' => [],
+ 'files' => $files,
];
}
}
@@ -79,7 +118,7 @@ public function getData(int $pageId, int $sectionId): ?array
'contextid' => $contextid,
'name' => $page->title,
'intro' => $page->comment ?? '',
- 'content' => $this->getPageContent($page),
+ 'content' => $this->normalizeContent($this->getPageContent($page)),
'sectionid' => $sectionId,
'sectionnumber' => 1,
'display' => 0,
@@ -117,6 +156,24 @@ private function createPageXml(array $pageData, string $pageDir): void
$this->createXmlFile('page', $xmlContent, $pageDir);
}
+ private function normalizeContent(string $html): string
+ {
+ return preg_replace_callback(
+ '#
]+src=["\'](?[^"\']+)["\']#i',
+ function ($match) {
+ $src = $match['url'];
+
+ if (preg_match('#/courses/[^/]+/document/(.+)$#', $src, $parts)) {
+ $filename = basename($parts[1]);
+ return str_replace($src, '@@PLUGINFILE@@/Documents/' . $filename, $match[0]);
+ }
+
+ return $match[0];
+ },
+ $html
+ );
+ }
+
/**
* Retrieves the content of the page.
*/
diff --git a/main/inc/lib/moodleexport/QuizExport.php b/main/inc/lib/moodleexport/QuizExport.php
index efa39d41467..7cd6794b6ba 100644
--- a/main/inc/lib/moodleexport/QuizExport.php
+++ b/main/inc/lib/moodleexport/QuizExport.php
@@ -172,11 +172,13 @@ private function getQuestionsForQuiz(int $quizId): array
foreach ($quizResources as $questionId => $questionData) {
if (in_array($questionId, $this->course->resources[RESOURCE_QUIZ][$quizId]->obj->question_ids)) {
+ $categoryId = $questionData->question_category ?? 0;
+ $categoryId = $categoryId > 0 ? $categoryId : $this->getDefaultCategoryId();
$questions[] = [
'id' => $questionData->source_id,
'questiontext' => $questionData->question,
'qtype' => $this->mapQuestionType($questionData->quiz_type),
- 'questioncategoryid' => $questionData->question_category ?? 0,
+ 'questioncategoryid' => $categoryId,
'answers' => $this->getAnswersForQuestion($questionData->source_id),
'maxmark' => $questionData->ponderation ?? 1,
];
@@ -246,6 +248,11 @@ private function getFeedbacksForQuiz(int $quizId): array
return $feedbacks;
}
+ private function getDefaultCategoryId(): int
+ {
+ return 1;
+ }
+
/**
* Creates the quiz.xml file.
*/
diff --git a/main/inc/lib/moodleexport/SectionExport.php b/main/inc/lib/moodleexport/SectionExport.php
index e87a23262ef..2a83bc4790a 100644
--- a/main/inc/lib/moodleexport/SectionExport.php
+++ b/main/inc/lib/moodleexport/SectionExport.php
@@ -255,7 +255,7 @@ private function addActivityToList(array $item, int $sectionId, array &$activiti
}
$itemType = $item['item_type'] === 'link' ? 'url' :
- ($item['item_type'] === 'work' ? 'assign' :
+ ($item['item_type'] === 'work' || $item['item_type'] === 'student_publication' ? 'assign' :
($item['item_type'] === 'survey' ? 'feedback' : $item['item_type']));
switch ($itemType) {