Skip to content

Commit 64e0b55

Browse files
committed
Merge branch 'GH-1970-3' of github.com:christianbeeznest/chamilo-lms into 1970
2 parents db14670 + e98d31a commit 64e0b55

File tree

3 files changed

+448
-82
lines changed

3 files changed

+448
-82
lines changed

public/main/lp/learnpath.class.php

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
use Chamilo\CourseBundle\Entity\CStudentPublication;
3030
use Chamilo\CourseBundle\Entity\CSurvey;
3131
use Chamilo\CourseBundle\Entity\CTool;
32+
use Chamilo\CourseBundle\Repository\CDocumentRepository;
3233
use Chamilo\CourseBundle\Repository\CLpRelUserRepository;
3334
use ChamiloSession as Session;
3435
use Doctrine\Common\Collections\Criteria;
@@ -789,21 +790,20 @@ public function delete($courseInfo = null, $id = null, $delete = 'keep')
789790
$course_id = isset($courseInfo['real_id']) ? $courseInfo['real_id'] : $course_id;
790791
}
791792

792-
// TODO: Implement a way of getting this to work when the current object is not set.
793-
// In clear: implement this in the item class as well (abstract class) and use the given ID in queries.
794-
// If an ID is specifically given and the current LP is not the same, prevent delete.
793+
// Prevent deleting a different LP than the current one if an explicit ID was passed
795794
if (!empty($id) && ($id != $this->lp_id)) {
796795
return false;
797796
}
798797

799-
$course = api_get_course_entity();
798+
$course = api_get_course_entity();
800799
$session = api_get_session_entity();
801-
/* @var CLp $lp */
800+
801+
/** @var CLp|null $lp */
802802
$lp = Container::getLpRepository()->find($this->lp_id);
803803

804-
// 1) Detach the asset to avoid FK constraint
805-
$asset = $lp->getAsset();
806-
if ($asset) {
804+
// Detach the asset to avoid FK constraint
805+
$asset = $lp ? $lp->getAsset() : null;
806+
if ($asset && $lp) {
807807
$lp->setAsset(null);
808808
$em = Database::getManager();
809809
$em->persist($lp);
@@ -815,10 +815,12 @@ public function delete($courseInfo = null, $id = null, $delete = 'keep')
815815
Container::getAssetRepository()->delete($asset);
816816
}
817817

818+
// Remove resource links (course/session context)
818819
Database::getManager()
819820
->getRepository(ResourceLink::class)
820821
->removeByResourceInContext($lp, $course, $session);
821822

823+
// Remove from gradebook if present
822824
$link_info = GradebookUtils::isResourceInCourseGradebook(
823825
api_get_course_int_id(),
824826
4,
@@ -830,8 +832,9 @@ public function delete($courseInfo = null, $id = null, $delete = 'keep')
830832
GradebookUtils::remove_resource_from_course_gradebook($link_info['id']);
831833
}
832834

833-
$trackRepo = Container::$container->get(TrackEDefaultRepository::class);
834-
$resourceNode = $lp->getResourceNode();
835+
// Tracking event
836+
$trackRepo = Container::$container->get(TrackEDefaultRepository::class);
837+
$resourceNode = $lp ? $lp->getResourceNode() : null;
835838
if ($resourceNode) {
836839
$trackRepo->registerResourceEvent(
837840
$resourceNode,
@@ -841,6 +844,20 @@ public function delete($courseInfo = null, $id = null, $delete = 'keep')
841844
api_get_session_id()
842845
);
843846
}
847+
848+
// Purge the SCORM ZIP registered under Documents/Learning paths (teacher-only folder)
849+
// This keeps the course storage meter consistent after LP deletion.
850+
try {
851+
if ($lp && $course) {
852+
$em = Database::getManager();
853+
/** @var CDocumentRepository $docRepo */
854+
$docRepo = $em->getRepository(CDocument::class);
855+
$docRepo->purgeScormZip($course, $lp);
856+
}
857+
} catch (\Throwable $e) {
858+
// Do not block LP deletion if purge fails; just log the error.
859+
error_log('[learnpath::delete] Failed to purge SCORM ZIP from Documents: '.$e->getMessage());
860+
}
844861
}
845862

846863
/**

public/main/lp/lp_upload.php

Lines changed: 83 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,13 @@
22

33
/* For licensing terms, see /license.txt */
44

5+
use Chamilo\CoreBundle\Entity\Session;
56
use Chamilo\CoreBundle\Helpers\ChamiloHelper;
67
use Chamilo\CourseBundle\Component\CourseCopy\CourseArchiver;
78
use Chamilo\CourseBundle\Component\CourseCopy\CourseRestorer;
9+
use Chamilo\CourseBundle\Entity\CDocument;
10+
use Chamilo\CourseBundle\Repository\CDocumentRepository;
11+
use Symfony\Component\HttpFoundation\File\UploadedFile;
812

