|
2 | 2 |
|
3 | 3 | /* For licensing terms, see /license.txt */
|
4 | 4 |
|
| 5 | +use Chamilo\CoreBundle\Entity\Session; |
5 | 6 | use Chamilo\CoreBundle\Helpers\ChamiloHelper;
|
6 | 7 | use Chamilo\CourseBundle\Component\CourseCopy\CourseArchiver;
|
7 | 8 | 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; |
8 | 12 |
|
9 | 13 | /**
|
10 | 14 | * Script managing the learnpath upload. To best treat the uploaded file, make sure we can identify it.
|
|
26 | 30 | }
|
27 | 31 |
|
28 | 32 | /*
|
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. |
32 | 35 | */
|
33 | 36 | $user_file = $_FILES['user_file'] ?? [];
|
34 | 37 | $is_error = $user_file['error'] ?? false;
|
35 | 38 | $em = Database::getManager();
|
36 | 39 |
|
37 | 40 | if (isset($_POST) && $is_error) {
|
| 41 | + // Redirect to the upload screen with an error |
38 | 42 | unset($_FILES['user_file']);
|
39 | 43 | ChamiloHelper::redirectTo(api_get_path(WEB_PATH).'main/upload/index.php?'.api_get_cidreq().'&origin=course&curdirpath=/&tool=learnpath');
|
40 | 44 | }
|
41 | 45 | elseif ('POST' === $_SERVER['REQUEST_METHOD']
|
42 | 46 | && !empty($_FILES['user_file']['name'])
|
43 | 47 | ) {
|
44 |
| - // A file upload has been detected, now deal with the file... |
| 48 | + // A file upload has been detected, now handle it... |
45 | 49 | $s = $_FILES['user_file']['name'];
|
46 | 50 |
|
47 |
| - // Get name of the zip file without the extension. |
| 51 | + // Derive filename info |
48 | 52 | $info = pathinfo($s);
|
49 | 53 | $filename = $info['basename'];
|
50 |
| - $extension = $info['extension']; |
| 54 | + $extension = $info['extension'] ?? ''; |
51 | 55 | $file_base_name = str_replace('.'.$extension, '', $filename);
|
52 | 56 |
|
53 | 57 | $new_dir = api_replace_dangerous_char(trim($file_base_name));
|
|
64 | 68 | case 'chamilo':
|
65 | 69 | $filename = CourseArchiver::importUploadedFile($_FILES['user_file']['tmp_name']);
|
66 | 70 | if ($filename) {
|
67 |
| - $course = CourseArchiver::readCourse($filename, false); |
| 71 | + $course = CourseArchiver::readCourse($filename, false); |
68 | 72 | $courseRestorer = new CourseRestorer($course);
|
69 | 73 | $courseRestorer->set_file_option(FILE_OVERWRITE);
|
70 | 74 | $courseRestorer->restore('', api_get_session_id());
|
71 | 75 | Display::addFlash(Display::return_message(get_lang('File upload succeeded!')));
|
72 | 76 | }
|
73 | 77 | break;
|
74 | 78 | case 'scorm':
|
75 |
| - $tmpFile = $_FILES['user_file']['tmp_name']; |
76 |
| - $fileSize = filesize($tmpFile); |
| 79 | + $tmpFile = $_FILES['user_file']['tmp_name']; |
77 | 80 | $blocked = learnpath::verify_document_size($tmpFile);
|
78 | 81 | if ($blocked) {
|
79 | 82 | ChamiloHelper::redirectTo(api_get_path(WEB_PATH).'main/upload/index.php?'.api_get_cidreq().'&origin=course&curdirpath=/&tool=learnpath');
|
|
91 | 94 | $scorm->parse_manifest();
|
92 | 95 | $lp = $scorm->import_manifest(api_get_course_int_id(), $_REQUEST['use_max_score']);
|
93 | 96 | if ($lp) {
|
94 |
| - $lp->setContentLocal($proximity) |
95 |
| - ->setContentMaker($maker); |
| 97 | + $lp->setContentLocal($proximity)->setContentMaker($maker); |
96 | 98 | $em->persist($lp);
|
97 | 99 | $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 | + |
98 | 118 | Display::addFlash(Display::return_message(get_lang('File upload succeeded!')));
|
99 | 119 | }
|
100 | 120 | }
|
|
127 | 147 | Display::addFlash(Display::return_message(get_lang('Unknown package format'), 'warning'));
|
128 | 148 |
|
129 | 149 | return false;
|
130 |
| - break; |
131 | 150 | }
|
132 | 151 | } 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) |
138 | 153 |
|
139 | 154 | if (!isset($_POST['file_name'])) {
|
140 | 155 | return false;
|
141 | 156 | }
|
142 | 157 |
|
143 |
| - // Escape path with basename so it can only be directly into the archive/ directory. |
| 158 | + // Escape path to ensure it only targets /archive/ |
144 | 159 | $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'] ?? ''; |
149 | 165 | $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)); |
151 | 167 |
|
152 | 168 | $result = learnpath::verify_document_size($s);
|
153 | 169 | 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.'))); |
157 | 171 | }
|
158 | 172 | $type = learnpath::getPackageType($s, basename($s));
|
159 | 173 |
|
160 | 174 | switch ($type) {
|
161 | 175 | case 'scorm':
|
162 | 176 | $oScorm = new scorm();
|
163 |
| - $entity = $oScorm->getEntity(); |
| 177 | + |
| 178 | + // Import from local path |
164 | 179 | $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 |
| - } |
172 | 180 |
|
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 | + ); |
176 | 193 | }
|
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); |
180 | 198 | }
|
181 | 199 |
|
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 | + } |
189 | 218 | break;
|
190 | 219 | case 'aicc':
|
191 |
| - $oAICC = new aicc(); |
| 220 | + $oAICC = new aicc(); |
192 | 221 | $entity = $oAICC->getEntity();
|
193 | 222 | $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 | + |
196 | 228 | if (!empty($config_dir)) {
|
197 | 229 | $oAICC->parse_config_files($config_dir);
|
198 | 230 | $oAICC->import_aicc(api_get_course_id());
|
|
217 | 249 | break;
|
218 | 250 | case '':
|
219 | 251 | default:
|
220 |
| - // There was an error, clean the file from the temp dir |
| 252 | + // Unknown format: cleanup and warn |
221 | 253 | if (is_file($s)) {
|
222 | 254 | unlink($s);
|
223 | 255 | }
|
224 | 256 | Display::addFlash(
|
225 | 257 | Display::return_message(get_lang('Unknown package format'), 'warning')
|
226 | 258 | );
|
227 |
| - |
228 | 259 | return false;
|
229 | 260 | }
|
230 | 261 | }
|
0 commit comments