prepareActivityDirectory($exportDir, 'resource', (int) $moduleId);
$resourceData = $this->getData((int) $activityId, (int) $sectionId, (int) $moduleId);
if (empty($resourceData)) {
return;
}
$this->createResourceXml($resourceData, $resourceDir);
$this->createModuleXml($resourceData, $resourceDir);
$this->createGradesXml($resourceData, $resourceDir);
$this->createFiltersXml($resourceData, $resourceDir);
$this->createGradeHistoryXml($resourceData, $resourceDir);
$this->createInforefXml($resourceData, $resourceDir);
$this->createRolesXml($resourceData, $resourceDir);
$this->createCommentsXml($resourceData, $resourceDir);
$this->createCalendarXml($resourceData, $resourceDir);
}
/**
* Get resource data dynamically from the course.
*/
public function getData(int $resourceId, int $sectionId, ?int $moduleId = null): array
{
if (empty($this->course->resources[RESOURCE_DOCUMENT][$resourceId])) {
return [];
}
$resource = $this->course->resources[RESOURCE_DOCUMENT][$resourceId];
$name = (string) ($resource->title ?? '');
if ($sectionId > 0) {
$name = $this->lpItemTitle($sectionId, RESOURCE_DOCUMENT, $resourceId, $name);
}
$name = $this->sanitizeMoodleActivityName($name, 255);
$effectiveModuleId = (int) ($moduleId ?? $resource->source_id);
if ($effectiveModuleId <= 0) {
$effectiveModuleId = (int) $resource->source_id;
}
$resourceFile = $this->buildResourceFileEntry($resource, $effectiveModuleId);
$introResult = $this->extractEmbeddedFilesAndNormalizeContent(
(string) ($resource->comment ?? ''),
$effectiveModuleId,
'mod_resource',
'intro',
0,
fn (int $sequence): int => $this->buildResourceIntroFileId($effectiveModuleId, $sequence)
);
return [
'id' => $resourceId,
'moduleid' => $effectiveModuleId,
'modulename' => 'resource',
'contextid' => $effectiveModuleId,
'name' => $name,
'intro' => $introResult['content'],
'sectionid' => $sectionId,
'sectionnumber' => 1,
'timemodified' => time(),
'users' => [],
'files' => array_merge([$resourceFile], $introResult['files']),
];
}
/**
* Creates the inforef.xml file, referencing users and files associated with the activity.
*
* @param array $references Contains 'users' and 'files' arrays to reference in the XML.
* @param string $directory The directory where the XML file will be saved.
*/
protected function createInforefXml(array $references, string $directory): void
{
$xmlContent = ''.PHP_EOL;
$xmlContent .= ''.PHP_EOL;
if (!empty($references['files']) && is_array($references['files'])) {
$xmlContent .= ' '.PHP_EOL;
foreach ($references['files'] as $file) {
$fileId = is_array($file) ? (int) ($file['id'] ?? 0) : (int) $file;
if ($fileId <= 0) {
continue;
}
$xmlContent .= ' '.PHP_EOL;
$xmlContent .= ' '.$fileId.''.PHP_EOL;
$xmlContent .= ' '.PHP_EOL;
}
$xmlContent .= ' '.PHP_EOL;
}
$xmlContent .= ''.PHP_EOL;
$this->createXmlFile('inforef', $xmlContent, $directory);
}
/**
* Build the files.xml entry for a resource activity file.
*/
private function buildResourceFileEntry(object $resource, int $moduleId): array
{
$adminData = MoodleExport::getAdminUserData();
$adminId = (int) ($adminData['id'] ?? 1);
$documentPath = (string) $resource->path;
$absolutePath = $this->course->path.$documentPath;
$filename = basename($documentPath);
$contenthash = is_file($absolutePath)
? sha1_file($absolutePath)
: hash('sha1', $filename);
return [
'id' => $this->buildResourceFileId($moduleId, (int) $resource->source_id),
'contenthash' => $contenthash,
'contextid' => $moduleId,
'component' => 'mod_resource',
'filearea' => 'content',
'itemid' => 0,
'filepath' => '/',
'documentpath' => $documentPath,
'filename' => $filename,
'userid' => $adminId,
'filesize' => (int) ($resource->size ?? 0),
'mimetype' => $this->guessMimeType($documentPath),
'status' => 0,
'timecreated' => time() - 3600,
'timemodified' => time(),
'source' => (string) ($resource->title ?? $filename),
'author' => 'Unknown',
'license' => 'allrightsreserved',
];
}
/**
* Build a stable file id for mod_resource main file entries.
*/
private function buildResourceFileId(int $moduleId, int $resourceId): int
{
$base = $moduleId > 0 ? $moduleId : $resourceId;
return 1000000000 + $base;
}
/**
* Build a stable file id for embedded intro files in mod_resource.
*/
private function buildResourceIntroFileId(int $moduleId, int $sequence): int
{
return 1250000000 + max(0, $moduleId) + max(1, $sequence);
}
/**
* Guess MIME type from file extension.
*/
private function guessMimeType(string $filePath): string
{
$ext = strtolower((string) pathinfo($filePath, PATHINFO_EXTENSION));
$map = [
'pdf' => 'application/pdf',
'jpg' => 'image/jpeg',
'jpeg' => 'image/jpeg',
'png' => 'image/png',
'gif' => 'image/gif',
'svg' => 'image/svg+xml',
'webp' => 'image/webp',
'html' => 'text/html',
'htm' => 'text/html',
'txt' => 'text/plain',
'css' => 'text/css',
'js' => 'application/javascript',
'mp4' => 'video/mp4',
'webm' => 'video/webm',
'mp3' => 'audio/mpeg',
'ogg' => 'audio/ogg',
'doc' => 'application/msword',
'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
'xls' => 'application/vnd.ms-excel',
'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
'ppt' => 'application/vnd.ms-powerpoint',
'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
'zip' => 'application/zip',
'rar' => 'application/x-rar-compressed',
];
return $map[$ext] ?? 'application/octet-stream';
}
/**
* Create the XML file for the resource.
*/
private function createResourceXml(array $resourceData, string $resourceDir): void
{
$xmlContent = ''.PHP_EOL;
$xmlContent .= ''.PHP_EOL;
$xmlContent .= ' '.PHP_EOL;
$xmlContent .= ' '.htmlspecialchars((string) $resourceData['name']).''.PHP_EOL;
$xmlContent .= ' '.PHP_EOL;
$xmlContent .= ' 1'.PHP_EOL;
$xmlContent .= ' 0'.PHP_EOL;
$xmlContent .= ' 0'.PHP_EOL;
$xmlContent .= ' $@NULL@$'.PHP_EOL;
$xmlContent .= ' 0'.PHP_EOL;
$xmlContent .= ' a:1:{s:10:"printintro";i:1;}'.PHP_EOL;
$xmlContent .= ' 0'.PHP_EOL;
$xmlContent .= ' 1'.PHP_EOL;
$xmlContent .= ' '.$resourceData['timemodified'].''.PHP_EOL;
$xmlContent .= ' '.PHP_EOL;
$xmlContent .= ''.PHP_EOL;
$this->createXmlFile('resource', $xmlContent, $resourceDir);
}
}