course = $course; } /** * Export a section and its activities to the specified directory. */ public function exportSection(int $sectionId, string $exportDir): void { $sectionDir = $exportDir."/sections/section_{$sectionId}"; if (!is_dir($sectionDir)) { mkdir($sectionDir, api_get_permissions_for_new_directories(), true); } if ($sectionId > 0) { $learnpath = $this->getLearnpathById($sectionId); if ($learnpath === null) { throw new Exception("Learnpath with ID $sectionId not found."); } $sectionData = $this->getSectionData($learnpath); } else { $sectionData = [ 'id' => 0, 'number' => 0, 'name' => get_lang('General'), 'summary' => get_lang('GeneralResourcesCourse'), 'sequence' => 0, 'visible' => 1, 'timemodified' => time(), 'activities' => $this->getActivitiesForGeneral(), ]; } $this->createSectionXml($sectionData, $sectionDir); $this->createInforefXml($sectionData, $sectionDir); $this->exportActivities($sectionData['activities'], $exportDir, $sectionId); } /** * Get all general items not linked to any lesson (learnpath). */ public function getGeneralItems(): array { $generalItems = []; // List of resource types and their corresponding ID keys $resourceTypes = [ RESOURCE_DOCUMENT => 'source_id', RESOURCE_QUIZ => 'source_id', RESOURCE_GLOSSARY => 'glossary_id', RESOURCE_LINK => 'source_id', RESOURCE_WORK => 'source_id', RESOURCE_FORUM => 'source_id', RESOURCE_SURVEY => 'source_id', ]; foreach ($resourceTypes as $resourceType => $idKey) { if (!empty($this->course->resources[$resourceType])) { foreach ($this->course->resources[$resourceType] as $id => $resource) { if (!$this->isItemInLearnpath($resource, $resourceType)) { $title = $resourceType === RESOURCE_WORK ? ($resource->params['title'] ?? '') : ($resource->title ?? $resource->name); $generalItems[] = [ 'id' => $resource->$idKey, 'item_type' => $resourceType, 'path' => $id, 'title' => $title, ]; } } } } return $generalItems; } /** * Get the activities for the general section. */ public function getActivitiesForGeneral(): array { $generalLearnpath = (object) [ 'items' => $this->getGeneralItems(), 'source_id' => 0, ]; return $this->getActivitiesForSection($generalLearnpath, true); } /** * Get the learnpath object by its ID. */ public function getLearnpathById(int $sectionId): ?object { foreach ($this->course->resources[RESOURCE_LEARNPATH] as $learnpath) { if ($learnpath->source_id == $sectionId) { return $learnpath; } } return null; } /** * Get section data for a learnpath. */ public function getSectionData(object $learnpath): array { return [ 'id' => $learnpath->source_id, 'number' => $learnpath->display_order, 'name' => $learnpath->name, 'summary' => $learnpath->description, 'sequence' => $learnpath->source_id, 'visible' => $learnpath->visibility, 'timemodified' => strtotime($learnpath->modified_on), 'activities' => $this->getActivitiesForSection($learnpath), ]; } /** * Get the activities for a specific section. */ public function getActivitiesForSection(object $learnpath, bool $isGeneral = false): array { $activities = []; $sectionId = $isGeneral ? 0 : $learnpath->source_id; foreach ($learnpath->items as $item) { $this->addActivityToList($item, $sectionId, $activities); } return $activities; } /** * Export the activities of a section. */ private function exportActivities(array $activities, string $exportDir, int $sectionId): void { $exportClasses = [ 'quiz' => QuizExport::class, 'glossary' => GlossaryExport::class, 'url' => UrlExport::class, 'assign' => AssignExport::class, 'forum' => ForumExport::class, 'page' => PageExport::class, 'resource' => ResourceExport::class, 'folder' => FolderExport::class, 'feedback' => FeedbackExport::class, ]; foreach ($activities as $activity) { $moduleName = $activity['modulename']; if (isset($exportClasses[$moduleName])) { $exportClass = new $exportClasses[$moduleName]($this->course); $exportClass->export($activity['id'], $exportDir, $activity['moduleid'], $sectionId); } else { throw new \Exception("Export for module '$moduleName' is not supported."); } } } /** * Check if an item is associated with any learnpath. */ private function isItemInLearnpath(object $item, string $type): bool { if (!empty($this->course->resources[RESOURCE_LEARNPATH])) { foreach ($this->course->resources[RESOURCE_LEARNPATH] as $learnpath) { if (!empty($learnpath->items)) { foreach ($learnpath->items as $learnpathItem) { if ($learnpathItem['item_type'] === $type && $learnpathItem['path'] == $item->source_id) { return true; } } } } } return false; } /** * Add an activity to the activities list. */ private function addActivityToList(array $item, int $sectionId, array &$activities): void { $activityData = null; $activityClassMap = [ 'quiz' => QuizExport::class, 'glossary' => GlossaryExport::class, 'url' => UrlExport::class, 'assign' => AssignExport::class, 'forum' => ForumExport::class, 'page' => PageExport::class, 'resource' => ResourceExport::class, 'folder' => FolderExport::class, 'feedback' => FeedbackExport::class, ]; $itemType = $item['item_type'] === 'link' ? 'url' : ($item['item_type'] === 'work' ? 'assign' : ($item['item_type'] === 'survey' ? 'feedback' : $item['item_type'])); switch ($itemType) { case 'quiz': case 'glossary': case 'assign': case 'url': case 'forum': case 'feedback': $activityId = $itemType === 'glossary' ? 1 : (int) $item['path']; $exportClass = $activityClassMap[$itemType]; $exportInstance = new $exportClass($this->course); $activityData = $exportInstance->getData($activityId, $sectionId); break; case 'document': $documentId = (int) $item['path']; $document = \DocumentManager::get_document_data_by_id($documentId, $this->course->code); // Determine the type of document and get the corresponding export class $documentType = $this->getDocumentType($document['filetype'], $document['path']); if ($documentType) { $activityClass = $activityClassMap[$documentType]; $exportInstance = new $activityClass($this->course); $activityData = $exportInstance->getData($item['path'], $sectionId); } break; } // Add the activity to the list if the data exists if ($activityData) { $activities[] = [ 'id' => $activityData['id'], 'moduleid' => $activityData['moduleid'], 'type' => $item['item_type'], 'modulename' => $activityData['modulename'], 'name' => $activityData['name'], ]; } } /** * Determine the document type based on filetype and path. */ private function getDocumentType(string $filetype, string $path): ?string { if ('html' === pathinfo($path, PATHINFO_EXTENSION)) { return 'page'; } elseif ('file' === $filetype) { return 'resource'; } elseif ('folder' === $filetype) { return 'folder'; } return null; } /** * Create the section.xml file. */ private function createSectionXml(array $sectionData, string $destinationDir): void { $xmlContent = ''.PHP_EOL; $xmlContent .= '
'.PHP_EOL; $xmlContent .= ' '.$sectionData['number'].''.PHP_EOL; $xmlContent .= ' '.htmlspecialchars($sectionData['name']).''.PHP_EOL; $xmlContent .= ' '.htmlspecialchars($sectionData['summary']).''.PHP_EOL; $xmlContent .= ' 1'.PHP_EOL; $xmlContent .= ' '.implode(',', array_column($sectionData['activities'], 'moduleid')).''.PHP_EOL; $xmlContent .= ' '.$sectionData['visible'].''.PHP_EOL; $xmlContent .= ' '.$sectionData['timemodified'].''.PHP_EOL; $xmlContent .= '
'.PHP_EOL; $xmlFile = $destinationDir.'/section.xml'; file_put_contents($xmlFile, $xmlContent); } /** * Create the inforef.xml file for the section. */ private function createInforefXml(array $sectionData, string $destinationDir): void { $xmlContent = ''.PHP_EOL; $xmlContent .= ''.PHP_EOL; foreach ($sectionData['activities'] as $activity) { $xmlContent .= ' '.htmlspecialchars($activity['name']).''.PHP_EOL; } $xmlContent .= ''.PHP_EOL; $xmlFile = $destinationDir.'/inforef.xml'; file_put_contents($xmlFile, $xmlContent); } }