ILIAS  release_6 Revision v6.24-5-g0c8bfefb3b8
class.ilCmiXapiContentUploadImporter.php
Go to the documentation of this file.
1<?php
2
3/* Copyright (c) 1998-2019 ILIAS open source, Extended GPL, see docs/LICENSE */
4
5use ILIAS\FileUpload\DTO\UploadResult as FileUploadResult;
6use ILIAS\FileUpload\DTO\ProcessingStatus as FileUploadProcessingStatus;
7use ILIAS\FileUpload\Location as FileUploadResultLocation;
8
19{
21
22 const RELATIVE_XSD_DIRECTORY = 'Modules/CmiXapi/xml/contentschema';
23
26
27 const CMI5_XML = 'cmi5.xml';
28 const CMI5_XSD = 'cmi5_v1_CourseStructure.xsd';
29
30 const TINCAN_XML = 'tincan.xml';
31 const TINCAN_XSD = 'tincan.xsd';
32
36 protected static $CONTENT_XML_FILENAMES = [
38 ];
39
43 protected static $CONTENT_XSD_FILENAMES = [
44 self::CMI5_XML => self::CMI5_XSD,
45 self::TINCAN_XML => self::TINCAN_XSD
46 ];
47
51 protected $object;
52
58 {
59 $this->object = $object;
60 }
61
66 {
67 global $DIC; /* @var \ILIAS\DI\Container $DIC */
68
69 if (!$DIC->filesystem()->web()->has($this->getWebDataDirRelativeObjectDirectory())) {
70 $DIC->filesystem()->web()->createDir($this->getWebDataDirRelativeObjectDirectory());
71 }
72 }
73
74 protected function sanitizeObjectDirectory()
75 {
76 ilUtil::renameExecutables(implode(DIRECTORY_SEPARATOR, [
78 ]));
79 }
80
86 public function importServerFile($serverFile)
87 {
89
90 $this->handleFile($serverFile);
91
93 }
94
99 protected function handleFile(string $serverFile)
100 {
101 $fileInfo = pathinfo($serverFile);
102
103 switch ($fileInfo['extension']) {
105
106 $this->handleXmlFile($serverFile);
107 break;
108
110
111 $this->handleZipContentUpload($serverFile);
112
113 if ($this->hasStoredContentXml()) {
114 $this->handleXmlFile($this->getStoredContentXml());
115 }
116
117 break;
118 }
119 }
120
127 public function importFormUpload(ilFileInputGUI $uploadInput)
128 {
130
131 $fileData = $_POST[$uploadInput->getPostVar()];
132
133 $uploadResult = $this->getUpload(
134 $fileData['tmp_name']
135 );
136
137 $this->handleUpload($uploadResult);
138
140 }
141
148 protected function getUpload($uploadFilePath)
149 {
150 global $DIC; /* @var \ILIAS\DI\Container $DIC */
151
152 if ($DIC->upload()->hasUploads()) {
153 if (!$DIC->upload()->hasBeenProcessed()) {
154 $DIC->upload()->process();
155 }
156
157 /* @var FileUploadResult $result */
158
159 $results = $DIC->upload()->getResults();
160
161 if (isset($results[$uploadFilePath])) {
162 $result = $results[$uploadFilePath];
163
164 if ($result->getStatus() == FileUploadProcessingStatus::OK) {
165 return $result;
166 }
167
169 'upload processing failed with message ' .
170 '"' . $result->getStatus()->getMessage() . '"'
171 );
172 }
173
174 throw new ilCmiXapiInvalidUploadContentException('upload lost during processing!');
175 }
176
177 throw new ilCmiXapiInvalidUploadContentException('no upload provided!');
178 }
179
184 protected function handleUpload(FileUploadResult $uploadResult)
185 {
186 switch ($this->fetchFileExtension($uploadResult)) {
188
189 $this->handleXmlFileFromUpload($uploadResult->getName(),$uploadResult->getPath());
190 break;
191
193
194 $this->handleZipContentUpload($uploadResult->getPath());
195
196 if ($this->hasStoredContentXml()) {
197 $this->handleXmlFile($this->getStoredContentXml());
198 }
199
200 break;
201 }
202 }
203
208 protected function handleXmlFile($xmlFilePath)
209 {
210 $dom = new DOMDocument();
211 $dom->load($xmlFilePath);
212
213 switch (basename($xmlFilePath)) {
214 case self::CMI5_XML:
215
216 $xsdFilePath = $this->getXsdFilePath(self::CMI5_XSD);
217 $this->validateXmlFile($dom, $xsdFilePath);
218
219 $this->initObjectFromCmi5Xml($dom);
220
221 break;
222
223 case self::TINCAN_XML:
224
225 $xsdFilePath = $this->getXsdFilePath(self::TINCAN_XSD);
226 $this->validateXmlFile($dom, $xsdFilePath);
227
228 $this->initObjectFromTincanXml($dom);
229
230 break;
231 }
232 }
233
239 protected function handleXmlFileFromUpload($xmlFileName, $xmlFilePath)
240 {
241 $dom = new DOMDocument();
242 $dom->load($xmlFilePath);
243 switch (basename($xmlFileName)) {
244 case self::CMI5_XML:
245
246 $xsdFilePath = $this->getXsdFilePath(self::CMI5_XSD);
247 $this->validateXmlFile($dom, $xsdFilePath);
248
249 $this->initObjectFromCmi5Xml($dom);
250
251 break;
252
253 case self::TINCAN_XML:
254
255 $xsdFilePath = $this->getXsdFilePath(self::TINCAN_XSD);
256 $this->validateXmlFile($dom, $xsdFilePath);
257
258 $this->initObjectFromTincanXml($dom);
259
260 break;
261 }
262 }
263
264 protected function validateXmlFile(DOMDocument $dom, $xsdFilePath)
265 {
266 if (!$dom->schemaValidate($xsdFilePath)) {
267 throw new ilCmiXapiInvalidUploadContentException('invalid content xml given!');
268 }
269 }
270
271 protected function handleZipContentUpload($uploadFilePath)
272 {
273 $targetPath = $this->getAbsoluteObjectDirectory();
274 $zar = new ZipArchive();
275 $zar->open($uploadFilePath);
276 $zar->extractTo($targetPath);
277 $zar->close();
278 }
279
283 protected function getAbsoluteObjectDirectory()
284 {
285 $dirs = [
286 ILIAS_ABSOLUTE_PATH,
289 ];
290
291 return implode(DIRECTORY_SEPARATOR, $dirs);
292 }
293
298 {
299 return self::RELATIVE_CONTENT_DIRECTORY_NAMEBASE . $this->object->getId();
300 }
301
306 protected function fetchFileExtension(FileUploadResult $uploadResult)
307 {
308 return pathinfo($uploadResult->getName(), PATHINFO_EXTENSION);
309 }
310
314 protected function hasStoredContentXml()
315 {
316 return $this->getStoredContentXml() !== '';
317 }
318
322 protected function getStoredContentXml()
323 {
324 global $DIC; /* @var \ILIAS\DI\Container $DIC */
325
326 foreach (self::$CONTENT_XML_FILENAMES as $xmlFileName) {
327 $xmlFilePath = $this->getWebDataDirRelativeObjectDirectory() . DIRECTORY_SEPARATOR . $xmlFileName;
328
329 if ($DIC->filesystem()->web()->has($xmlFilePath)) {
330 return $this->getAbsoluteObjectDirectory() . DIRECTORY_SEPARATOR . $xmlFileName;
331 }
332 }
333
334 return '';
335 }
336
341 protected function getXsdFilePath($xsdFileName)
342 {
343 return ILIAS_ABSOLUTE_PATH . DIRECTORY_SEPARATOR . self::RELATIVE_XSD_DIRECTORY . DIRECTORY_SEPARATOR . $xsdFileName;
344 }
345
346 protected function initObjectFromCmi5Xml($dom)
347 {
348 global $DIC;
349 $xPath = new DOMXPath($dom);
350
351 $courseNode = $xPath->query("//*[local-name()='course']")->item(0);
352 // TODO: multilanguage support
353 $title = $xPath->query("//*[local-name()='title']/*[local-name()='langstring']", $courseNode)->item(0)->nodeValue;
354 $this->object->setTitle(trim($title));
355
356 $description = $xPath->query("//*[local-name()='description']/*[local-name()='langstring']", $courseNode)->item(0)->nodeValue;
357 $this->object->setDescription(trim($description));
358
359 $publisherId = trim($courseNode->getAttribute('id'));
360 $this->object->setPublisherId($publisherId);
361
362 $activityId = $this->generateActivityId($publisherId);
363 $this->object->setActivityId($activityId);
364
365 foreach ($xPath->query("//*[local-name()='au']") as $assignedUnitNode) {
366 $relativeLaunchUrl = $xPath->query("//*[local-name()='url']", $assignedUnitNode)->item(0)->nodeValue;
367 $launchParameters = $xPath->query("//*[local-name()='launchParameters']", $assignedUnitNode)->item(0)->nodeValue;
368 $moveOn = trim($assignedUnitNode->getAttribute('moveOn'));
369 $entitlementKey = $xPath->query("//*[local-name()='entitlementKey']", $assignedUnitNode)->item(0)->nodeValue;
370 $masteryScore = trim($assignedUnitNode->getAttribute('masteryScore'));
371
372 if (!empty($relativeLaunchUrl)) {
373 $this->object->setLaunchUrl(trim($relativeLaunchUrl));
374 }
375 if (!empty($launchParameters)) {
376 $this->object->setLaunchParameters(trim($launchParameters));
377 }
378 if (!empty($moveOn)) {
381 }
382 $this->object->setMoveOn($moveOn);
383 }
384 if (!empty($entitlementKey)) {
385 $this->object->setEntitlementKey($entitlementKey);
386 }
387 if (!empty($masteryScore)) {
388 $this->object->setMasteryScore($masteryScore);
389 }
390 else {
391 $this->object->setMasteryScore(ilObjCmiXapi::LMS_MASTERY_SCORE);
392 }
393
394 break; // TODO: manage multi au imports
395 }
396 $xml_str = $dom->saveXML();
397 $this->object->setXmlManifest($xml_str);
398 $this->object->update();
399 $this->object->save();
400
401 $lpSettings = new ilLPObjSettings($this->object->getId());
403 switch ($moveOn)
404 {
407 break;
410 break;
413 break;
414 case ilCmiXapiLP::MOVEON_COMPLETED_AND_PASSED : // ich würde es noch implementieren
416 break;
417 }
418 $lpSettings->setMode($mode);
419 $lpSettings->update();
420 }
421
422 protected function initObjectFromTincanXml($dom)
423 {
424 $xPath = new DOMXPath($dom);
425
426 foreach ($xPath->query("//*[local-name()='activity']") as $activityNode) {
427 $title = $xPath->query("//*[local-name()='name']", $activityNode)->item(0)->nodeValue;
428 $this->object->setTitle(trim($title));
429
430 $description = $xPath->query("//*[local-name()='description']", $activityNode)->item(0)->nodeValue;
431 $this->object->setDescription(trim($description));
432
433 $activityId = $activityNode->getAttribute('id');
434 $this->object->setActivityId(trim($activityId));
435
436 $relativeLaunchUrl = $xPath->query("//*[local-name()='launch']", $activityNode)->item(0)->nodeValue;
437 $this->object->setLaunchUrl(trim($relativeLaunchUrl));
438
439 break; // TODO: manage multi activities imports
440 }
441
442 $xml_str = $dom->saveXML();
443 $this->object->setXmlManifest($xml_str);
444 $this->object->update();
445 $this->object->save();
446 }
447
448 private function generateActivityId($publisherId)
449 {
450 global $DIC;
451 $objId = $this->object->getId();
452 $activityId = "https://ilias.de/cmi5/activityid/".(new \Ramsey\Uuid\UuidFactory())->uuid3(ilCmiXapiUser::getIliasUuid(),$objId . '-' . $publisherId);
453 return $activityId;
454 }
455
456
457}
$result
$_POST["username"]
An exception for terminatinating execution or to throw for unit testing.
__construct(ilObjCmiXapi $object)
ilCmiXapiContentUploadImporter constructor.
fetchFileExtension(FileUploadResult $uploadResult)
const MOVEON_COMPLETED_OR_PASSED
const MOVEON_COMPLETED_AND_PASSED
This class represents a file property in a property form.
getPostVar()
Get Post Variable.
static getWebspaceDir($mode="filesystem")
get webspace directory
static renameExecutables($a_dir)
Rename uploaded executables for security reasons.
Interface Location.
Definition: Location.php:17
$results
$DIC
Definition: xapitoken.php:46
$objId
Definition: xapitoken.php:41