@@ -138,10 +138,8 @@ public function importResources(int $node, string $backupId, Request $req): Json
138138 $ this ->setDebugFromRequest ($ req );
139139 $ mode = strtolower ((string ) $ req ->query ->get ('mode ' , 'auto ' )); // 'auto' | 'dat' | 'moodle'
140140
141- // Reutilizas TU loader actual con el nuevo flag
142141 $ course = $ this ->loadLegacyCourseForAnyBackup ($ backupId , $ mode === 'dat ' ? 'chamilo ' : $ mode );
143142
144- // Lo demás igual
145143 $ this ->logDebug ('[importResources] course loaded ' , [
146144 'has_resources ' => \is_array ($ course ->resources ?? null ),
147145 'keys ' => array_keys ((array ) ($ course ->resources ?? [])),
@@ -178,6 +176,9 @@ public function importRestore(
178176
179177 try {
180178 $ payload = json_decode ($ req ->getContent () ?: '{} ' , true );
179+ // Keep mode consistent with GET /import/{backupId}/resources
180+ $ mode = strtolower ((string ) ($ payload ['mode ' ] ?? 'auto ' ));
181+
181182 $ importOption = (string ) ($ payload ['importOption ' ] ?? 'full_backup ' );
182183 $ sameFileNameOption = (int ) ($ payload ['sameFileNameOption ' ] ?? 2 );
183184
@@ -192,24 +193,28 @@ public function importRestore(
192193 'sameFileNameOption ' => $ sameFileNameOption ,
193194 'selectedTypes ' => $ selectedTypes ,
194195 'hasResourcesMap ' => !empty ($ selectedResources ),
196+ 'mode ' => $ mode ,
195197 ]);
196198
197- $ course = $ this ->loadLegacyCourseForAnyBackup ($ backupId );
199+ // Load with same mode to avoid switching source on POST
200+ $ course = $ this ->loadLegacyCourseForAnyBackup ($ backupId , $ mode === 'dat ' ? 'chamilo ' : $ mode );
198201 if (!\is_object ($ course ) || empty ($ course ->resources ) || !\is_array ($ course ->resources )) {
199202 return $ this ->json (['error ' => 'Backup has no resources ' ], 400 );
200203 }
201204
202- $ resourcesAll = ( array ) $ course ->resources ;
205+ $ resourcesAll = $ course ->resources ;
203206 $ this ->logDebug ('[importRestore] BEFORE filter keys ' , array_keys ($ resourcesAll ));
204207
208+ // Always hydrate LP dependencies (even in full_backup).
209+ $ this ->hydrateLpDependenciesFromSnapshot ($ course , $ resourcesAll );
210+ $ this ->logDebug ('[importRestore] AFTER hydrate keys ' , array_keys ((array ) $ course ->resources ));
211+
205212 // Detect source BEFORE any filtering (meta may be dropped by filters)
206213 $ importSource = $ this ->getImportSource ($ course );
207214 $ isMoodle = ('moodle ' === $ importSource );
208215 $ this ->logDebug ('[importRestore] detected import source ' , ['import_source ' => $ importSource , 'isMoodle ' => $ isMoodle ]);
209216
210217 if ('select_items ' === $ importOption ) {
211- $ this ->hydrateLpDependenciesFromSnapshot ($ course , $ resourcesAll );
212-
213218 if (empty ($ selectedResources ) && !empty ($ selectedTypes )) {
214219 $ selectedResources = $ this ->buildSelectionFromTypes ($ course , $ selectedTypes );
215220 }
@@ -218,7 +223,6 @@ public function importRestore(
218223 foreach ($ selectedResources as $ ids ) {
219224 if (\is_array ($ ids ) && !empty ($ ids )) {
220225 $ hasAny = true ;
221-
222226 break ;
223227 }
224228 }
@@ -237,7 +241,11 @@ public function importRestore(
237241 // NON-MOODLE
238242 if (!$ isMoodle ) {
239243 $ this ->logDebug ('[importRestore] non-Moodle backup -> using CourseRestorer ' );
244+ // Trace around normalization to detect bucket drops
245+ $ this ->logDebug ('[importRestore] BEFORE normalize ' , array_keys ((array ) $ course ->resources ));
246+
240247 $ this ->normalizeBucketsForRestorer ($ course );
248+ $ this ->logDebug ('[importRestore] AFTER normalize ' , array_keys ((array ) $ course ->resources ));
241249
242250 $ restorer = new CourseRestorer ($ course );
243251 $ restorer ->set_file_option ($ this ->mapSameNameOption ($ sameFileNameOption ));
@@ -276,15 +284,16 @@ public function importRestore(
276284 $ mark = static function (array &$ dst , bool $ cond , string $ key ): void { if ($ cond ) { $ dst [$ key ] = true ; } };
277285
278286 if ('full_backup ' === $ importOption ) {
287+ // Be tolerant with plural 'documents'
279288 $ mark ($ wantedGroups , $ present ('link ' ) || $ present ('link_category ' ), 'links ' );
280289 $ mark ($ wantedGroups , $ present ('forum ' ) || $ present ('forum_category ' ), 'forums ' );
281- $ mark ($ wantedGroups , $ present ('document ' ), 'documents ' );
290+ $ mark ($ wantedGroups , $ present ('document ' ) || $ present ( ' documents ' ) , 'documents ' );
282291 $ mark ($ wantedGroups , $ present ('quiz ' ) || $ present ('exercise ' ), 'quizzes ' );
283292 $ mark ($ wantedGroups , $ present ('scorm ' ), 'scorm ' );
284293 } else {
285294 $ mark ($ wantedGroups , $ present ('link ' ), 'links ' );
286295 $ mark ($ wantedGroups , $ present ('forum ' ) || $ present ('forum_category ' ), 'forums ' );
287- $ mark ($ wantedGroups , $ present ('document ' ), 'documents ' );
296+ $ mark ($ wantedGroups , $ present ('document ' ) || $ present ( ' documents ' ) , 'documents ' );
288297 $ mark ($ wantedGroups , $ present ('quiz ' ) || $ present ('exercise ' ), 'quizzes ' );
289298 $ mark ($ wantedGroups , $ present ('scorm ' ), 'scorm ' );
290299 }
@@ -3288,40 +3297,158 @@ private function loadLegacyCourseForAnyBackup(string $backupId, string $force =
32883297 throw new \RuntimeException ('Unsupported package: neither course_info.dat nor moodle_backup.xml found. ' );
32893298 }
32903299
3300+ /**
3301+ * Normalize resource buckets to the exact keys supported by CourseRestorer.
3302+ * Only the canonical keys below are produced; common aliases are mapped.
3303+ * - Never drop data: merge buckets; keep __meta as-is.
3304+ * - Make sure "document" survives if it existed before.
3305+ */
32913306 private function normalizeBucketsForRestorer (object $ course ): void
32923307 {
32933308 if (!isset ($ course ->resources ) || !\is_array ($ course ->resources )) {
32943309 return ;
32953310 }
32963311
3297- $ map = [
3298- 'link ' => RESOURCE_LINK ,
3299- 'link_category ' => RESOURCE_LINKCATEGORY ,
3300- 'forum ' => RESOURCE_FORUM ,
3301- 'forum_category ' => RESOURCE_FORUMCATEGORY ,
3302- 'forum_topic ' => RESOURCE_FORUMTOPIC ,
3303- 'forum_post ' => RESOURCE_FORUMPOST ,
3304- 'thread ' => RESOURCE_FORUMTOPIC ,
3305- 'post ' => RESOURCE_FORUMPOST ,
3306- 'document ' => RESOURCE_DOCUMENT ,
3307- 'quiz ' => RESOURCE_QUIZ ,
3308- 'exercise_question ' => RESOURCE_QUIZQUESTION ,
3309- 'survey ' => RESOURCE_SURVEY ,
3310- 'survey_question ' => RESOURCE_SURVEYQUESTION ,
3311- 'tool_intro ' => RESOURCE_TOOL_INTRO ,
3312+ // Canonical keys -> constants used by the restorer (reference only)
3313+ $ allowed = [
3314+ 'link ' => \defined ('RESOURCE_LINK ' ) ? RESOURCE_LINK : null ,
3315+ 'link_category ' => \defined ('RESOURCE_LINKCATEGORY ' ) ? RESOURCE_LINKCATEGORY : null ,
3316+ 'forum ' => \defined ('RESOURCE_FORUM ' ) ? RESOURCE_FORUM : null ,
3317+ 'forum_category ' => \defined ('RESOURCE_FORUMCATEGORY ' ) ? RESOURCE_FORUMCATEGORY : null ,
3318+ 'forum_topic ' => \defined ('RESOURCE_FORUMTOPIC ' ) ? RESOURCE_FORUMTOPIC : null ,
3319+ 'forum_post ' => \defined ('RESOURCE_FORUMPOST ' ) ? RESOURCE_FORUMPOST : null ,
3320+ 'document ' => \defined ('RESOURCE_DOCUMENT ' ) ? RESOURCE_DOCUMENT : null ,
3321+ 'quiz ' => \defined ('RESOURCE_QUIZ ' ) ? RESOURCE_QUIZ : null ,
3322+ 'exercise_question ' => \defined ('RESOURCE_QUIZQUESTION ' ) ? RESOURCE_QUIZQUESTION : null ,
3323+ 'survey ' => \defined ('RESOURCE_SURVEY ' ) ? RESOURCE_SURVEY : null ,
3324+ 'survey_question ' => \defined ('RESOURCE_SURVEYQUESTION ' ) ? RESOURCE_SURVEYQUESTION : null ,
3325+ 'tool_intro ' => \defined ('RESOURCE_TOOL_INTRO ' ) ? RESOURCE_TOOL_INTRO : null ,
33123326 ];
33133327
3314- $ res = $ course ->resources ;
3315- foreach ($ map as $ from => $ to ) {
3316- if (isset ($ res [$ from ]) && \is_array ($ res [$ from ])) {
3317- if (!isset ($ res [$ to ])) {
3318- $ res [$ to ] = $ res [$ from ];
3328+ // Minimal, well-scoped alias map (input -> canonical)
3329+ $ alias = [
3330+ // docs
3331+ 'documents ' => 'document ' ,
3332+ 'document ' => 'document ' ,
3333+ 'document ' => 'document ' ,
3334+ 'Document ' => 'document ' ,
3335+
3336+ // tool intro
3337+ 'tool introduction ' => 'tool_intro ' ,
3338+ 'tool_introduction ' => 'tool_intro ' ,
3339+ 'tool/introduction ' => 'tool_intro ' ,
3340+ 'tool intro ' => 'tool_intro ' ,
3341+ 'Tool introduction ' => 'tool_intro ' ,
3342+
3343+ // forums
3344+ 'forum ' => 'forum ' ,
3345+ 'forums ' => 'forum ' ,
3346+ 'forum_category ' => 'forum_category ' ,
3347+ 'Forum_Category ' => 'forum_category ' ,
3348+ 'forumcategory ' => 'forum_category ' ,
3349+ 'forum_topic ' => 'forum_topic ' ,
3350+ 'forumtopic ' => 'forum_topic ' ,
3351+ 'thread ' => 'forum_topic ' ,
3352+ 'forum_post ' => 'forum_post ' ,
3353+ 'forumpost ' => 'forum_post ' ,
3354+ 'post ' => 'forum_post ' ,
3355+
3356+ // links
3357+ 'link ' => 'link ' ,
3358+ 'links ' => 'link ' ,
3359+ 'link_category ' => 'link_category ' ,
3360+ 'link category ' => 'link_category ' ,
3361+
3362+ // quiz + questions
3363+ 'quiz ' => 'quiz ' ,
3364+ 'exercise_question ' => 'exercise_question ' ,
3365+ 'Exercise_Question ' => 'exercise_question ' ,
3366+ 'exercisequestion ' => 'exercise_question ' ,
3367+
3368+ // surveys
3369+ 'survey ' => 'survey ' ,
3370+ 'surveys ' => 'survey ' ,
3371+ 'survey_question ' => 'survey_question ' ,
3372+ 'surveyquestion ' => 'survey_question ' ,
3373+ ];
3374+
3375+ $ before = $ course ->resources ;
3376+
3377+ // Keep meta buckets verbatim
3378+ $ meta = [];
3379+ foreach ($ before as $ k => $ v ) {
3380+ if (\is_string ($ k ) && str_starts_with ($ k , '__ ' )) {
3381+ $ meta [$ k ] = $ v ;
3382+ unset($ before [$ k ]);
3383+ }
3384+ }
3385+
3386+ $ hadDocument =
3387+ isset ($ before ['document ' ]) ||
3388+ isset ($ before ['Document ' ]) ||
3389+ isset ($ before ['documents ' ]);
3390+
3391+ // Merge helper (preserve numeric/string ids)
3392+ $ merge = static function (array $ dst , array $ src ): array {
3393+ foreach ($ src as $ id => $ obj ) {
3394+ if (!array_key_exists ($ id , $ dst )) {
3395+ $ dst [$ id ] = $ obj ;
33193396 }
3320- unset($ res [$ from ]);
3397+ }
3398+ return $ dst ;
3399+ };
3400+
3401+ $ out = [];
3402+
3403+ foreach ($ before as $ rawKey => $ bucket ) {
3404+ if (!\is_array ($ bucket )) {
3405+ // Unexpected shape; skip silently (defensive)
3406+ continue ;
3407+ }
3408+
3409+ // Normalize key shape first
3410+ $ k = (string ) $ rawKey ;
3411+ $ norm = strtolower (trim ($ k ));
3412+ $ norm = strtr ($ norm , ['\\' => '/ ' , '- ' => '_ ' ]); // cheap normalization
3413+ // Map via alias table if present
3414+ $ canon = $ alias [$ norm ] ?? $ alias [str_replace ('/ ' , '_ ' , $ norm )] ?? null ;
3415+
3416+ // If still unknown, try a sane guess: underscores + lowercase
3417+ if (null === $ canon ) {
3418+ $ guess = str_replace (['/ ' , ' ' ], '_ ' , $ norm );
3419+ $ canon = \array_key_exists ($ guess , $ allowed ) ? $ guess : null ;
3420+ }
3421+
3422+ // Only produce buckets with canonical keys we support; unknown keys are ignored here
3423+ if (null !== $ canon && \array_key_exists ($ canon , $ allowed )) {
3424+ $ out [$ canon ] = isset ($ out [$ canon ]) ? $ merge ($ out [$ canon ], $ bucket ) : $ bucket ;
33213425 }
33223426 }
33233427
3324- $ course ->resources = $ res ;
3428+ // Hard safety net: if a "document" bucket existed before, ensure it remains.
3429+ if ($ hadDocument && !isset ($ out ['document ' ])) {
3430+ $ out ['document ' ] = (array ) ($ before ['document ' ] ?? $ before ['Document ' ] ?? $ before ['documents ' ] ?? []);
3431+ }
3432+
3433+ // Gentle ordering to keep things readable
3434+ $ order = [
3435+ 'announcement ' , 'document ' , 'link ' , 'link_category ' ,
3436+ 'forum ' , 'forum_category ' , 'forum_topic ' , 'forum_post ' ,
3437+ 'quiz ' , 'exercise_question ' ,
3438+ 'survey ' , 'survey_question ' ,
3439+ 'learnpath ' , 'tool_intro ' ,
3440+ 'work ' ,
3441+ ];
3442+ $ w = [];
3443+ foreach ($ order as $ i => $ key ) { $ w [$ key ] = $ i ; }
3444+ uksort ($ out , static function ($ a , $ b ) use ($ w ) {
3445+ $ wa = $ w [$ a ] ?? 9999 ;
3446+ $ wb = $ w [$ b ] ?? 9999 ;
3447+ return $ wa <=> $ wb ?: strcasecmp ($ a , $ b );
3448+ });
3449+
3450+ // Final assign (meta first, then normalized buckets)
3451+ $ course ->resources = $ meta + $ out ;
33253452 }
33263453
33273454 /**
0 commit comments