diff --git a/main/inc/lib/moodleexport/ActivityExport.php b/main/inc/lib/moodleexport/ActivityExport.php index e7cd8951326..d87c0636fba 100644 --- a/main/inc/lib/moodleexport/ActivityExport.php +++ b/main/inc/lib/moodleexport/ActivityExport.php @@ -14,6 +14,7 @@ abstract class ActivityExport { protected $course; + public const DOCS_MODULE_ID = 1000000; public function __construct($course) { @@ -252,4 +253,26 @@ protected function createCalendarXml(array $activityData, string $destinationDir $this->createXmlFile('calendar', $xmlContent, $destinationDir); } + + /** + * Returns the title of the item in the LP (if it exists); otherwise, $fallback. + */ + protected function lpItemTitle(int $sectionId, string $itemType, int $resourceId, ?string $fallback): string + { + if (!isset($this->course->resources[RESOURCE_LEARNPATH])) { + return $fallback ?? ''; + } + foreach ($this->course->resources[RESOURCE_LEARNPATH] as $lp) { + if ((int) $lp->source_id !== $sectionId || empty($lp->items)) { + continue; + } + foreach ($lp->items as $it) { + $type = $it['item_type'] === 'student_publication' ? 'work' : $it['item_type']; + if ($type === $itemType && (int) $it['path'] === $resourceId) { + return $it['title'] ?? ($fallback ?? ''); + } + } + } + return $fallback ?? ''; + } } diff --git a/main/inc/lib/moodleexport/FileExport.php b/main/inc/lib/moodleexport/FileExport.php index 4f982ec3628..dc17b7c14ea 100644 --- a/main/inc/lib/moodleexport/FileExport.php +++ b/main/inc/lib/moodleexport/FileExport.php @@ -256,22 +256,23 @@ private function getFileData(object $document): array /** * Get file data for files inside a folder. */ - private function getFolderFileData(array $file, int $sourceId, string $parentPath = '/Documents/'): array + private function getFolderFileData(array $file, int $sourceId, string $parentPath = '/'): array { $adminData = MoodleExport::getAdminUserData(); $adminId = $adminData['id']; $contenthash = hash('sha1', basename($file['path'])); $mimetype = $this->getMimeType($file['path']); $filename = basename($file['path']); - $filepath = $this->ensureTrailingSlash($parentPath); + $relDir = dirname($file['path']); + $filepath = $this->ensureTrailingSlash($relDir === '.' ? '/' : '/'.$relDir.'/'); return [ 'id' => $file['id'], 'contenthash' => $contenthash, - 'contextid' => $sourceId, + 'contextid' => ActivityExport::DOCS_MODULE_ID, 'component' => 'mod_folder', 'filearea' => 'content', - 'itemid' => (int) $file['id'], + 'itemid' => ActivityExport::DOCS_MODULE_ID, 'filepath' => $filepath, 'documentpath' => 'document/'.$file['path'], 'filename' => $filename, diff --git a/main/inc/lib/moodleexport/FolderExport.php b/main/inc/lib/moodleexport/FolderExport.php index a1b1ac6dd0f..0ba0295e823 100644 --- a/main/inc/lib/moodleexport/FolderExport.php +++ b/main/inc/lib/moodleexport/FolderExport.php @@ -44,12 +44,12 @@ public function export($activityId, $exportDir, $moduleId, $sectionId): void */ public function getData(int $folderId, int $sectionId): ?array { - if ($folderId === 0) { + if ($folderId === 0 || $folderId === ActivityExport::DOCS_MODULE_ID) { return [ - 'id' => 0, - 'moduleid' => 0, + 'id' => ActivityExport::DOCS_MODULE_ID, + 'moduleid' => ActivityExport::DOCS_MODULE_ID, 'modulename' => 'folder', - 'contextid' => 0, + 'contextid' => ActivityExport::DOCS_MODULE_ID, 'name' => 'Documents', 'sectionid' => $sectionId, 'timemodified' => time(), @@ -98,17 +98,13 @@ private function createFolderXml(array $folderData, string $folderDir): void private function getFilesForFolder(int $folderId): array { $files = []; - if ($folderId === 0) { + if ($folderId === ActivityExport::DOCS_MODULE_ID) { foreach ($this->course->resources[RESOURCE_DOCUMENT] as $doc) { if ($doc->file_type === 'file') { - $files[] = [ - 'id' => (int) $doc->source_id, - 'contenthash' => hash('sha1', basename($doc->path)), - 'filename' => basename($doc->path), - 'filepath' => '/Documents/', - 'filesize' => (int) $doc->size, - 'mimetype' => $this->getMimeType($doc->path), - ]; + $ext = strtolower(pathinfo($doc->path, PATHINFO_EXTENSION)); + if (!in_array($ext, ['html', 'htm'], true)) { + $files[] = ['id' => (int) $doc->source_id]; + } } } } diff --git a/main/inc/lib/moodleexport/ForumExport.php b/main/inc/lib/moodleexport/ForumExport.php index 36cda8a9a8b..5044d3ea9d3 100644 --- a/main/inc/lib/moodleexport/ForumExport.php +++ b/main/inc/lib/moodleexport/ForumExport.php @@ -45,7 +45,7 @@ public function export($activityId, $exportDir, $moduleId, $sectionId): void */ public function getData(int $forumId, int $sectionId): ?array { - $forum = $this->course->resources['forum'][$forumId]->obj; + $forum = $this->course->resources[RESOURCE_FORUM][$forumId]->obj; $adminData = MoodleExport::getAdminUserData(); $adminId = $adminData['id']; @@ -78,14 +78,17 @@ public function getData(int $forumId, int $sectionId): ?array } } - $fileIds = []; + $name = $forum->forum_title ?? ''; + if ($sectionId > 0) { + $name = $this->lpItemTitle($sectionId, RESOURCE_FORUM, $forumId, $name); + } return [ 'id' => $forumId, 'moduleid' => $forumId, 'modulename' => 'forum', 'contextid' => $this->course->info['real_id'], - 'name' => $forum->forum_title, + 'name' => $name, 'description' => $forum->forum_comment, 'timecreated' => time(), 'timemodified' => time(), @@ -94,7 +97,7 @@ public function getData(int $forumId, int $sectionId): ?array 'userid' => $adminId, 'threads' => $threads, 'users' => [$adminId], - 'files' => $fileIds, + 'files' => [], ]; } diff --git a/main/inc/lib/moodleexport/MoodleExport.php b/main/inc/lib/moodleexport/MoodleExport.php index bc8d7cf6e80..791cc932658 100644 --- a/main/inc/lib/moodleexport/MoodleExport.php +++ b/main/inc/lib/moodleexport/MoodleExport.php @@ -443,105 +443,171 @@ private function getActivities(): array $activities = []; $glossaryAdded = false; - $documentsFolder = [ - 'id' => 0, + $lpIndex = []; + if (!empty($this->course->resources[RESOURCE_LEARNPATH])) { + foreach ($this->course->resources[RESOURCE_LEARNPATH] as $lp) { + $sid = (int) $lp->source_id; + foreach ($lp->items ?? [] as $it) { + $type = $it['item_type']; + if ($type === 'student_publication') { $type = 'assign'; } + elseif ($type === 'link') { $type = 'url'; } + elseif ($type === 'survey') { $type = 'feedback'; } + elseif ($type === 'document') { $type = 'document'; } + + $rid = $it['path']; + $title = $it['title'] ?? ''; + + if (ctype_digit((string) $rid)) { + $lpIndex[$sid][$type]['id'][(int) $rid] = $title; + } else { + $lpIndex[$sid][$type]['path'][(string) $rid] = $title; + } + } + } + } + + $titleFromLp = function (int $sectionId, string $moduleName, int $resourceId, string $fallback) use ($lpIndex) { + $type = in_array($moduleName, ['page','resource'], true) ? 'document' : $moduleName; + + if (isset($lpIndex[$sectionId][$type]['id'][$resourceId])) { + return $lpIndex[$sectionId][$type]['id'][$resourceId]; + } + + if ($type === 'assign' && isset($lpIndex[$sectionId]['student_publication']['id'][$resourceId])) { + return $lpIndex[$sectionId]['student_publication']['id'][$resourceId]; + } + + if ($type === 'document') { + $doc = \DocumentManager::get_document_data_by_id($resourceId, $this->course->code); + if (!empty($doc['path'])) { + $p = (string) $doc['path']; + foreach ([$p, 'document/'.$p, '/'.$p] as $cand) { + if (isset($lpIndex[$sectionId]['document']['path'][$cand])) { + return $lpIndex[$sectionId]['document']['path'][$cand]; + } + } + } + } + + return $fallback; + }; + + $activities[] = [ + 'id' => ActivityExport::DOCS_MODULE_ID, 'sectionid' => 0, - 'modulename' => 'folder', - 'moduleid' => 0, - 'title' => 'Documents', + 'modulename'=> 'folder', + 'moduleid' => ActivityExport::DOCS_MODULE_ID, + 'title' => 'Documents', ]; - $activities[] = $documentsFolder; + $htmlPageIds = []; foreach ($this->course->resources as $resourceType => $resources) { foreach ($resources as $resource) { $exportClass = null; - $moduleName = ''; - $title = ''; - $id = 0; + $moduleName = ''; + $title = ''; + $id = 0; + $sectionId = 0; - // Handle quizzes + // QUIZ if ($resourceType === RESOURCE_QUIZ && $resource->obj->iid > 0) { $exportClass = QuizExport::class; - $moduleName = 'quiz'; - $id = $resource->obj->iid; - $title = $resource->obj->title; + $moduleName = 'quiz'; + $id = (int) $resource->obj->iid; + $title = $resource->obj->title ?? ''; + $sectionId = (new QuizExport($this->course))->getSectionIdForActivity($id, $resourceType); + $title = $titleFromLp($sectionId, $moduleName, $id, $title); } - // Handle links - if ($resourceType === RESOURCE_LINK && $resource->source_id > 0) { + // URL + elseif ($resourceType === RESOURCE_LINK && $resource->source_id > 0) { $exportClass = UrlExport::class; - $moduleName = 'url'; - $id = $resource->source_id; - $title = $resource->title; + $moduleName = 'url'; + $id = (int) $resource->source_id; + $title = $resource->title ?? ''; + $sectionId = (new UrlExport($this->course))->getSectionIdForActivity($id, $resourceType); + $title = $titleFromLp($sectionId, $moduleName, $id, $title); } - // Handle glossaries + // GLOSSARY (uno solo) elseif ($resourceType === RESOURCE_GLOSSARY && $resource->glossary_id > 0 && !$glossaryAdded) { $exportClass = GlossaryExport::class; - $moduleName = 'glossary'; - $id = 1; - $title = get_lang('Glossary'); + $moduleName = 'glossary'; + $id = 1; + $title = get_lang('Glossary'); + $sectionId = 0; $glossaryAdded = true; } - // Handle forums + // FORUM elseif ($resourceType === RESOURCE_FORUM && $resource->source_id > 0) { $exportClass = ForumExport::class; - $moduleName = 'forum'; - $id = $resource->obj->iid; - $title = $resource->obj->forum_title; + $moduleName = 'forum'; + $id = (int) $resource->obj->iid; + $title = $resource->obj->forum_title ?? ''; + $sectionId = (new ForumExport($this->course))->getSectionIdForActivity($id, $resourceType); + $title = $titleFromLp($sectionId, $moduleName, $id, $title); } - // Handle documents (HTML pages) + // DOCUMENT elseif ($resourceType === RESOURCE_DOCUMENT && $resource->source_id > 0) { $document = \DocumentManager::get_document_data_by_id($resource->source_id, $this->course->code); - if ('html' === pathinfo($document['path'], PATHINFO_EXTENSION) && substr_count($resource->path, '/') === 1) { + $ext = strtolower(pathinfo($document['path'], PATHINFO_EXTENSION)); + + // HTML → page + if ($ext === 'html' || $ext === 'htm') { $exportClass = PageExport::class; - $moduleName = 'page'; - $id = $resource->source_id; - $title = $document['title']; + $moduleName = 'page'; + $id = (int) $resource->source_id; + $title = $document['title'] ?? ''; + $sectionId = (new PageExport($this->course))->getSectionIdForActivity($id, $resourceType); + $title = $titleFromLp($sectionId, $moduleName, $id, $title); $htmlPageIds[] = $id; } - if ('file' === $resource->file_type && !in_array($resource->source_id, $htmlPageIds)) { + + if ($resource->file_type === 'file' && !in_array($resource->source_id, $htmlPageIds, true)) { $resourceExport = new ResourceExport($this->course); - if ($resourceExport->getSectionIdForActivity($resource->source_id, $resourceType) > 0) { - $isRoot = substr_count($resource->path, '/') === 1; - if ($isRoot) { - $exportClass = ResourceExport::class; - $moduleName = 'resource'; - $id = $resource->source_id; - $title = $resource->title; - } + $sectionTmp = $resourceExport->getSectionIdForActivity((int) $resource->source_id, $resourceType); + if ($sectionTmp > 0) { + $exportClass = ResourceExport::class; + $moduleName = 'resource'; + $id = (int) $resource->source_id; + $title = $resource->title ?? ''; + $sectionId = $sectionTmp; + $title = $titleFromLp($sectionId, $moduleName, $id, $title); } } } - // Handle course introduction (page) + // INTRO → page elseif ($resourceType === RESOURCE_TOOL_INTRO && $resource->source_id == 'course_homepage') { $exportClass = PageExport::class; - $moduleName = 'page'; - $id = 0; - $title = get_lang('Introduction'); + $moduleName = 'page'; + $id = 0; + $title = get_lang('Introduction'); + $sectionId = 0; } - // Handle assignments (work) + // ASSIGN elseif ($resourceType === RESOURCE_WORK && $resource->source_id > 0) { $exportClass = AssignExport::class; - $moduleName = 'assign'; - $id = $resource->source_id; - $title = $resource->params['title'] ?? ''; + $moduleName = 'assign'; + $id = (int) $resource->source_id; + $title = $resource->params['title'] ?? ''; + $sectionId = (new AssignExport($this->course))->getSectionIdForActivity($id, $resourceType); + $title = $titleFromLp($sectionId, $moduleName, $id, $title); } - // Handle feedback (survey) + // FEEDBACK / SURVEY elseif ($resourceType === RESOURCE_SURVEY && $resource->source_id > 0) { $exportClass = FeedbackExport::class; - $moduleName = 'feedback'; - $id = $resource->source_id; - $title = $resource->params['title'] ?? ''; + $moduleName = 'feedback'; + $id = (int) $resource->source_id; + $title = $resource->params['title'] ?? ''; + $sectionId = (new FeedbackExport($this->course))->getSectionIdForActivity($id, $resourceType); + $title = $titleFromLp($sectionId, $moduleName, $id, $title); } - // Add the activity if the class and module name are set if ($exportClass && $moduleName) { - $exportInstance = new $exportClass($this->course); $activities[] = [ - 'id' => $id, - 'sectionid' => $exportInstance->getSectionIdForActivity($id, $resourceType), - 'modulename' => $moduleName, - 'moduleid' => $id, - 'title' => $title, + 'id' => $id, + 'sectionid' => $sectionId, + 'modulename'=> $moduleName, + 'moduleid' => $id, + 'title' => $title, ]; } } @@ -854,4 +920,57 @@ private function exportBackupSettings(array $sections, array $activities): array return $settings; } + + /** + * Maps module name to item_type of c_lp_item. + * (c_lp_item.item_type: document, quiz, link, forum, student_publication, survey) + */ + private function mapToLpItemType(string $moduleOrItemType): string + { + switch ($moduleOrItemType) { + case 'page': + case 'resource': + return 'document'; + case 'assign': + return 'student_publication'; + case 'url': + return 'link'; + case 'feedback': + return 'survey'; + default: + return $moduleOrItemType; // quiz, forum... + } + } + + /** Index titles by section + type + id from the LP items (c_lp_item.title). */ + private function buildLpTitleIndex(): array + { + $idx = []; + if (empty($this->course->resources[RESOURCE_LEARNPATH])) { + return $idx; + } + foreach ($this->course->resources[RESOURCE_LEARNPATH] as $lp) { + $sid = (int) $lp->source_id; + if (empty($lp->items)) { + continue; + } + foreach ($lp->items as $it) { + $type = $this->mapToLpItemType($it['item_type']); + $rid = (string) $it['path']; + $idx[$sid][$type][$rid] = $it['title'] ?? ''; + } + } + return $idx; + } + + /** Returns the LP title if it exists; otherwise, use the fallback. */ + private function titleFromLp(array $idx, int $sectionId, string $moduleName, int $resourceId, string $fallback): string + { + if ($sectionId <= 0) { + return $fallback; + } + $type = $this->mapToLpItemType($moduleName); + $rid = (string) $resourceId; + return $idx[$sectionId][$type][$rid] ?? $fallback; + } } diff --git a/main/inc/lib/moodleexport/PageExport.php b/main/inc/lib/moodleexport/PageExport.php index 7e69deef9d1..c2aa97305f4 100644 --- a/main/inc/lib/moodleexport/PageExport.php +++ b/main/inc/lib/moodleexport/PageExport.php @@ -111,12 +111,17 @@ public function getData(int $pageId, int $sectionId): ?array $pageResources = $this->course->resources[RESOURCE_DOCUMENT] ?? []; foreach ($pageResources as $page) { if ($page->source_id == $pageId) { + $name = $page->title ?? ''; + if ($sectionId > 0) { + $name = $this->lpItemTitle($sectionId, RESOURCE_DOCUMENT, $page->source_id, $name); + } + return [ 'id' => $page->source_id, 'moduleid' => $page->source_id, 'modulename' => 'page', 'contextid' => $contextid, - 'name' => $page->title, + 'name' => $name, 'intro' => $page->comment ?? '', 'content' => $this->normalizeContent($this->getPageContent($page)), 'sectionid' => $sectionId, diff --git a/main/inc/lib/moodleexport/QuizExport.php b/main/inc/lib/moodleexport/QuizExport.php index 69c11285833..8f303de07e1 100644 --- a/main/inc/lib/moodleexport/QuizExport.php +++ b/main/inc/lib/moodleexport/QuizExport.php @@ -49,19 +49,23 @@ public function export($activityId, $exportDir, $moduleId, $sectionId): void */ public function getData(int $quizId, int $sectionId): array { - $quizResources = $this->course->resources[RESOURCE_QUIZ]; + $quizResources = $this->course->resources[RESOURCE_QUIZ] ?? []; foreach ($quizResources as $quiz) { if ($quiz->obj->iid == -1) { continue; } - - if ($quiz->obj->iid == $quizId) { + if ((int) $quiz->obj->iid === $quizId) { $contextid = $quiz->obj->c_id; + $name = $quiz->obj->title ?? ''; + if ($sectionId > 0) { + $name = $this->lpItemTitle($sectionId, RESOURCE_QUIZ, $quizId, $name); + } + return [ 'id' => $quiz->obj->iid, - 'name' => $quiz->obj->title, + 'name' => $name, 'intro' => $quiz->obj->description, 'timeopen' => $quiz->obj->start_time ?? 0, 'timeclose' => $quiz->obj->end_time ?? 0, @@ -105,6 +109,7 @@ public function getData(int $quizId, int $sectionId): array 'completionpass' => $quiz->obj->completionpass ?? 0, 'completionminattempts' => $quiz->obj->completionminattempts ?? 0, 'allowofflineattempts' => $quiz->obj->allowofflineattempts ?? 0, + 'navmethod' => $quiz->obj->navmethod ?? 'free', 'users' => [], 'files' => [], ]; diff --git a/main/inc/lib/moodleexport/ResourceExport.php b/main/inc/lib/moodleexport/ResourceExport.php index 68fad4e41e4..1c6c97e1860 100644 --- a/main/inc/lib/moodleexport/ResourceExport.php +++ b/main/inc/lib/moodleexport/ResourceExport.php @@ -46,12 +46,17 @@ public function getData(int $resourceId, int $sectionId): array { $resource = $this->course->resources[RESOURCE_DOCUMENT][$resourceId]; + $name = $resource->title; + if ($sectionId > 0) { + $name = $this->lpItemTitle($sectionId, RESOURCE_DOCUMENT, $resourceId, $name); + } + return [ 'id' => $resourceId, 'moduleid' => $resource->source_id, 'modulename' => 'resource', 'contextid' => $resource->source_id, - 'name' => $resource->title, + 'name' => $name, 'intro' => $resource->comment ?? '', 'sectionid' => $sectionId, 'sectionnumber' => 1, diff --git a/main/inc/lib/moodleexport/SectionExport.php b/main/inc/lib/moodleexport/SectionExport.php index 2a83bc4790a..b633fdd1856 100644 --- a/main/inc/lib/moodleexport/SectionExport.php +++ b/main/inc/lib/moodleexport/SectionExport.php @@ -113,10 +113,11 @@ public function getActivitiesForGeneral(): array $activities = $this->getActivitiesForSection($generalLearnpath, true); - if (!in_array('folder', array_column($activities, 'modulename'))) { + $ya = array_column($activities, 'modulename'); + if (!in_array('folder', $ya, true)) { $activities[] = [ - 'id' => 0, - 'moduleid' => 0, + 'id' => ActivityExport::DOCS_MODULE_ID, + 'moduleid' => ActivityExport::DOCS_MODULE_ID, 'modulename' => 'folder', 'name' => 'Documents', 'sectionid' => 0, diff --git a/main/inc/lib/moodleexport/UrlExport.php b/main/inc/lib/moodleexport/UrlExport.php index c92b96a0fc5..3652437a93d 100644 --- a/main/inc/lib/moodleexport/UrlExport.php +++ b/main/inc/lib/moodleexport/UrlExport.php @@ -47,13 +47,18 @@ public function getData(int $activityId, int $sectionId): ?array // Extract the URL information from the course data $url = $this->course->resources['link'][$activityId]; + $name = $url->title; + if ($sectionId > 0) { + $name = $this->lpItemTitle($sectionId, RESOURCE_LINK, $activityId, $name); + } + // Return the URL data formatted for export return [ 'id' => $activityId, 'moduleid' => $activityId, 'modulename' => 'url', 'contextid' => $this->course->info['real_id'], - 'name' => $url->title, + 'name' => $name, 'description' => $url->description, 'externalurl' => $url->url, 'timecreated' => time(),