<select />
</component>
<component name="ChangeListManager">
- <list default="true" id="5d2ecd5e-a05a-4f96-a195-fa6372618165" name="Default Changelist" comment="wait #5127 @0.25">
- <change beforePath="$PROJECT_DIR$/.idea/php.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/php.xml" afterDir="false" />
+ <list default="true" id="5d2ecd5e-a05a-4f96-a195-fa6372618165" name="Default Changelist" comment="wip #5041 @1.5">
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/Http/Controllers/Admin/Operations/ELearningPackage/ImportOperation.php" beforeDir="false" afterPath="$PROJECT_DIR$/app/Http/Controllers/Admin/Operations/ELearningPackage/ImportOperation.php" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/Models/ELearningPackage.php" beforeDir="false" afterPath="$PROJECT_DIR$/app/Models/ELearningPackage.php" afterDir="false" />
- <change beforePath="$PROJECT_DIR$/composer.lock" beforeDir="false" afterPath="$PROJECT_DIR$/composer.lock" afterDir="false" />
+ <change beforePath="$PROJECT_DIR$/app/SubForms/ElearningPackageContent.php" beforeDir="false" afterPath="$PROJECT_DIR$/app/SubForms/ElearningPackageContent.php" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
<workItem from="1646226240059" duration="178000" />
<workItem from="1646233658795" duration="1190000" />
<workItem from="1646312342244" duration="162000" />
- <workItem from="1646312529624" duration="5314000" />
- </task>
- <task id="LOCAL-00211" summary="wait #4949 @2">
- <created>1638986018445</created>
- <option name="number" value="00211" />
- <option name="presentableId" value="LOCAL-00211" />
- <option name="project" value="LOCAL" />
- <updated>1638986018446</updated>
+ <workItem from="1646312529624" duration="5350000" />
+ <workItem from="1646330633143" duration="4094000" />
</task>
<task id="LOCAL-00212" summary="wait #4951 @3">
<created>1639054348287</created>
<option name="project" value="LOCAL" />
<updated>1646226301093</updated>
</task>
- <option name="localTasksCounter" value="260" />
+ <task id="LOCAL-00260" summary="wip #5041 @1.5">
+ <created>1646318060645</created>
+ <option name="number" value="00260" />
+ <option name="presentableId" value="LOCAL-00260" />
+ <option name="project" value="LOCAL" />
+ <updated>1646318060645</updated>
+ </task>
+ <option name="localTasksCounter" value="261" />
<servers />
</component>
<component name="TypeScriptGeneratedFilesManager">
<MESSAGE value="wait #5045 @0.5" />
<MESSAGE value="wip #5041 @3" />
<MESSAGE value="wip #5041" />
- <MESSAGE value="wip #5041 @1.5" />
<MESSAGE value="wip #5041 @0.5" />
<MESSAGE value="wip #5041 @1" />
<MESSAGE value="wip #5093 @2" />
<MESSAGE value="wait #5127" />
<MESSAGE value="wait #5127 @0.5" />
<MESSAGE value="wait #5127 @0.25" />
- <option name="LAST_COMMIT_MESSAGE" value="wait #5127 @0.25" />
+ <MESSAGE value="wip #5041 @1.5" />
+ <option name="LAST_COMMIT_MESSAGE" value="wip #5041 @1.5" />
</component>
<component name="XSLT-Support.FileAssociations.UIState">
<expand />
$owner = User::withoutGlobalScopes()->findOrFail($this->owner);
$organization = $owner->companyName;
+ $defaultModuleContent = ['content_title' => '', 'complete_when_opened' => false, 'mandatory' => true,
+ 'quiz_id' => '', 'fb_id' => '', 'audio_id' => '', 'pdf_id' => '', 'video_id' => ''];
+
$vdir = new VirtualDirectory($dest);
$modules = [];
foreach ($this->contents as $id => $content) {
- $m = $this->_compileModule($id, $content, $vdir, $user);
+ $m = $this->_compileModule($id, array_merge($defaultModuleContent, $content), $vdir, $user);
if ($m !== false) {
$modules[] = $m;
}
{
$basePath = 'packages/' . $id . '/';
+
$res = ['id' => $id, 'title' => trim($c['content_title']), 'type' => $c['content_type'], 'scorm' => !$c['complete_when_opened'], 'mandatory' => $c['mandatory'], 'path' => $basePath . 'index.html'];
if ($res['type'] === 'QZ' && $res['mandatory']) {
$res['lock'] = range(0, $id - 1);
$moduleTitle = $this->_compileFluidbook($c['fb_id'], $vdir, $basePath, $user);
break;
case 'QZ':
- $moduleTitle = $this->_compileQuiz($c['quiz_id'], $vdir, $basePath);
+ $moduleTitle = $this->_compileQuiz($c['quiz_id'], $c['quiz_file'], $vdir, $basePath);
break;
case 'PD':
$moduleTitle = $this->_compileMedia($c['audio_id'], $c['audio_file'], $vdir, $basePath);
return $meta->title;
}
- protected function _compileQuiz($id, $vdir, $basePath)
+ protected function _compileQuiz($id, $quiz_file, $vdir, $basePath)
+ {
+ if ($id) {
+ return $this->_compileQuizFromExisting($id, $vdir, $basePath);
+ }
+ return $this->_compileZip($quiz_file, $vdir, $basePath);
+ }
+
+ protected function _compileQuizFromExisting($id, $vdir, $basePath)
{
$tmp = Files::tmpdir();
$vdir->addTemp($tmp);
}
$spf = new \SplFileInfo($f->getPath());
Zip::extract($spf->getPathname(), $tmp);
+
+ if (file_exists($tmp . '/data.xml')) {
+ $sx = simplexml_load_file($tmp . '/data.xml');
+ $title = (string)$sx->title;
+ } else if (file_exists($tmp . '/data.xml')) {
+ $d = $this->_decodejs($tmp . '/data.xml');
+ if (isset($d['title'])) {
+ $title = $d['title'];
+ }
+ }
+
$vdir->copyDirectory($tmp, $basePath);
- return $spf->getBasename('.' . $spf->getExtension());
+ if (isset($title) && $title) {
+ return $title;
+ } else {
+ return $spf->getBasename('.' . $spf->getExtension());
+ }
}
- public function importZip($zip)
+ public function importPackageFromDirectory($dir)
{
- $tmp = Files::tmpdir();
- Zip::extract($zip, $tmp);
+
/** @var DirectoryIterator $it */
- $it = Files::getDirectoryIterator($tmp);
+ $it = Files::getDirectoryIterator($dir);
+ $contents = [];
foreach ($it as $sub) {
/** @var $sub DirectoryIterator */
if (!$sub->isDir()) {
continue;
}
- $this->importDirectory($sub);
+ $c = $this->importDirectory($sub);
+ if (null === $c || !is_array($c)) {
+ continue;
+ }
+ if (isset($c['content_type'])) {
+ $contents[] = $c;
+ } else {
+ $contents = array_merge($contents, $c);
+ }
}
-
- Files::rmdir($tmp);
+ usort($contents, function ($a, $b) {
+ if ($a['content_type'] == 'QZ') {
+ return 1;
+ }
+ return 0;
+ });
+ $this->setAttribute('contents', $contents);
}
/**
* @param $dir DirectoryIterator
- * @return void
+ * @return array|null
*/
protected function importDirectory($dir)
{
- switch (mb_strtolower($dir->getBasename())) {
- case 'fb':
- $this->importFluidbook($dir);
- break;
- case 'qz':
- $this->importQuiz($dir);
- break;
- case 'an':
- case 'ti5':
- case 'vi':
- case 'pd':
- case 'in':
- $this->importMedia($dir);
- break;
- case 'fc':
- $this->importScormContent($dir);
- break;
+ $type = $this->_normalizeType($dir->getBasename());
+ switch ($type) {
+ case 'FB':
+ return $this->importFluidbook($dir);
+ case 'QZ':
+ return $this->importQuiz($dir);
+ case 'AN':
+ case 'Ti5':
+ case 'VI':
+ case 'PD':
+ case 'IN':
+ return $this->importMedia($dir, $type);
+ case 'FC':
+ return $this->importScormContent($dir, $type, true);
default:
break;
}
}
+ protected function _normalizeType($type)
+ {
+ $type = mb_strtoupper($type);
+ if ($type === 'TI5') {
+ $type = 'Ti5';
+ }
+ return $type;
+ }
+
/**
* @param $dir DirectoryIterator
- * @return void
+ * @return array | null
*/
protected function importFluidbook($dir)
{
return;
}
$d = $this->_decodejs($datas);
- $this->contents[] = ['content_type' => 'FB', 'fb_id' => $d['id']];
+ return ['content_type' => 'FB', 'fb_id' => $d['id']];
}
protected function _decodejs($file, $associative = true)
{
- $c = file_get_contents($file);
+ $c = trim(file_get_contents($file), " \t\n\r\0\x0B;");
$offsetjson = strpos($c, '{');
return json_decode(substr($c, $offsetjson), $associative);
}
+ /**
+ * @param $dir DirectoryIterator
+ * @return array | null
+ */
protected function importQuiz($dir)
{
$data = $dir->getPathname() . "/data.js";
if (file_exists($data)) {
$d = $this->_decodejs($data);
- $this->contents[] = ['content_type' => 'QZ', 'quiz_id' => $d['id']];
- return;
+ return ['content_type' => 'QZ', 'quiz_id' => $d['id']];
}
// Old quiz with xml
+ return $this->importScormContent($dir, 'QZ');
}
- protected function importMedia($dir)
+ /**
+ * @param $dir DirectoryIterator
+ * @param $type string
+ * @return array|null
+ */
+ protected function importMedia($dir, $type)
{
-
+ $it = Files::getDirectoryIterator($dir->getPathname(), true);
+ $exts = ['mp3' => ['PD'], 'mp4' => ['AN', 'VI', 'Ti5'], 'pdf' => ['IN']];
+ $res = [];
+ foreach ($it as $item) {
+ /** @var $item DirectoryIterator */
+ if ($item->isDir()) {
+ continue;
+ }
+ $ext = $item->getExtension();
+ if (!isset($exts[$ext])) {
+ continue;
+ }
+ if (!in_array($type, $exts[$ext])) {
+ $type = $exts[$ext][0];
+ }
+ $r = $this->importFile($item->getPathname(), $type);
+ if (null !== $r) {
+ $res[] = $r;
+ }
+ }
+ return $res;
}
- protected function importScormContent($dir)
+ /**
+ * @param $dir DirectoryIterator
+ * @param $type string
+ * @param $complete_when_opened bool
+ * @return array|null
+ */
+ protected function importScormContent($dir, $type, $complete_when_opened = false)
{
+ $zip = Files::tempnam() . '.zip';
+ Zip::archive($dir, $zip);
+ return $this->importFile($zip, $type, $complete_when_opened);
+ }
+ /**
+ * @param $file string
+ * @param $type string
+ * @param $complete_when_opened bool
+ * @return array|null
+ * @throws \Spatie\MediaLibrary\MediaCollections\Exceptions\FileDoesNotExist
+ * @throws \Spatie\MediaLibrary\MediaCollections\Exceptions\FileIsTooBig
+ */
+ protected function importFile($file, $type, $complete_when_opened = false)
+ {
+ if (!file_exists($file)) {
+ return null;
+ }
+ $fieldnames = ['QZ' => 'quiz_file', 'FC' => 'content_file', 'AN' => 'video_file', 'Ti5' => 'video_file', 'PD' => 'audio_file', 'IN' => 'infographic_file'];
+ $fieldname = $fieldnames[$type] ?? 'content_file';
+ $collection = $this->_randCollectionName($fieldname);
+ $this->addMedia($file)->preservingOriginal()->toMediaCollection($collection);
+ return ['content_type' => $type, 'complete_when_opened' => $complete_when_opened, $fieldname => $collection];
}
}