]> _ Git - fluidbook-toolbox.git/commitdiff
wip #5041 @2
authorVincent Vanwaelscappel <vincent@cubedesigners.com>
Thu, 3 Mar 2022 19:13:36 +0000 (20:13 +0100)
committerVincent Vanwaelscappel <vincent@cubedesigners.com>
Thu, 3 Mar 2022 19:13:36 +0000 (20:13 +0100)
.idea/workspace.xml
app/Http/Controllers/Admin/Operations/ELearningPackage/ImportOperation.php
app/Models/ELearningPackage.php
app/SubForms/ElearningPackageContent.php

index 9f4d8c73ed0ad0f54eca50b64d327631a5d9be09..1cc6101f7b934969b4b7a3e637e7b1cd4f5e0b31 100644 (file)
     <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 />
index d6c8b401c8f7018379d2fac63922675d434e300e..1a3e811bba268b4999ee08c4c93fdff832b668ae 100644 (file)
@@ -5,6 +5,8 @@ namespace App\Http\Controllers\Admin\Operations\ELearningPackage;
 use App\Models\Asset;
 use App\Models\ELearningMedia;
 use App\Models\ELearningPackage;
+use Cubist\Util\Files\Files;
+use Cubist\Util\Zip;
 use Illuminate\Support\Facades\File;
 use Prologue\Alerts\Facades\Alert;
 use Symfony\Component\HttpFoundation\File\UploadedFile;
@@ -35,12 +37,16 @@ trait ImportOperation
                 continue;
             }
 
+            $tmp = Files::tmpdir();
+            Zip::extract($file->getPathname(), $tmp);
+
             $package = new ELearningPackage();
             $package->title = str_replace(['-', '_', '.'], ' ', implode(' ', $e));
             $package->owner = backpack_user()->id;
-            $package->importZip($file->getPathname());
+            $package->importPackageFromDirectory($tmp);
             $package->save();
 
+            Files::rmdir($tmp);
             unlink($file->getPathname());
 
             $importedFile++;
index 344b19e6850c745fefd9a3053723837bcac261e4..97ac5499302a717e4d0e526f0bf48d6bbea97872 100644 (file)
@@ -55,12 +55,15 @@ class ELearningPackage extends ToolboxModel
         $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;
             }
@@ -88,6 +91,7 @@ class ELearningPackage extends ToolboxModel
     {
         $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);
@@ -98,7 +102,7 @@ class ELearningPackage extends ToolboxModel
                 $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);
@@ -145,7 +149,15 @@ class ELearningPackage extends ToolboxModel
         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);
@@ -218,59 +230,93 @@ class ELearningPackage extends ToolboxModel
         }
         $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)
     {
@@ -279,34 +325,91 @@ class ELearningPackage extends ToolboxModel
             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];
     }
 }
index e93ae04834367af4370d44bd422e1ad7b16b5f3b..4f28abfe275968ae192ee81d5f6f64742eed492b 100644 (file)
@@ -43,6 +43,7 @@ class ElearningPackageContent extends SubForm
         $this->addField('audio_file', Files::class, __('Charger un audio'), ['when' => ['content_type' => 'PD', 'audio_id' => ''], 'acceptedFiles' => ['audio/mpeg']]);
 
         $this->addField('quiz_id', QuizID::class, __('Quiz'), ['when' => ['content_type' => 'QZ']]);
+        $this->addField('quiz_file', Files::class, __('Charger un quiz'), ['when' => ['content_type' => 'QZ', 'quiz_id' => ''], 'acceptedFiles' => ['.zip']]);
 
         $this->addField('pdf_id', ElearningMediaPdfID::class, __('PDF'), ['when' => ['content_type' => 'IN']]);
         $this->addField('infographic_file', Files::class, __('Charger une infographie'), ['when' => ['content_type' => ['IN']], 'acceptedFiles' => ['application/pdf']]);