Skip to content

Commit ec0fb45

Browse files
authored
Merge pull request #6146 from christianbeeznest/Aix-21977-6
Internal: Improvements and structure adjustments for moodle import - refs BT#21977
2 parents e34e1a5 + 00b080c commit ec0fb45

File tree

7 files changed

+226
-80
lines changed

7 files changed

+226
-80
lines changed

main/coursecopy/export_moodle.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
// Rebuild the course object based on selected resources
5252
$cb = new CourseBuilder('partial');
5353
$course = $cb->build(0, null, false, array_keys($selectedResources), $selectedResources);
54+
$course = CourseSelectForm::get_posted_course(null, 0, '', $course);
5455

5556
// Get admin details
5657
$adminId = (int) $_POST['admin_id'];

main/inc/lib/moodleexport/CourseExport.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ class CourseExport
1717
private $courseInfo;
1818
private $activities;
1919

20-
public function __construct($course, $activities)
20+
public function __construct($course, $activities = [])
2121
{
2222
$this->course = $course;
2323
$this->courseInfo = api_get_course_info($course->code);

main/inc/lib/moodleexport/FileExport.php

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ public function getFilesData(): array
7878
'component' => 'mod_assign',
7979
'filearea' => 'introattachment',
8080
'itemid' => (int) $work->params['id'],
81-
'filepath' => '/',
81+
'filepath' => '/Documents/',
8282
'documentpath' => 'document/'.$docData['path'],
8383
'filename' => basename($docData['path']),
8484
'userid' => $adminId,
@@ -187,11 +187,15 @@ private function createFileXmlEntry(array $file): string
187187
private function processDocument(array $filesData, object $document): array
188188
{
189189
if ($document->file_type === 'file') {
190-
$filesData['files'][] = $this->getFileData($document);
190+
$fileData = $this->getFileData($document);
191+
$fileData['filepath'] = '/Documents/';
192+
$fileData['contextid'] = 0;
193+
$fileData['component'] = 'mod_folder';
194+
$filesData['files'][] = $fileData;
191195
} elseif ($document->file_type === 'folder') {
192196
$folderFiles = \DocumentManager::getAllDocumentsByParentId($this->course->info, $document->source_id);
193197
foreach ($folderFiles as $file) {
194-
$filesData['files'][] = $this->getFolderFileData($file, (int) $document->source_id);
198+
$filesData['files'][] = $this->getFolderFileData($file, (int) $document->source_id, '/Documents/'.dirname($file['path']).'/');
195199
}
196200
}
197201

@@ -233,14 +237,14 @@ private function getFileData(object $document): array
233237
/**
234238
* Get file data for files inside a folder.
235239
*/
236-
private function getFolderFileData(array $file, int $sourceId): array
240+
private function getFolderFileData(array $file, int $sourceId, string $parentPath = '/Documents/'): array
237241
{
238242
$adminData = MoodleExport::getAdminUserData();
239243
$adminId = $adminData['id'];
240244
$contenthash = hash('sha1', basename($file['path']));
241245
$mimetype = $this->getMimeType($file['path']);
242246
$filename = basename($file['path']);
243-
$filepath = $this->ensureTrailingSlash(dirname($file['path']));
247+
$filepath = $this->ensureTrailingSlash($parentPath);
244248

245249
return [
246250
'id' => $file['id'],
@@ -267,9 +271,15 @@ private function getFolderFileData(array $file, int $sourceId): array
267271
/**
268272
* Ensure the directory path has a trailing slash.
269273
*/
270-
private function ensureTrailingSlash($path): string
274+
private function ensureTrailingSlash(string $path): string
271275
{
272-
return empty($path) || $path === '.' || $path === '/' ? '/' : rtrim($path, '/').'/';
276+
if (empty($path) || $path === '.' || $path === '/') {
277+
return '/';
278+
}
279+
280+
$path = preg_replace('/\/+/', '/', $path);
281+
282+
return rtrim($path, '/') . '/';
273283
}
274284

275285
/**

main/inc/lib/moodleexport/FolderExport.php

Lines changed: 34 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -44,26 +44,32 @@ public function export($activityId, $exportDir, $moduleId, $sectionId): void
4444
*/
4545
public function getData(int $folderId, int $sectionId): ?array
4646
{
47-
$folder = $this->course->resources['document'][$folderId];
48-
49-
$folderPath = $folder->path.'/';
50-
foreach ($this->course->resources['document'] as $resource) {
51-
if ($resource->path !== $folder->path && str_starts_with($resource->path, $folderPath)) {
52-
return [
53-
'id' => $folderId,
54-
'moduleid' => $folder->source_id,
55-
'modulename' => 'folder',
56-
'contextid' => $folder->source_id,
57-
'name' => $folder->title,
58-
'sectionid' => $sectionId,
59-
'timemodified' => time(),
60-
];
61-
}
47+
if ($folderId === 0) {
48+
return [
49+
'id' => 0,
50+
'moduleid' => 0,
51+
'modulename' => 'folder',
52+
'contextid' => 0,
53+
'name' => 'Documents',
54+
'sectionid' => $sectionId,
55+
'timemodified' => time(),
56+
];
6257
}
6358

64-
return null;
59+
$folder = $this->course->resources['document'][$folderId];
60+
61+
return [
62+
'id' => $folderId,
63+
'moduleid' => $folder->source_id,
64+
'modulename' => 'folder',
65+
'contextid' => $folder->source_id,
66+
'name' => $folder->title,
67+
'sectionid' => $sectionId,
68+
'timemodified' => time(),
69+
];
6570
}
6671

72+
6773
/**
6874
* Create the XML file for the folder.
6975
*/
@@ -92,19 +98,19 @@ private function createFolderXml(array $folderData, string $folderDir): void
9298
*/
9399
private function getFilesForFolder(int $folderId): array
94100
{
95-
$documentData = \DocumentManager::getAllDocumentsByParentId($this->course->info, $folderId);
96-
97101
$files = [];
98-
foreach ($documentData as $doc) {
99-
if ($doc['filetype'] === 'file') {
100-
$files[] = [
101-
'id' => (int) $doc['id'],
102-
'contenthash' => 'hash'.$doc['id'],
103-
'filename' => $doc['basename'],
104-
'filepath' => $doc['path'],
105-
'filesize' => (int) $doc['size'],
106-
'mimetype' => $this->getMimeType($doc['basename']),
107-
];
102+
if ($folderId === 0) {
103+
foreach ($this->course->resources[RESOURCE_DOCUMENT] as $doc) {
104+
if ($doc->file_type === 'file') {
105+
$files[] = [
106+
'id' => (int) $doc->source_id,
107+
'contenthash' => hash('sha1', basename($doc->path)),
108+
'filename' => basename($doc->path),
109+
'filepath' => '/Documents/',
110+
'filesize' => (int) $doc->size,
111+
'mimetype' => $this->getMimeType($doc->path),
112+
];
113+
}
108114
}
109115
}
110116

main/inc/lib/moodleexport/MoodleExport.php

Lines changed: 98 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
namespace moodleexport;
66

7+
use Chamilo\CourseBundle\Component\CourseCopy\CourseBuilder;
78
use Exception;
89
use RecursiveDirectoryIterator;
910
use RecursiveIteratorIterator;
@@ -25,7 +26,18 @@ class MoodleExport
2526
*/
2627
public function __construct(object $course)
2728
{
29+
// Build the complete course object
30+
$cb = new CourseBuilder('complete');
31+
$complete = $cb->build();
32+
33+
// Store the selected course
2834
$this->course = $course;
35+
36+
// Fill missing resources from learnpath
37+
$this->fillResourcesFromLearnpath($complete);
38+
39+
// Fill missing quiz questions
40+
$this->fillQuestionsFromQuiz($complete);
2941
}
3042

3143
/**
@@ -177,6 +189,67 @@ public static function getAdminUserData(): array
177189
return self::$adminUserData;
178190
}
179191

192+
/**
193+
* Fills missing resources from the learnpath into the course structure.
194+
*
195+
* This method checks if the course has a learnpath and ensures that all
196+
* referenced resources (documents, quizzes, etc.) exist in the course's
197+
* resources array by pulling them from the complete course object.
198+
*/
199+
private function fillResourcesFromLearnpath(object $complete): void
200+
{
201+
// Check if the course has learnpath
202+
if (!isset($this->course->resources['learnpath'])) {
203+
return;
204+
}
205+
206+
foreach ($this->course->resources['learnpath'] as $learnpathId => $learnpath) {
207+
if (!isset($learnpath->items)) {
208+
continue;
209+
}
210+
211+
foreach ($learnpath->items as $item) {
212+
$type = $item['item_type']; // Resource type (document, quiz, etc.)
213+
$resourceId = $item['path']; // Resource ID in resources
214+
215+
// Check if the resource exists in the complete object and is not yet in the course resources
216+
if (isset($complete->resources[$type][$resourceId]) && !isset($this->course->resources[$type][$resourceId])) {
217+
// Add the resource directly to the original course resources structure
218+
$this->course->resources[$type][$resourceId] = $complete->resources[$type][$resourceId];
219+
}
220+
}
221+
}
222+
}
223+
224+
/**
225+
* Fills missing exercise questions related to quizzes in the course.
226+
*
227+
* This method checks if the course has quizzes and ensures that all referenced
228+
* questions exist in the course's resources array by pulling them from the complete
229+
* course object.
230+
*/
231+
private function fillQuestionsFromQuiz(object $complete): void
232+
{
233+
// Check if the course has quizzes
234+
if (!isset($this->course->resources['quiz'])) {
235+
return;
236+
}
237+
238+
foreach ($this->course->resources['quiz'] as $quizId => $quiz) {
239+
if (!isset($quiz->obj->question_ids)) {
240+
continue;
241+
}
242+
243+
foreach ($quiz->obj->question_ids as $questionId) {
244+
// Check if the question exists in the complete object and is not yet in the course resources
245+
if (isset($complete->resources['Exercise_Question'][$questionId]) && !isset($this->course->resources['Exercise_Question'][$questionId])) {
246+
// Add the question directly to the original course resources structure
247+
$this->course->resources['Exercise_Question'][$questionId] = $complete->resources['Exercise_Question'][$questionId];
248+
}
249+
}
250+
}
251+
}
252+
180253
/**
181254
* Export root XML files such as badges, completion, gradebook, etc.
182255
*/
@@ -359,6 +432,14 @@ private function getActivities(): array
359432
$activities = [];
360433
$glossaryAdded = false;
361434

435+
$documentsFolder = [
436+
'id' => 0,
437+
'sectionid' => 0,
438+
'modulename' => 'folder',
439+
'moduleid' => 0,
440+
'title' => 'Documents',
441+
];
442+
$activities[] = $documentsFolder;
362443
foreach ($this->course->resources as $resourceType => $resources) {
363444
foreach ($resources as $resource) {
364445
$exportClass = null;
@@ -403,36 +484,27 @@ private function getActivities(): array
403484
$moduleName = 'page';
404485
$id = $resource->source_id;
405486
$title = $document['title'];
406-
} elseif ('file' === $resource->file_type) {
407-
$isRoot = substr_count($resource->path, '/') === 1;
408-
409-
if ($isRoot) {
410-
$exportClass = ResourceExport::class;
411-
$moduleName = 'resource';
412-
$id = $resource->source_id;
413-
$title = $resource->title;
414-
}
415-
} elseif ('folder' === $resource->file_type) {
416-
$isEmpty = true;
417-
$folderPath = $resource->path.'/';
418-
419-
foreach ($this->course->resources['document'] as $childResource) {
420-
if (str_starts_with($childResource->path, $folderPath) && $childResource->path !== $resource->path) {
421-
$isEmpty = false;
422-
break;
487+
}
488+
if ('file' === $resource->file_type) {
489+
$resourceExport = new ResourceExport($this->course);
490+
if ($resourceExport->getSectionIdForActivity($resource->source_id, $resourceType) > 0) {
491+
$isRoot = substr_count($resource->path, '/') === 1;
492+
if ($isRoot) {
493+
$exportClass = ResourceExport::class;
494+
$moduleName = 'resource';
495+
$id = $resource->source_id;
496+
$title = $resource->title;
423497
}
424498
}
425-
426-
$isRoot = substr_count($resource->path, '/') === 1;
427-
428-
if (!$isEmpty && $isRoot) {
429-
$exportClass = FolderExport::class;
430-
$moduleName = 'folder';
431-
$id = $resource->source_id;
432-
$title = $resource->title;
433-
}
434499
}
435500
}
501+
// Handle course introduction (page)
502+
elseif ($resourceType === RESOURCE_TOOL_INTRO && $resource->source_id == 'course_homepage') {
503+
$exportClass = PageExport::class;
504+
$moduleName = 'page';
505+
$id = 0;
506+
$title = get_lang('Introduction');
507+
}
436508
// Handle assignments (work)
437509
elseif ($resourceType === RESOURCE_WORK && $resource->source_id > 0) {
438510
$exportClass = AssignExport::class;

main/inc/lib/moodleexport/PageExport.php

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,12 +44,35 @@ public function export($activityId, $exportDir, $moduleId, $sectionId): void
4444
*/
4545
public function getData(int $pageId, int $sectionId): ?array
4646
{
47-
$pageResources = $this->course->resources[RESOURCE_DOCUMENT];
47+
$contextid = $this->course->info['real_id'];
48+
if ($pageId === 0) {
49+
if (
50+
isset($this->course->resources[RESOURCE_TOOL_INTRO]['course_homepage']) &&
51+
is_object($this->course->resources[RESOURCE_TOOL_INTRO]['course_homepage']) &&
52+
!empty($this->course->resources[RESOURCE_TOOL_INTRO]['course_homepage']->intro_text)
53+
) {
4854

55+
return [
56+
'id' => 0,
57+
'moduleid' => 0,
58+
'modulename' => 'page',
59+
'contextid' => $contextid,
60+
'name' => get_lang('Introduction'),
61+
'intro' => '',
62+
'content' => trim($this->course->resources[RESOURCE_TOOL_INTRO]['course_homepage']->intro_text),
63+
'sectionid' => $sectionId,
64+
'sectionnumber' => 1,
65+
'display' => 0,
66+
'timemodified' => time(),
67+
'users' => [],
68+
'files' => [],
69+
];
70+
}
71+
}
72+
73+
$pageResources = $this->course->resources[RESOURCE_DOCUMENT] ?? [];
4974
foreach ($pageResources as $page) {
5075
if ($page->source_id == $pageId) {
51-
$contextid = $this->course->info['real_id'];
52-
5376
return [
5477
'id' => $page->source_id,
5578
'moduleid' => $page->source_id,

0 commit comments

Comments
 (0)