913
/**
1014
* Script managing the learnpath upload. To best treat the uploaded file, make sure we can identify it.
@@ -26,28 +30,28 @@
2630
}
2731

2832
/*
29-
* Check the request method in place of a variable from POST
30-
* because if the file size exceed the maximum file upload
31-
* size set in php.ini, all variables from POST are cleared !
33+
* Check the request method instead of relying on POST variables,
34+
* because if the uploaded file exceeds php.ini limits, POST is cleared.
3235
*/
3336
$user_file = $_FILES['user_file'] ?? [];
3437
$is_error = $user_file['error'] ?? false;
3538
$em = Database::getManager();
3639

3740
if (isset($_POST) && $is_error) {
41+
// Redirect to the upload screen with an error
3842
unset($_FILES['user_file']);
3943
ChamiloHelper::redirectTo(api_get_path(WEB_PATH).'main/upload/index.php?'.api_get_cidreq().'&origin=course&curdirpath=/&tool=learnpath');
4044
}
4145
elseif ('POST' === $_SERVER['REQUEST_METHOD']
4246
&& !empty($_FILES['user_file']['name'])
4347
) {
44-
// A file upload has been detected, now deal with the file...
48+
// A file upload has been detected, now handle it...
4549
$s = $_FILES['user_file']['name'];
4650

47-
// Get name of the zip file without the extension.
51+
// Derive filename info
4852
$info = pathinfo($s);
4953
$filename = $info['basename'];
50-
$extension = $info['extension'];
54+
$extension = $info['extension'] ?? '';
5155
$file_base_name = str_replace('.'.$extension, '', $filename);
5256

5357
$new_dir = api_replace_dangerous_char(trim($file_base_name));
@@ -64,16 +68,15 @@
6468
case 'chamilo':
6569
$filename = CourseArchiver::importUploadedFile($_FILES['user_file']['tmp_name']);
6670
if ($filename) {
67-
$course = CourseArchiver::readCourse($filename, false);
71+
$course = CourseArchiver::readCourse($filename, false);
6872
$courseRestorer = new CourseRestorer($course);
6973
$courseRestorer->set_file_option(FILE_OVERWRITE);
7074
$courseRestorer->restore('', api_get_session_id());
7175
Display::addFlash(Display::return_message(get_lang('File upload succeeded!')));
7276
}
7377
break;
7478
case 'scorm':
75-
$tmpFile = $_FILES['user_file']['tmp_name'];
76-
$fileSize = filesize($tmpFile);
79+
$tmpFile = $_FILES['user_file']['tmp_name'];
7780
$blocked = learnpath::verify_document_size($tmpFile);
7881
if ($blocked) {
7982
ChamiloHelper::redirectTo(api_get_path(WEB_PATH).'main/upload/index.php?'.api_get_cidreq().'&origin=course&curdirpath=/&tool=learnpath');
@@ -91,10 +94,27 @@
9194
$scorm->parse_manifest();
9295
$lp = $scorm->import_manifest(api_get_course_int_id(), $_REQUEST['use_max_score']);
9396
if ($lp) {
94-
$lp->setContentLocal($proximity)
95-
->setContentMaker($maker);
97+
$lp->setContentLocal($proximity)->setContentMaker($maker);
9698
$em->persist($lp);
9799
$em->flush();
100+
101+
/** @var CDocumentRepository $docRepo */
102+
$docRepo = $em->getRepository(CDocument::class);
103+
104+
/** @var Session|null $session */
105+
$session = api_get_session_entity();
106+
107+
$uploadedZip = new UploadedFile(
108+
$_FILES['user_file']['tmp_name'],
109+
$_FILES['user_file']['name'],
110+
$_FILES['user_file']['type'] ?? null,
111+
$_FILES['user_file']['error'] ?? 0,
112+
true
113+
);
114+
115+
// Save under Documents / Learning paths (course/session aware)
116+
$docRepo->registerScormZip(api_get_course_entity(), $session, $lp, $uploadedZip);
117+
98118
Display::addFlash(Display::return_message(get_lang('File upload succeeded!')));
99119
}
100120
}
@@ -127,72 +147,84 @@
127147
Display::addFlash(Display::return_message(get_lang('Unknown package format'), 'warning'));
128148

129149
return false;
130-
break;
131150
}
132151
} elseif ('POST' === $_SERVER['REQUEST_METHOD']) {
133-
// end if is_uploaded_file
134-
// If file name given to get in /upload/, try importing this way.
135-
// A file upload has been detected, now deal with the file...
136-
// Directory creation.
137-
$stopping_error = false;
152+
// Fallback: import from an existing file in /upload/ (no $_FILES)
138153

139154
if (!isset($_POST['file_name'])) {
140155
return false;
141156
}
142157

143-
// Escape path with basename so it can only be directly into the archive/ directory.
158+
// Escape path to ensure it only targets /archive/
144159
$s = api_get_path(SYS_ARCHIVE_PATH).basename($_POST['file_name']);
145-
// Get name of the zip file without the extension
146-
$info = pathinfo($s);
147-
$filename = $info['basename'];
148-
$extension = $info['extension'];
160+
161+
// Derive filename info
162+
$info = pathinfo($s);
163+
$filename = $info['basename'];
164+
$extension = $info['extension'] ?? '';
149165
$file_base_name = str_replace('.'.$extension, '', $filename);
150-
$new_dir = api_replace_dangerous_char(trim($file_base_name));
166+
$new_dir = api_replace_dangerous_char(trim($file_base_name));
151167

152168
$result = learnpath::verify_document_size($s);
153169
if ($result) {
154-
Display::addFlash(
155-
Display::return_message(get_lang('The file is too big to upload.'))
156-
);
170+
Display::addFlash(Display::return_message(get_lang('The file is too big to upload.')));
157171
}
158172
$type = learnpath::getPackageType($s, basename($s));
159173

160174
switch ($type) {
161175
case 'scorm':
162176
$oScorm = new scorm();
163-
$entity = $oScorm->getEntity();
177+
178+
// Import from local path
164179
$manifest = $oScorm->import_local_package($s, $current_dir);
165-
// The file was treated, it can now be cleaned from the temp dir
166-
unlink($s);
167-
if (!empty($manifest)) {
168-
$oScorm->parse_manifest();
169-
$oScorm->import_manifest(api_get_course_int_id(), $_REQUEST['use_max_score']);
170-
Display::addFlash(Display::return_message(get_lang('File upload succeeded!')));
171-
}
172180

173-
$proximity = '';
174-
if (!empty($_REQUEST['content_proximity'])) {
175-
$proximity = Database::escape_string($_REQUEST['content_proximity']);
181+
// Make a tmp copy of the ZIP (so we can register it in Documents after unlink)
182+
$uploadedZip = null;
183+
if (is_file($s)) {
184+
$tmpCopy = tempnam(sys_get_temp_dir(), 'scorm_zip_');
185+
@copy($s, $tmpCopy);
186+
$uploadedZip = new UploadedFile(
187+
$tmpCopy,
188+
basename($s),
189+
'application/zip',
190+
null,
191+
true
192+
);
176193
}
177-
$maker = '';
178-
if (!empty($_REQUEST['content_maker'])) {
179-
$maker = Database::escape_string($_REQUEST['content_maker']);
194+
195+
// Clean original file from /archive
196+
if (is_file($s)) {
197+
unlink($s);
180198
}
181199

182-
$entity
183-
->setContentLocal($proximity)
184-
->setContentMaker($maker)
185-
->setJsLib('scorm_api.php')
186-
;
187-
$em->persist($entity);
188-
$em->flush();
200+
if (!empty($manifest)) {
201+
$oScorm->parse_manifest();
202+
203+
// Create the LP entity (CLp)
204+
$lp = $oScorm->import_manifest(api_get_course_int_id(), $_REQUEST['use_max_score'] ?? 1);
205+
206+
if ($lp) {
207+
/** @var CDocumentRepository $docRepo */
208+
$docRepo = $em->getRepository(CDocument::class);
209+
210+
/** @var Session|null $session */
211+
$session = api_get_session_entity();
212+
213+
// Save under Documents / Learning paths (course/session aware)
214+
$docRepo->registerScormZip(api_get_course_entity(), $session, $lp, $uploadedZip);
215+
Display::addFlash(Display::return_message(get_lang('File upload succeeded!')));
216+
}
217+
}
189218
break;
190219
case 'aicc':
191-
$oAICC = new aicc();
220+
$oAICC = new aicc();
192221
$entity = $oAICC->getEntity();
193222
$config_dir = $oAICC->import_local_package($s, $current_dir);
194-
// The file was treated, it can now be cleaned from the temp dir
195-
unlink($s);
223+
224+
if (is_file($s)) {
225+
unlink($s);
226+
}
227+
196228
if (!empty($config_dir)) {
197229
$oAICC->parse_config_files($config_dir);
198230
$oAICC->import_aicc(api_get_course_id());
@@ -217,14 +249,13 @@
217249
break;
218250
case '':
219251
default:
220-
// There was an error, clean the file from the temp dir
252+
// Unknown format: cleanup and warn
221253
if (is_file($s)) {
222254
unlink($s);
223255
}
224256
Display::addFlash(
225257
Display::return_message(get_lang('Unknown package format'), 'warning')
226258
);
227-
228259
return false;
229260
}
230261
}

0 commit comments

Comments
 (0)