]> _ Git - fluidbook-toolbox.git/commitdiff
wip #5319 @2
authorVincent Vanwaelscappel <vincent@cubedesigners.com>
Tue, 28 Jun 2022 14:26:45 +0000 (16:26 +0200)
committerVincent Vanwaelscappel <vincent@cubedesigners.com>
Tue, 28 Jun 2022 14:26:45 +0000 (16:26 +0200)
58 files changed:
app/Http/Controllers/Admin/Base/QuizController.php [deleted file]
app/Http/Controllers/Admin/CompanyCrudController.php
app/Http/Controllers/Admin/CrudController.php [new file with mode: 0644]
app/Http/Controllers/Admin/CubemailsCrudController.php [new file with mode: 0644]
app/Http/Controllers/Admin/ElearningMediaCrudController.php [new file with mode: 0644]
app/Http/Controllers/Admin/ElearningPackageCrudController.php [new file with mode: 0644]
app/Http/Controllers/Admin/FluidbookCollectionCrudController.php [new file with mode: 0644]
app/Http/Controllers/Admin/FluidbookIconsetCrudController.php [new file with mode: 0644]
app/Http/Controllers/Admin/FluidbookPublicationCrudController.php
app/Http/Controllers/Admin/FluidbookStatsController.php [new file with mode: 0644]
app/Http/Controllers/Admin/FluidbookThemeCrudController.php
app/Http/Controllers/Admin/FluidbookTranslateCrudController.php [new file with mode: 0644]
app/Http/Controllers/Admin/LocaleCrudController.php
app/Http/Controllers/Admin/Operations/DownloadOperation.php [deleted file]
app/Http/Controllers/Admin/Operations/ImportOperation.php [deleted file]
app/Http/Controllers/Admin/Operations/LogOperation.php [deleted file]
app/Http/Controllers/Admin/Operations/PreviewOperation.php [deleted file]
app/Http/Controllers/Admin/Operations/Quiz/DownloadOperation.php [new file with mode: 0644]
app/Http/Controllers/Admin/Operations/Quiz/ImportOperation.php [new file with mode: 0644]
app/Http/Controllers/Admin/Operations/Quiz/LogOperation.php [new file with mode: 0644]
app/Http/Controllers/Admin/Operations/Quiz/PreviewOperation.php [new file with mode: 0644]
app/Http/Controllers/Admin/Operations/Quiz/ReportOperation.php [new file with mode: 0644]
app/Http/Controllers/Admin/Operations/ReportOperation.php [deleted file]
app/Http/Controllers/Admin/PageCrudController.php [new file with mode: 0644]
app/Http/Controllers/Admin/QuizCrudController.php
app/Http/Controllers/Admin/QuizatttemptCrudController.php
app/Http/Controllers/Admin/QuiztranslationCrudController.php
app/Http/Controllers/Admin/SettingsCrudController.php
app/Http/Controllers/Admin/SignatureCrudController.php
app/Http/Controllers/Admin/ToolboxTranslateCrudController.php
app/Http/Controllers/Admin/UsersCrudController.php
app/Jobs/QuizDownload.php [new file with mode: 0644]
app/Models/ELearningPackage.php
app/Models/Quiz.php
resources/elearningpackage/js/app.js
resources/quiz/fonts/Roboto-Bold-webfont.woff [new file with mode: 0644]
resources/quiz/fonts/Roboto-Light-webfont.woff [new file with mode: 0644]
resources/quiz/fonts/Roboto-Regular-webfont.woff [new file with mode: 0644]
resources/quiz/fonts/RobotoCondensed-Bold-webfont.woff [new file with mode: 0644]
resources/quiz/fonts/RobotoCondensed-Light-webfont.woff [new file with mode: 0644]
resources/quiz/fonts/RobotoCondensed-Regular-webfont.woff [new file with mode: 0644]
resources/quiz/fonts/fontawesome-webfont.woff [new file with mode: 0644]
resources/quiz/fonts/fontawesome-webfont.woff2 [new file with mode: 0644]
resources/quiz/index.html
resources/quiz/js/app.js
resources/quiz/js/bootstrap.js [deleted file]
resources/quiz/js/scorm.js [deleted file]
resources/quiz/style/004-fonts.sass
resources/quiz/style/fonts/Roboto-Bold-webfont.woff [deleted file]
resources/quiz/style/fonts/Roboto-Light-webfont.woff [deleted file]
resources/quiz/style/fonts/Roboto-Regular-webfont.woff [deleted file]
resources/quiz/style/fonts/RobotoCondensed-Bold-webfont.woff [deleted file]
resources/quiz/style/fonts/RobotoCondensed-Light-webfont.woff [deleted file]
resources/quiz/style/fonts/RobotoCondensed-Regular-webfont.woff [deleted file]
resources/quiz/style/fonts/fontawesome-webfont.woff [deleted file]
resources/quiz/style/fonts/fontawesome-webfont.woff2 [deleted file]
resources/scorm/scorm.js [new file with mode: 0644]
resources/views/vendor/backpack/crud/buttons/quiz/download.blade.php

diff --git a/app/Http/Controllers/Admin/Base/QuizController.php b/app/Http/Controllers/Admin/Base/QuizController.php
deleted file mode 100644 (file)
index 4ee0969..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-<?php
-
-
-namespace App\Http\Controllers\Admin\Base;
-
-use App\Http\Controllers\Admin\Operations\DownloadOperation;
-use App\Http\Controllers\Admin\Operations\ImportOperation;
-use App\Http\Controllers\Admin\Operations\LogOperation;
-use App\Http\Controllers\Admin\Operations\PreviewOperation;
-use App\Http\Controllers\Admin\Operations\ReportOperation;
-use Cubist\Backpack\Magic\Controllers\CubistMagicController;
-
-class QuizController extends CubistMagicController
-{
-    use PreviewOperation;
-    use DownloadOperation;
-    use ImportOperation;
-    use LogOperation;
-    use ReportOperation;
-
-    function setupListOperation()
-    {
-        $this->crud->addClause('whereIn', 'owner', auth()->user()->getManagedUsers());
-    }
-
-}
index f397a4cd1d7d89531a84406e9ae47ce7a17583d6..10e66dd4449ba9522bf7f158cee7c0bc72e8c882 100644 (file)
@@ -5,6 +5,7 @@ namespace App\Http\Controllers\Admin;
 class CompanyCrudController extends \Cubist\Backpack\Magic\Controllers\CubistMagicController
 {
     use \Cubist\Backpack\Magic\Operations\CreateOperation;
+       use \Cubist\Backpack\Http\Controllers\Operations\CloneEditOperation;
        use \Cubist\Backpack\Magic\Operations\UpdateOperation;
        use \Cubist\Backpack\Http\Controllers\Operations\ReviseOperation;
        
diff --git a/app/Http/Controllers/Admin/CrudController.php b/app/Http/Controllers/Admin/CrudController.php
new file mode 100644 (file)
index 0000000..a5aba83
--- /dev/null
@@ -0,0 +1,27 @@
+<?php
+
+namespace App\Http\Controllers\Admin;
+
+class CrudController extends \Cubist\Backpack\Magic\Controllers\CubistMagicController
+{
+    use \Cubist\Backpack\Magic\Operations\CreateOperation;
+       use \Cubist\Backpack\Http\Controllers\Operations\CloneEditOperation;
+       use \Cubist\Backpack\Magic\Operations\UpdateOperation;
+       use \Cubist\Backpack\Http\Controllers\Operations\BulkPublishOperation;
+       use \Backpack\CRUD\app\Http\Controllers\Operations\CloneOperation;
+       use \Backpack\CRUD\app\Http\Controllers\Operations\BulkCloneOperation;
+       use \Backpack\CRUD\app\Http\Controllers\Operations\DeleteOperation;
+       use \Backpack\CRUD\app\Http\Controllers\Operations\BulkDeleteOperation;
+       use \Cubist\Backpack\Http\Controllers\Operations\ReviseOperation;
+       
+
+
+    /*
+       */
+
+    protected $_modelNamespace = 'App\Models\Base\ToolboxModel';
+    protected $_routeURL = '';
+    protected $_singular = '';
+    protected $_plural = '';
+    protected $_oneInstance= false;
+}
diff --git a/app/Http/Controllers/Admin/CubemailsCrudController.php b/app/Http/Controllers/Admin/CubemailsCrudController.php
new file mode 100644 (file)
index 0000000..ebc4671
--- /dev/null
@@ -0,0 +1,29 @@
+<?php
+
+namespace App\Http\Controllers\Admin;
+
+class CubemailsCrudController extends \Cubist\Backpack\Magic\Controllers\CubistMagicController
+{
+    use \Cubist\Backpack\Magic\Operations\CreateOperation;
+       use \Cubist\Backpack\Http\Controllers\Operations\CloneEditOperation;
+       use \Cubist\Backpack\Magic\Operations\UpdateOperation;
+       use \Cubist\Backpack\Http\Controllers\Operations\BulkPublishOperation;
+       use \Backpack\CRUD\app\Http\Controllers\Operations\CloneOperation;
+       use \Backpack\CRUD\app\Http\Controllers\Operations\BulkCloneOperation;
+       use \Backpack\CRUD\app\Http\Controllers\Operations\DeleteOperation;
+       use \Backpack\CRUD\app\Http\Controllers\Operations\BulkDeleteOperation;
+       use \Cubist\Backpack\Http\Controllers\Operations\ReviseOperation;
+       
+
+
+    /*
+       __('paramètre')
+       __('paramètres')
+       */
+
+    protected $_modelNamespace = 'App\Models\CubedesignersEmails';
+    protected $_routeURL = 'cubemails';
+    protected $_singular = 'paramètre';
+    protected $_plural = 'paramètres';
+    protected $_oneInstance= true;
+}
diff --git a/app/Http/Controllers/Admin/ElearningMediaCrudController.php b/app/Http/Controllers/Admin/ElearningMediaCrudController.php
new file mode 100644 (file)
index 0000000..069469a
--- /dev/null
@@ -0,0 +1,32 @@
+<?php
+
+namespace App\Http\Controllers\Admin;
+
+class ElearningMediaCrudController extends \Cubist\Backpack\Magic\Controllers\CubistMagicController
+{
+    use \App\Http\Controllers\Admin\Operations\ELearningMedia\ImportOperation;
+       use \App\Http\Controllers\Admin\Operations\ELearningMedia\PreviewOperation;
+       use \App\Http\Controllers\Admin\Operations\ELearningMedia\DownloadOperation;
+       use \Cubist\Backpack\Magic\Operations\CreateOperation;
+       use \Cubist\Backpack\Http\Controllers\Operations\CloneEditOperation;
+       use \Cubist\Backpack\Magic\Operations\UpdateOperation;
+       use \Cubist\Backpack\Http\Controllers\Operations\BulkPublishOperation;
+       use \Backpack\CRUD\app\Http\Controllers\Operations\CloneOperation;
+       use \Backpack\CRUD\app\Http\Controllers\Operations\BulkCloneOperation;
+       use \Backpack\CRUD\app\Http\Controllers\Operations\DeleteOperation;
+       use \Backpack\CRUD\app\Http\Controllers\Operations\BulkDeleteOperation;
+       use \Cubist\Backpack\Http\Controllers\Operations\ReviseOperation;
+       
+
+
+    /*
+       __('media')
+       __('media')
+       */
+
+    protected $_modelNamespace = 'App\Models\ELearningMedia';
+    protected $_routeURL = 'elearning-media';
+    protected $_singular = 'media';
+    protected $_plural = 'media';
+    protected $_oneInstance= false;
+}
diff --git a/app/Http/Controllers/Admin/ElearningPackageCrudController.php b/app/Http/Controllers/Admin/ElearningPackageCrudController.php
new file mode 100644 (file)
index 0000000..d7a6705
--- /dev/null
@@ -0,0 +1,32 @@
+<?php
+
+namespace App\Http\Controllers\Admin;
+
+class ElearningPackageCrudController extends \Cubist\Backpack\Magic\Controllers\CubistMagicController
+{
+    use \App\Http\Controllers\Admin\Operations\ELearningPackage\ImportOperation;
+       use \App\Http\Controllers\Admin\Operations\ELearningPackage\PreviewOperation;
+       use \App\Http\Controllers\Admin\Operations\ELearningPackage\DownloadOperation;
+       use \Cubist\Backpack\Magic\Operations\CreateOperation;
+       use \Cubist\Backpack\Http\Controllers\Operations\CloneEditOperation;
+       use \Cubist\Backpack\Magic\Operations\UpdateOperation;
+       use \Cubist\Backpack\Http\Controllers\Operations\BulkPublishOperation;
+       use \Backpack\CRUD\app\Http\Controllers\Operations\CloneOperation;
+       use \Backpack\CRUD\app\Http\Controllers\Operations\BulkCloneOperation;
+       use \Backpack\CRUD\app\Http\Controllers\Operations\DeleteOperation;
+       use \Backpack\CRUD\app\Http\Controllers\Operations\BulkDeleteOperation;
+       use \Cubist\Backpack\Http\Controllers\Operations\ReviseOperation;
+       
+
+
+    /*
+       __('package')
+       __('package')
+       */
+
+    protected $_modelNamespace = 'App\Models\ELearningPackage';
+    protected $_routeURL = 'elearning-package';
+    protected $_singular = 'package';
+    protected $_plural = 'package';
+    protected $_oneInstance= false;
+}
diff --git a/app/Http/Controllers/Admin/FluidbookCollectionCrudController.php b/app/Http/Controllers/Admin/FluidbookCollectionCrudController.php
new file mode 100644 (file)
index 0000000..69bc308
--- /dev/null
@@ -0,0 +1,30 @@
+<?php
+
+namespace App\Http\Controllers\Admin;
+
+class FluidbookCollectionCrudController extends \Cubist\Backpack\Magic\Controllers\CubistMagicController
+{
+    use \App\Http\Controllers\Admin\Operations\FluidbookCollection\DownloadOperation;
+       use \Cubist\Backpack\Magic\Operations\CreateOperation;
+       use \Cubist\Backpack\Http\Controllers\Operations\CloneEditOperation;
+       use \Cubist\Backpack\Magic\Operations\UpdateOperation;
+       use \Cubist\Backpack\Http\Controllers\Operations\BulkPublishOperation;
+       use \Backpack\CRUD\app\Http\Controllers\Operations\CloneOperation;
+       use \Backpack\CRUD\app\Http\Controllers\Operations\BulkCloneOperation;
+       use \Backpack\CRUD\app\Http\Controllers\Operations\DeleteOperation;
+       use \Backpack\CRUD\app\Http\Controllers\Operations\BulkDeleteOperation;
+       use \Cubist\Backpack\Http\Controllers\Operations\ReviseOperation;
+       
+
+
+    /*
+       __('collection')
+       __('collections')
+       */
+
+    protected $_modelNamespace = 'App\Models\FluidbookCollection';
+    protected $_routeURL = 'fluidbook-collection';
+    protected $_singular = 'collection';
+    protected $_plural = 'collections';
+    protected $_oneInstance= false;
+}
diff --git a/app/Http/Controllers/Admin/FluidbookIconsetCrudController.php b/app/Http/Controllers/Admin/FluidbookIconsetCrudController.php
new file mode 100644 (file)
index 0000000..464f000
--- /dev/null
@@ -0,0 +1,29 @@
+<?php
+
+namespace App\Http\Controllers\Admin;
+
+class FluidbookIconsetCrudController extends \Cubist\Backpack\Magic\Controllers\CubistMagicController
+{
+    use \Cubist\Backpack\Magic\Operations\CreateOperation;
+       use \Cubist\Backpack\Http\Controllers\Operations\CloneEditOperation;
+       use \Cubist\Backpack\Magic\Operations\UpdateOperation;
+       use \Cubist\Backpack\Http\Controllers\Operations\BulkPublishOperation;
+       use \Backpack\CRUD\app\Http\Controllers\Operations\CloneOperation;
+       use \Backpack\CRUD\app\Http\Controllers\Operations\BulkCloneOperation;
+       use \Backpack\CRUD\app\Http\Controllers\Operations\DeleteOperation;
+       use \Backpack\CRUD\app\Http\Controllers\Operations\BulkDeleteOperation;
+       use \Cubist\Backpack\Http\Controllers\Operations\ReviseOperation;
+       
+
+
+    /*
+       __('icon set')
+       __('icon sets')
+       */
+
+    protected $_modelNamespace = 'App\Models\FluidbookIconset';
+    protected $_routeURL = 'fluidbook-iconset';
+    protected $_singular = 'icon set';
+    protected $_plural = 'icon sets';
+    protected $_oneInstance= false;
+}
index a3cd2010d7a80888338c708478c3df8d8f70e669..a09d1efa06f67d77cafc784409b1484bcdc10f43 100644 (file)
@@ -5,6 +5,7 @@ namespace App\Http\Controllers\Admin;
 class FluidbookPublicationCrudController extends \Cubist\Backpack\Magic\Controllers\CubistMagicController
 {
     use \Cubist\Backpack\Magic\Operations\CreateOperation;
+       use \Cubist\Backpack\Http\Controllers\Operations\CloneEditOperation;
        use \Cubist\Backpack\Magic\Operations\UpdateOperation;
        use \Backpack\CRUD\app\Http\Controllers\Operations\CloneOperation;
        use \Backpack\CRUD\app\Http\Controllers\Operations\DeleteOperation;
diff --git a/app/Http/Controllers/Admin/FluidbookStatsController.php b/app/Http/Controllers/Admin/FluidbookStatsController.php
new file mode 100644 (file)
index 0000000..ab77b39
--- /dev/null
@@ -0,0 +1,159 @@
+<?php
+
+namespace App\Http\Controllers\Admin;
+
+use App\Http\Controllers\Controller;
+use App\Models\FluidbookPublication;
+use Backpack\CRUD\app\Library\Widget;
+use Carbon\Carbon;
+use Chartisan\PHP\Chartisan;
+use Cubist\Matomo\Reporting;
+
+class FluidbookStatsController extends Controller
+{
+
+    private function _getReporting($fluidbook_id)
+    {
+        // Get the appropriate server / API token based on the Fluidbook ID
+        // Stats are split across different servers depending on the ID:
+        // ID < 21210 = stats3.fluidbook.com
+        // ID >= 21210 (even numbers) = stats4.fluidbook.com
+        // ID >= 21211 (odd numbers) = stats5.fluidbook.com
+
+        // Each stats server has a different instance of Matamo, so we need to provide different API tokens for each
+        // Normally this information would be stored in the .env but there's no good way to do that with an array, so
+        // it is simpler to keep it here. These are also stored in the shared Bitwarden entry for Matomo.
+        $matomo_tokens = [
+            'stats3.fluidbook.com' => '9df722a0bd30878ddc4d737352427502',
+            'stats4.fluidbook.com' => '3ffdbe052ae625f065573df9fa9515df',
+            'stats5.fluidbook.com' => '85e9cc307b6e5083249949e9472a80b8',
+        ];
+
+        $fluidbook_id = intval($fluidbook_id);
+
+        if ($fluidbook_id < 21210) {
+            $server = 'stats3.fluidbook.com';
+        } elseif ($fluidbook_id >= 21210 && $fluidbook_id % 2 === 0) {
+            $server = 'stats4.fluidbook.com';
+        } else {
+            $server = 'stats5.fluidbook.com';
+        }
+
+        //dump("Server is $server");
+
+        return new Reporting("https://{$server}/", $matomo_tokens[$server]);
+    }
+
+
+    private function _parseDate($date) {
+        // Match possible date strings:
+        // - YYYY
+        // - YYYY-MM
+        // - YYYY-MM-DD
+        // - YYYY-MM-DD,YYYY-MM-DD
+        // https://regex101.com/r/BLrqm0/1
+        $regex = '/^(?<start_date>(?<start_year>2\d{3})-?(?<start_month>0[1-9]|1[012])?-?(?<start_day>0[1-9]|[12][0-9]|3[01])?),?(?<end_date>2\d{3}-(?>0[1-9]|1[012])-(?>0[1-9]|[12][0-9]|3[01]))?/';
+
+        preg_match($regex, $date, $date_matches);
+
+        return $date_matches;
+    }
+
+    protected function summary($fluidbook_id, $date = null)
+    {
+        $dates = $date ? $this->_parseDate($date) : false;
+
+        $fluidbook = FluidbookPublication::findOrFail($fluidbook_id);
+
+
+        // TODO: year(s)? view like the old version: https://workshop.fluidbook.com/stats/10003_ab3cacc39ebbf2478e0931629d114e74
+        // Need to calculate all the available dates, probably based on creation date of the Fluidbook
+
+        // TODO: month view, breakdown of individual day stats: https://workshop.fluidbook.com/stats/10003_ab3cacc39ebbf2478e0931629d114e74/2017/10
+        // These would be linked from the "Year(s)" view above...
+
+        // Matomo API
+        // We need to pass it a date (eg. "2022-01-01") or date range (eg. "2022-03-01,2022-05-15")
+        // We can then specify the granularity of stats by specifying the period:
+        // - range = aggregated summary of stats for the specified date range
+        // - year = summary of stats broken down by year(s)
+        // - month = summary of stats broken down by month(s)
+        // - day = summary of stats broken down by day(s)
+
+        // Which mode are we in?
+        if (isset($dates['start_date']) && isset($dates['end_date'])) { // Custom date range
+            $mode = 'range';
+            $date_range = "{$dates['start_date']},{$dates['end_date']}";
+            $period = 'day'; // Segregate stats by day
+
+        } elseif (isset($dates['start_year']) && isset($dates['start_month'])) { // Month view
+            $mode = 'month';
+            $month = $dates['start_month'];
+            $year = $dates['start_year'];
+            $last_day_of_month = cal_days_in_month(CAL_GREGORIAN, $month, $year);
+            $date_range = "{$year}-{$month}-01,{$year}-{$month}-{$last_day_of_month}";
+            $period = 'day'; // Segregate stats by day
+
+        } elseif (isset($dates['start_year'])) {
+            $mode = 'year';
+            $year = $dates['start_year'];
+            $end_date = $year == date('Y') ? date('Y-m-d') : "{$year}-12-31"; // If it's the current year, don't get future dates
+            $date_range = "{$year}-01-01,{$end_date}"; // Full range of specified year
+            $period = 'month'; // Segregate stats by month
+
+        } else { // No valid dates specified, display the full data set
+            $mode = 'overview';
+            $start_date = $fluidbook->created_at->isoFormat('YYYY-MM-DD');
+            $date_range = $start_date . ',' . date('Y-m-d');
+            $period = 'month'; // Segregate stats by month
+        }
+
+        // TODO: support the ability to specify a date range from a date-picker and also maybe choose the breakdown (by year/month/day/range)
+
+        $report = $this->_getReporting($fluidbook_id);
+
+        echo "Getting stats for date range $date_range, segregated by $period";
+        //dump(collect($report->getVisits($fluidbook_id, $date_range, $period))->sum('nb_visits'));
+        //dump($report->getVisits($fluidbook_id, $date_range, 'range'));
+        // dd($report->getVisits($fluidbook_id, $date_range, $period));
+        $visits = collect($report->getVisits($fluidbook_id, $date_range, $period));
+        $pageviews = collect($report->getPageViews($fluidbook_id, $date_range, $period));
+
+        // Get the search keywords as a range because we don't need to display them by date
+        $searches = collect($report->getSearchKeywords($fluidbook_id, $date_range, 'range'));
+
+        // Format dates for display as labels on the x-axis
+        $labels = $visits->keys()->map(function($label, $index) use ($period) {
+            return match ($period) {
+                'day' => Carbon::parse($label)->isoFormat('DD'), // Convert YYYY-MM-DD string from API into zero-padded day alone
+                'month' => Carbon::parse($label)->isoFormat('MMM'), // Convert to abbreviated month name
+                default => $label,
+            };
+        })->toArray();
+
+        // Format dates for display in the tooltip title
+        $formatted_dates = $visits->keys()->map(function($label, $index) use ($period) {
+            return match ($period) {
+                'day' => Carbon::parse($label)->isoFormat('dddd Do MMMM YYYY'),
+                'month' => Carbon::parse($label)->isoFormat('MMMM YYYY'),
+                default => $label,
+            };
+        })->toArray();
+
+        $chart = Chartisan::build()
+            ->labels($labels)
+            ->extra(['tooltip_labels' => array_combine($labels, $formatted_dates)])
+            ->dataset('Visits', $visits->pluck('nb_visits')->toArray())
+            ->dataset('Page Views', $pageviews->pluck('nb_pageviews')->toArray())
+            ->toJSON();
+
+        // header('Content-Type: application/json; charset=utf-8');
+        // return $chart;
+
+        return view('fluidbook_stats.summary', compact('fluidbook', 'visits', 'pageviews', 'searches', 'chart', 'mode', 'period', 'dates'));
+
+
+    }
+
+
+}
index d7ef37e174d8757393189cbaa4764575244a880f..9acbdce5d46228a20d506f40b5b665e8f4c46bb8 100644 (file)
@@ -5,6 +5,7 @@ namespace App\Http\Controllers\Admin;
 class FluidbookThemeCrudController extends \Cubist\Backpack\Magic\Controllers\CubistMagicController
 {
     use \Cubist\Backpack\Magic\Operations\CreateOperation;
+       use \Cubist\Backpack\Http\Controllers\Operations\CloneEditOperation;
        use \Cubist\Backpack\Magic\Operations\UpdateOperation;
        use \Cubist\Backpack\Http\Controllers\Operations\BulkPublishOperation;
        use \Backpack\CRUD\app\Http\Controllers\Operations\CloneOperation;
diff --git a/app/Http/Controllers/Admin/FluidbookTranslateCrudController.php b/app/Http/Controllers/Admin/FluidbookTranslateCrudController.php
new file mode 100644 (file)
index 0000000..ca308fe
--- /dev/null
@@ -0,0 +1,30 @@
+<?php
+
+namespace App\Http\Controllers\Admin;
+
+class FluidbookTranslateCrudController extends \Cubist\Backpack\Magic\Controllers\CubistMagicController
+{
+    use \App\Http\Controllers\Admin\Operations\FluidbookTranslate\ExcelExportOperation;
+       use \App\Http\Controllers\Admin\Operations\FluidbookTranslate\ExcelImportOperation;
+       use \Cubist\Backpack\Magic\Operations\CreateOperation;
+       use \Cubist\Backpack\Http\Controllers\Operations\CloneEditOperation;
+       use \Cubist\Backpack\Magic\Operations\UpdateOperation;
+       use \Cubist\Backpack\Http\Controllers\Operations\BulkPublishOperation;
+       use \Backpack\CRUD\app\Http\Controllers\Operations\CloneOperation;
+       use \Backpack\CRUD\app\Http\Controllers\Operations\BulkCloneOperation;
+       use \Backpack\CRUD\app\Http\Controllers\Operations\DeleteOperation;
+       use \Backpack\CRUD\app\Http\Controllers\Operations\BulkDeleteOperation;
+       
+
+
+    /*
+       __('traduction')
+       __('traductions')
+       */
+
+    protected $_modelNamespace = 'App\Models\FluidbookTranslate';
+    protected $_routeURL = 'fluidbook-translate';
+    protected $_singular = 'traduction';
+    protected $_plural = 'traductions';
+    protected $_oneInstance= true;
+}
index 77182f157aa871e60980fe0c2661611c2eb6d76f..05607edd1fdf0baf2364367dfd7503b32a036395 100644 (file)
@@ -5,6 +5,7 @@ namespace App\Http\Controllers\Admin;
 class LocaleCrudController extends \Cubist\Backpack\Magic\Controllers\CubistMagicController
 {
     use \Cubist\Backpack\Magic\Operations\CreateOperation;
+       use \Cubist\Backpack\Http\Controllers\Operations\CloneEditOperation;
        use \Cubist\Backpack\Magic\Operations\UpdateOperation;
        use \Cubist\Backpack\Http\Controllers\Operations\BulkPublishOperation;
        use \Backpack\CRUD\app\Http\Controllers\Operations\CloneOperation;
diff --git a/app/Http/Controllers/Admin/Operations/DownloadOperation.php b/app/Http/Controllers/Admin/Operations/DownloadOperation.php
deleted file mode 100644 (file)
index c55612c..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-<?php
-
-namespace App\Http\Controllers\Admin\Operations;
-
-use App\Models\QuizTranslation;
-use Cubist\Util\Files\Files;
-use Cubist\Util\Zip;
-use Illuminate\Support\Facades\Route;
-use Cubist\Util\Str;
-
-trait DownloadOperation
-{
-    protected function setupDownloadRoutes($segment, $routeName, $controller)
-    {
-        Route::match(['get'], $segment . '/{id}/download', $controller . '@download');
-    }
-
-    protected function setupDownloadDefaults()
-    {
-        $this->crud->addButtonFromView('line', 'download', 'quiz.download', 'end');
-    }
-
-    protected function download($id)
-    {
-        $compilepath = protected_path('quiz/final/' . $id);
-        $entry = $this->crud->getEntry($id);
-        $entry->compile($compilepath);
-
-        $translation = QuizTranslation::find($entry->getAttribute('translation'));
-        $fname = Str::slugCase($entry->getAttribute('client') . ' ' . $entry->getAttribute('project') . ' ' . date_format($entry->getAttribute('updated_at'), 'Ymd') . ' ' . $translation->locale . ' ' . $id) . '.zip';
-        $dest = protected_path('quiz/download/' . $fname);
-
-        Zip::archive($compilepath, $dest);
-
-        return response(null)->header('Content-Type', 'application/zip')
-            ->header('Content-Disposition', 'attachment; filename="' . $fname . '"')
-            ->header('X-Sendfile', $dest);
-    }
-}
diff --git a/app/Http/Controllers/Admin/Operations/ImportOperation.php b/app/Http/Controllers/Admin/Operations/ImportOperation.php
deleted file mode 100644 (file)
index 1ba768b..0000000
+++ /dev/null
@@ -1,164 +0,0 @@
-<?php
-
-namespace App\Http\Controllers\Admin\Operations;
-
-
-use App\Models\Quiz;
-use App\Models\QuizTranslation;
-use Cubist\Util\Files\Files;
-use Illuminate\Support\Facades\Route;
-use Prologue\Alerts\Facades\Alert;
-use ZipArchive;
-
-trait ImportOperation
-{
-    protected function setupImportRoutes($segment, $routeName, $controller)
-    {
-        Route::match(['post'], $segment . '/import', $controller . '@import');
-    }
-
-    protected function setupImportDefaults()
-    {
-        $this->crud->addButtonFromView('top', 'import', 'quiz.import', 'end');
-    }
-
-    protected function import()
-    {
-        $files = $_FILES['file']['tmp_name'];
-
-        if (!count($files)) {
-            Alert::warning('No file were imported')->flash();
-            return;
-        }
-
-        $default = ['title' => '',
-            'translation' => '1',
-            'scorm' => '1',
-            'review' => 'always',
-            'instantReview' => '1',
-            'threshold' => '0',
-            'owner' => auth()->user()->id];
-
-        foreach (Quiz::getColors() as $name => $color) {
-            $default[$name] = $color['default'];
-        }
-
-        foreach (Quiz::getMessages() as $name => $message) {
-            $default[$name] = '';
-        }
-
-
-        $j = 0;
-        foreach ($files as $file) {
-            $z = new ZipArchive();
-            $ok = $z->open($file);
-            if ($ok !== true) {
-                Alert::warning('Unable to open ' . $file)->flash();
-                continue;
-            }
-
-
-            $data = [];
-            $datacontent = $z->getFromName('data.xml');
-            if (!$datacontent || stripos($datacontent, '<quiz') === false) {
-                Alert::warning($file . ' doesn\'t seem to be a valid quiz')->flash();
-                continue;
-            }
-            $x = simplexml_load_string($datacontent, "SimpleXMLElement", LIBXML_NOERROR | LIBXML_ERR_NONE);
-
-            // Discover translation
-            $validatetrans = $x->xpath('/quiz/translations/validateAnswer');
-            if ($validatetrans) {
-                $validatetrans = (string)$validatetrans[0];
-                $translation = QuizTranslation::where('validateAnswer', '=', $validatetrans)->first();
-                if ($translation) {
-                    $data['translation'] = $translation->id;
-                }
-            }
-            // Handle message from XML
-            foreach (Quiz::getMessages() as $name => $message) {
-                $e = $x->xpath('/quiz/' . $name);
-                if (!$e) {
-                    continue;
-                }
-                $m = (string)$e[0];
-                // We only define the message if different from the translation default
-                if (isset($translation) && $translation->$name !== $m) {
-                    $data[$name] = $m;
-                }
-            }
-
-            // Handle other attributes from XML
-            $attributes = ['title', 'review', 'thresehold'];
-            foreach (Quiz::getColors() as $name => $color) {
-                $attributes[] = $name;
-            }
-            foreach (Quiz::getActions() as $name => $action) {
-                $attributes[] = $name;
-            }
-            foreach ($attributes as $attribute => $xpath) {
-                if (is_int($attribute)) {
-                    $attribute = $xpath;
-                    $xpath = '/quiz/' . $xpath;
-                }
-                $e = $x->xpath($xpath);
-                if (!$e) {
-                    continue;
-                }
-                $data[$attribute] = (string)$e[0];
-            }
-
-            // Handle Questions
-            $xquestions = $x->xpath('/quiz/questions/question');
-            $questions = [];
-            foreach ($xquestions as $xquestion) {
-                $q = [
-                    'type' => 'multiple',
-                    'count_for_score' => true,
-                    'report_label' => '',
-                    'placeholder' => '',
-                    'min_score' => 0,
-                    'question' => (string)$xquestion->label,
-                    'explaination' => (string)$xquestion->correction,
-                    'multiple' => isset($xquestion['multiple']) ? (bool)$xquestion['multiple'] : false,
-                    'answers' => [],
-                ];
-                foreach ($xquestion->answers[0]->answer as $xanswer) {
-                    $q['answers'][] = [
-                        'answer' => (string)$xanswer,
-                        'correct' => isset($xanswer['correct']) ? (bool)$xanswer['correct'] : false,
-                    ];
-                }
-                $questions[] = $q;
-
-            }
-            $data['questions'] = $questions;
-            $data = array_merge($default, $data);
-
-            /** @var Quiz $q */
-            $q = new Quiz();
-            $q = $q->create($data);
-
-            $temp = Files::tmpdir();
-            $assets = ['logo' => 'logo.png', 'banner' => 'banner.jpg'];
-            foreach ($assets as $field => $asset) {
-                $f = $temp . '/' . $asset;
-                $c = $z->getFromName('assets/' . $asset);
-                if ($c) {
-                    file_put_contents($f, $c);
-                    $q->addMediaToField($field, $f);
-                }
-            }
-            $z->close();
-            $j++;
-        }
-
-
-        if ($j === 0) {
-            Alert::warning('No quiz were imported')->flash();
-        } else {
-            Alert::success('<b>' . $j . ' quiz(zes)</b> were imported')->flash();
-        }
-        return redirect($this->crud->route);
-    }
-}
diff --git a/app/Http/Controllers/Admin/Operations/LogOperation.php b/app/Http/Controllers/Admin/Operations/LogOperation.php
deleted file mode 100644 (file)
index 89d5209..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-<?php
-
-
-namespace App\Http\Controllers\Admin\Operations;
-
-use App\Http\Middleware\Authenticate;
-use App\Http\Middleware\CheckIfAdmin;
-use App\Http\Middleware\VerifyCsrfToken;
-use App\Models\QuizAttempt;
-use DateTime;
-use Illuminate\Http\Request;
-use Illuminate\Support\Facades\Route;
-
-trait LogOperation
-{
-    protected function setupLogRoutes($segment, $routeName, $controller)
-    {
-        Route::match(['post','get'], $segment . '/{id}/log', $controller . '@log')->withoutMiddleware([VerifyCsrfToken::class, Authenticate::class, CheckIfAdmin::class]);
-    }
-
-    protected function log($id)
-    {
-        $request = request();
-
-        $log = new QuizAttempt();
-        $log->quiz = $id;
-        $log->score = $request->get('score');
-        $log->passed = $request->get('passed') !== 'false' ? '1' : '0';
-        $log->answers = json_encode($request->get('questions'));
-        $log->save();
-
-        return response()->json(['ok' => true])->header('Access-Control-Allow-Origin', '*');
-    }
-}
diff --git a/app/Http/Controllers/Admin/Operations/PreviewOperation.php b/app/Http/Controllers/Admin/Operations/PreviewOperation.php
deleted file mode 100644 (file)
index dab52cf..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-<?php
-
-namespace App\Http\Controllers\Admin\Operations;
-
-use Cubist\Util\Files\Files;
-use Illuminate\Support\Facades\Route;
-
-trait PreviewOperation
-{
-    protected function setupPreviewRoutes($segment, $routeName, $controller)
-    {
-        Route::match(['get'], $segment . '/{id}/preview/{path?}', $controller . '@preview')
-            ->where(['id' => '[0-9]+', 'path' => '.*']);
-    }
-
-    protected function setupPreviewDefaults()
-    {
-        $this->crud->addButtonFromView('line', 'open_preview', 'quiz.preview', 'begining');
-    }
-
-    protected function preview($id, $path = 'index.html')
-    {
-        $dest = protected_path('quiz/final/' . $id);
-
-        if ($path === 'index.html') {
-            $entry = $this->crud->getEntry($id);
-            $entry->compile($dest);
-        }
-
-        $p = $dest . '/' . $path;
-        return response(null)->header('Content-Type', Files::_getMimeType($p))->header('X-Sendfile', $p);
-    }
-}
diff --git a/app/Http/Controllers/Admin/Operations/Quiz/DownloadOperation.php b/app/Http/Controllers/Admin/Operations/Quiz/DownloadOperation.php
new file mode 100644 (file)
index 0000000..ee798de
--- /dev/null
@@ -0,0 +1,29 @@
+<?php
+
+namespace App\Http\Controllers\Admin\Operations\Quiz;
+
+use App\Jobs\QuizDownload;
+use App\Models\Quiz;
+use Illuminate\Support\Facades\Route;
+use Prologue\Alerts\Facades\Alert;
+
+trait DownloadOperation
+{
+    protected function setupDownloadRoutes($segment, $routeName, $controller)
+    {
+        Route::match(['get'], $segment . '/{id}/download/{action}', $controller . '@download');
+    }
+
+    protected function setupDownloadDefaults()
+    {
+
+        $this->crud->addButtonFromView('line', 'download', 'quiz.download', 'end');
+    }
+
+    protected function download($id, $action)
+    {
+        QuizDownload::dispatch(Quiz::find($id), $action, backpack_user())->onQueue('download');;
+        Alert::add('success', __('La compilation a été placée en file d\'attente. Vous recevrez un email lorsqu\'elle sera terminée.'))->flash();
+        return redirect(backpack_url('quiz'));
+    }
+}
diff --git a/app/Http/Controllers/Admin/Operations/Quiz/ImportOperation.php b/app/Http/Controllers/Admin/Operations/Quiz/ImportOperation.php
new file mode 100644 (file)
index 0000000..b476ccb
--- /dev/null
@@ -0,0 +1,164 @@
+<?php
+
+namespace App\Http\Controllers\Admin\Operations\Quiz;
+
+
+use App\Models\Quiz;
+use App\Models\QuizTranslation;
+use Cubist\Util\Files\Files;
+use Illuminate\Support\Facades\Route;
+use Prologue\Alerts\Facades\Alert;
+use ZipArchive;
+
+trait ImportOperation
+{
+    protected function setupImportRoutes($segment, $routeName, $controller)
+    {
+        Route::match(['post'], $segment . '/import', $controller . '@import');
+    }
+
+    protected function setupImportDefaults()
+    {
+        $this->crud->addButtonFromView('top', 'import', 'quiz.import', 'end');
+    }
+
+    protected function import()
+    {
+        $files = $_FILES['file']['tmp_name'];
+
+        if (!count($files)) {
+            Alert::warning('No file were imported')->flash();
+            return;
+        }
+
+        $default = ['title' => '',
+            'translation' => '1',
+            'scorm' => '1',
+            'review' => 'always',
+            'instantReview' => '1',
+            'threshold' => '0',
+            'owner' => auth()->user()->id];
+
+        foreach (Quiz::getColors() as $name => $color) {
+            $default[$name] = $color['default'];
+        }
+
+        foreach (Quiz::getMessages() as $name => $message) {
+            $default[$name] = '';
+        }
+
+
+        $j = 0;
+        foreach ($files as $file) {
+            $z = new ZipArchive();
+            $ok = $z->open($file);
+            if ($ok !== true) {
+                Alert::warning('Unable to open ' . $file)->flash();
+                continue;
+            }
+
+
+            $data = [];
+            $datacontent = $z->getFromName('data.xml');
+            if (!$datacontent || stripos($datacontent, '<quiz') === false) {
+                Alert::warning($file . ' doesn\'t seem to be a valid quiz')->flash();
+                continue;
+            }
+            $x = simplexml_load_string($datacontent, "SimpleXMLElement", LIBXML_NOERROR | LIBXML_ERR_NONE);
+
+            // Discover translation
+            $validatetrans = $x->xpath('/quiz/translations/validateAnswer');
+            if ($validatetrans) {
+                $validatetrans = (string)$validatetrans[0];
+                $translation = QuizTranslation::where('validateAnswer', '=', $validatetrans)->first();
+                if ($translation) {
+                    $data['translation'] = $translation->id;
+                }
+            }
+            // Handle message from XML
+            foreach (Quiz::getMessages() as $name => $message) {
+                $e = $x->xpath('/quiz/' . $name);
+                if (!$e) {
+                    continue;
+                }
+                $m = (string)$e[0];
+                // We only define the message if different from the translation default
+                if (isset($translation) && $translation->$name !== $m) {
+                    $data[$name] = $m;
+                }
+            }
+
+            // Handle other attributes from XML
+            $attributes = ['title', 'review', 'thresehold'];
+            foreach (Quiz::getColors() as $name => $color) {
+                $attributes[] = $name;
+            }
+            foreach (Quiz::getActions() as $name => $action) {
+                $attributes[] = $name;
+            }
+            foreach ($attributes as $attribute => $xpath) {
+                if (is_int($attribute)) {
+                    $attribute = $xpath;
+                    $xpath = '/quiz/' . $xpath;
+                }
+                $e = $x->xpath($xpath);
+                if (!$e) {
+                    continue;
+                }
+                $data[$attribute] = (string)$e[0];
+            }
+
+            // Handle Questions
+            $xquestions = $x->xpath('/quiz/questions/question');
+            $questions = [];
+            foreach ($xquestions as $xquestion) {
+                $q = [
+                    'type' => 'multiple',
+                    'count_for_score' => true,
+                    'report_label' => '',
+                    'placeholder' => '',
+                    'min_score' => 0,
+                    'question' => (string)$xquestion->label,
+                    'explaination' => (string)$xquestion->correction,
+                    'multiple' => isset($xquestion['multiple']) ? (bool)$xquestion['multiple'] : false,
+                    'answers' => [],
+                ];
+                foreach ($xquestion->answers[0]->answer as $xanswer) {
+                    $q['answers'][] = [
+                        'answer' => (string)$xanswer,
+                        'correct' => isset($xanswer['correct']) ? (bool)$xanswer['correct'] : false,
+                    ];
+                }
+                $questions[] = $q;
+
+            }
+            $data['questions'] = $questions;
+            $data = array_merge($default, $data);
+
+            /** @var Quiz $q */
+            $q = new Quiz();
+            $q = $q->create($data);
+
+            $temp = Files::tmpdir();
+            $assets = ['logo' => 'logo.png', 'banner' => 'banner.jpg'];
+            foreach ($assets as $field => $asset) {
+                $f = $temp . '/' . $asset;
+                $c = $z->getFromName('assets/' . $asset);
+                if ($c) {
+                    file_put_contents($f, $c);
+                    $q->addMediaToField($field, $f);
+                }
+            }
+            $z->close();
+            $j++;
+        }
+
+
+        if ($j === 0) {
+            Alert::warning('No quiz were imported')->flash();
+        } else {
+            Alert::success('<b>' . $j . ' quiz(zes)</b> were imported')->flash();
+        }
+        return redirect($this->crud->route);
+    }
+}
diff --git a/app/Http/Controllers/Admin/Operations/Quiz/LogOperation.php b/app/Http/Controllers/Admin/Operations/Quiz/LogOperation.php
new file mode 100644 (file)
index 0000000..fba1210
--- /dev/null
@@ -0,0 +1,32 @@
+<?php
+
+
+namespace App\Http\Controllers\Admin\Operations\Quiz;
+
+use App\Http\Middleware\Authenticate;
+use App\Http\Middleware\CheckIfAdmin;
+use App\Http\Middleware\VerifyCsrfToken;
+use App\Models\QuizAttempt;
+use Illuminate\Support\Facades\Route;
+
+trait LogOperation
+{
+    protected function setupLogRoutes($segment, $routeName, $controller)
+    {
+        Route::match(['post','get'], $segment . '/{id}/log', $controller . '@log')->withoutMiddleware([VerifyCsrfToken::class, Authenticate::class, CheckIfAdmin::class]);
+    }
+
+    protected function log($id)
+    {
+        $request = request();
+
+        $log = new QuizAttempt();
+        $log->quiz = $id;
+        $log->score = $request->get('score');
+        $log->passed = $request->get('passed') !== 'false' ? '1' : '0';
+        $log->answers = json_encode($request->get('questions'));
+        $log->save();
+
+        return response()->json(['ok' => true])->header('Access-Control-Allow-Origin', '*');
+    }
+}
diff --git a/app/Http/Controllers/Admin/Operations/Quiz/PreviewOperation.php b/app/Http/Controllers/Admin/Operations/Quiz/PreviewOperation.php
new file mode 100644 (file)
index 0000000..72fef6f
--- /dev/null
@@ -0,0 +1,33 @@
+<?php
+
+namespace App\Http\Controllers\Admin\Operations\Quiz;
+
+use Cubist\Util\Files\Files;
+use Illuminate\Support\Facades\Route;
+
+trait PreviewOperation
+{
+    protected function setupPreviewRoutes($segment, $routeName, $controller)
+    {
+        Route::match(['get'], $segment . '/{id}/preview/{path?}', $controller . '@preview')
+            ->where(['id' => '[0-9]+', 'path' => '.*']);
+    }
+
+    protected function setupPreviewDefaults()
+    {
+        $this->crud->addButtonFromView('line', 'open_preview', 'quiz.preview', 'begining');
+    }
+
+    protected function preview($id, $path = 'index.html')
+    {
+        $dest = protected_path('quiz/final/' . $id);
+
+        if ($path === 'index.html') {
+            $entry = $this->crud->getEntry($id);
+            $entry->compile($dest);
+        }
+
+        $p = $dest . '/' . $path;
+        return response(null)->header('Content-Type', Files::_getMimeType($p))->header('X-Sendfile', $p);
+    }
+}
diff --git a/app/Http/Controllers/Admin/Operations/Quiz/ReportOperation.php b/app/Http/Controllers/Admin/Operations/Quiz/ReportOperation.php
new file mode 100644 (file)
index 0000000..2dd68fe
--- /dev/null
@@ -0,0 +1,129 @@
+<?php
+
+namespace App\Http\Controllers\Admin\Operations\Quiz;
+
+use App\Models\Quiz;
+use App\Models\QuizAttempt;
+use Cubist\Util\Files\Files;
+use Illuminate\Support\Facades\Route;
+use PhpOffice\PhpSpreadsheet\Spreadsheet;
+use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
+
+trait ReportOperation
+{
+    protected function setupReportRoutes($segment, $routeName, $controller)
+    {
+        Route::match(['get'], $segment . '/{id}/report', $controller . '@report');
+    }
+
+    protected function setupReportDefaults()
+    {
+        $this->crud->addButtonFromView('line', 'report', 'quiz.report', 'end');
+    }
+
+    protected function report($id)
+    {
+        $quiz = Quiz::where('id', $id)->first()->getPageData();
+        $first = ['#', 'Date'];
+        if ($quiz->display_score) {
+            $first = array_merge($first, ['Score', 'Passed']);
+        }
+
+        $emailQuestion = false;
+        $countForScore = [];
+
+        foreach ($quiz->questions as $q => $question) {
+            $label = $question['report_label'] ?: $question['question'];
+            $first[] = $label;
+            if ($quiz->display_score && $question['count_for_score']) {
+                $first[] = $label . ' status';
+                $countForScore[] = $q;
+            }
+            if ($question['type'] === 'email') {
+                $emailQuestion = $q;
+            }
+        }
+
+        $attemptsList = [$first];
+        $users = [];
+        /** @var QuizAttempt[] $attempts */
+        $attempts = QuizAttempt::where('quiz', $id)->orderBy('created_at', 'ASC')->get();
+        foreach ($attempts as $attempt) {
+            $email = $attempt->id;
+            $data = $attempt->getPageData();
+            $a = [$data->get('id'), $data->get('created_at')];
+            if ($quiz->display_score) {
+                $a[] = $data->get('score');
+                $a[] = ($data->get('passed') ? '1' : '0');
+            }
+            $answers = $data->get('answers', []);
+
+            if (null === $answers || !is_array($answers)) {
+                continue;
+            }
+            foreach ($answers as $aid => $answer) {
+                if (null === $answer) {
+                    continue;
+                }
+                $aa = $answer['anwser'] ?? $answer['answer'] ?? '';
+                if ($emailQuestion !== false) {
+                    if ($aid == $emailQuestion + 1) {
+                        $email = trim(mb_strtolower($aa));
+                    }
+                }
+                $a[] = is_array($aa) ? implode(', ', $aa) : $aa;
+                if ($quiz->display_score && in_array($aid - 1, $countForScore, true)) {
+                    $a[] = $answer['score'];
+                }
+            }
+
+            if ($emailQuestion !== false) {
+                if (!isset($users[$email])) {
+                    $users[$email] = ['totalAttempts' => 0, 'attemptsBeforePassing' => 0, 'passed' => false, 'worstScore' => 100, 'bestScore' => 0];
+                }
+                $users[$email]['totalAttempts']++;
+                $users[$email]['worstScore'] = min($users[$email]['worstScore'], $data->get('score'));
+                $users[$email]['bestScore'] = max($users[$email]['bestScore'], $data->get('score'));
+                if (!$users[$email]['passed']) {
+                    $users[$email]['attemptsBeforePassing']++;
+                }
+                if ($data->get('passed')) {
+                    $users[$email]['passed'] = true;
+                }
+            }
+
+            $attemptsList[] = $a;
+        }
+
+
+        $spreadsheet = new Spreadsheet();
+        $sheet = $spreadsheet->getActiveSheet();
+
+        if ($emailQuestion !== false) {
+            $usersList = [['Email', 'Passed', 'Attempts before passed', 'Total attempts', 'Best score', 'Worst score']];
+            foreach ($users as $email => $user) {
+                $usersList[] = [$email, $user['passed'] ? '1' : '0', $user['attemptsBeforePassing'], $user['totalAttempts'], $user['bestScore'], $user['worstScore']];
+            }
+
+            $sheet->setTitle('USERS');
+            $sheet->fromArray($usersList);
+            foreach (range('A', 'Z') as $columnID) {
+                $sheet->getColumnDimension($columnID)->setAutoSize(true);
+            }
+            $sheet = $spreadsheet->createSheet();
+        }
+
+        array_reverse($attemptsList);
+        $sheet->setTitle('DATA');
+        $sheet->fromArray($attemptsList);
+        foreach (range('A', 'Z') as $columnID) {
+            $sheet->getColumnDimension($columnID)->setAutoSize(true);
+        }
+
+        $writer = new Xlsx($spreadsheet);
+        $tmp = Files::tempnam();
+        $writer->save($tmp);
+
+        return response()->download($tmp, 'report_' . $id . '_' . date('YmdHi') . '.xlsx')->deleteFileAfterSend();
+    }
+}
diff --git a/app/Http/Controllers/Admin/Operations/ReportOperation.php b/app/Http/Controllers/Admin/Operations/ReportOperation.php
deleted file mode 100644 (file)
index a42f5db..0000000
+++ /dev/null
@@ -1,129 +0,0 @@
-<?php
-
-namespace App\Http\Controllers\Admin\Operations;
-
-use App\Models\Quiz;
-use App\Models\QuizAttempt;
-use Cubist\Util\Files\Files;
-use Illuminate\Support\Facades\Route;
-use PhpOffice\PhpSpreadsheet\Spreadsheet;
-use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
-
-trait ReportOperation
-{
-    protected function setupReportRoutes($segment, $routeName, $controller)
-    {
-        Route::match(['get'], $segment . '/{id}/report', $controller . '@report');
-    }
-
-    protected function setupReportDefaults()
-    {
-        $this->crud->addButtonFromView('line', 'report', 'quiz.report', 'end');
-    }
-
-    protected function report($id)
-    {
-        $quiz = Quiz::where('id', $id)->first()->getPageData();
-        $first = ['#', 'Date'];
-        if ($quiz->display_score) {
-            $first = array_merge($first, ['Score', 'Passed']);
-        }
-
-        $emailQuestion = false;
-        $countForScore = [];
-
-        foreach ($quiz->questions as $q => $question) {
-            $label = $question['report_label'] ?: $question['question'];
-            $first[] = $label;
-            if ($quiz->display_score && $question['count_for_score']) {
-                $first[] = $label . ' status';
-                $countForScore[] = $q;
-            }
-            if ($question['type'] === 'email') {
-                $emailQuestion = $q;
-            }
-        }
-
-        $attemptsList = [$first];
-        $users = [];
-        /** @var QuizAttempt[] $attempts */
-        $attempts = QuizAttempt::where('quiz', $id)->orderBy('created_at', 'ASC')->get();
-        foreach ($attempts as $attempt) {
-            $email = $attempt->id;
-            $data = $attempt->getPageData();
-            $a = [$data->get('id'), $data->get('created_at')];
-            if ($quiz->display_score) {
-                $a[] = $data->get('score');
-                $a[] = ($data->get('passed') ? '1' : '0');
-            }
-            $answers = $data->get('answers', []);
-
-            if (null === $answers || !is_array($answers)) {
-                continue;
-            }
-            foreach ($answers as $aid => $answer) {
-                if (null === $answer) {
-                    continue;
-                }
-                $aa = $answer['anwser'] ?? $answer['answer'] ?? '';
-                if ($emailQuestion !== false) {
-                    if ($aid == $emailQuestion + 1) {
-                        $email = trim(mb_strtolower($aa));
-                    }
-                }
-                $a[] = is_array($aa) ? implode(', ', $aa) : $aa;
-                if ($quiz->display_score && in_array($aid - 1, $countForScore, true)) {
-                    $a[] = $answer['score'];
-                }
-            }
-
-            if ($emailQuestion !== false) {
-                if (!isset($users[$email])) {
-                    $users[$email] = ['totalAttempts' => 0, 'attemptsBeforePassing' => 0, 'passed' => false, 'worstScore' => 100, 'bestScore' => 0];
-                }
-                $users[$email]['totalAttempts']++;
-                $users[$email]['worstScore'] = min($users[$email]['worstScore'], $data->get('score'));
-                $users[$email]['bestScore'] = max($users[$email]['bestScore'], $data->get('score'));
-                if (!$users[$email]['passed']) {
-                    $users[$email]['attemptsBeforePassing']++;
-                }
-                if ($data->get('passed')) {
-                    $users[$email]['passed'] = true;
-                }
-            }
-
-            $attemptsList[] = $a;
-        }
-
-
-        $spreadsheet = new Spreadsheet();
-        $sheet = $spreadsheet->getActiveSheet();
-
-        if ($emailQuestion !== false) {
-            $usersList = [['Email', 'Passed', 'Attempts before passed', 'Total attempts', 'Best score', 'Worst score']];
-            foreach ($users as $email => $user) {
-                $usersList[] = [$email, $user['passed'] ? '1' : '0', $user['attemptsBeforePassing'], $user['totalAttempts'], $user['bestScore'], $user['worstScore']];
-            }
-
-            $sheet->setTitle('USERS');
-            $sheet->fromArray($usersList);
-            foreach (range('A', 'Z') as $columnID) {
-                $sheet->getColumnDimension($columnID)->setAutoSize(true);
-            }
-            $sheet = $spreadsheet->createSheet();
-        }
-
-        array_reverse($attemptsList);
-        $sheet->setTitle('DATA');
-        $sheet->fromArray($attemptsList);
-        foreach (range('A', 'Z') as $columnID) {
-            $sheet->getColumnDimension($columnID)->setAutoSize(true);
-        }
-
-        $writer = new Xlsx($spreadsheet);
-        $tmp = Files::tempnam();
-        $writer->save($tmp);
-
-        return response()->download($tmp, 'report_' . $id . '_' . date('YmdHi') . '.xlsx')->deleteFileAfterSend();
-    }
-}
diff --git a/app/Http/Controllers/Admin/PageCrudController.php b/app/Http/Controllers/Admin/PageCrudController.php
new file mode 100644 (file)
index 0000000..1f77d06
--- /dev/null
@@ -0,0 +1,29 @@
+<?php
+
+namespace App\Http\Controllers\Admin;
+
+class PageCrudController extends \Cubist\Backpack\Magic\Controllers\CubistMagicNestedController
+{
+    use \Cubist\Backpack\Magic\Operations\CreateOperation;
+       use \Cubist\Backpack\Http\Controllers\Operations\CloneEditOperation;
+       use \Cubist\Backpack\Magic\Operations\UpdateOperation;
+       use \Cubist\Backpack\Http\Controllers\Operations\BulkPublishOperation;
+       use \Backpack\CRUD\app\Http\Controllers\Operations\CloneOperation;
+       use \Backpack\CRUD\app\Http\Controllers\Operations\BulkCloneOperation;
+       use \Backpack\CRUD\app\Http\Controllers\Operations\DeleteOperation;
+       use \Backpack\CRUD\app\Http\Controllers\Operations\BulkDeleteOperation;
+       use \Cubist\Backpack\Http\Controllers\Operations\ReviseOperation;
+       
+
+
+    /*
+       __('page')
+       __('pages')
+       */
+
+    protected $_modelNamespace = 'App\Models\Page';
+    protected $_routeURL = 'page';
+    protected $_singular = 'page';
+    protected $_plural = 'pages';
+    protected $_oneInstance= false;
+}
index 41ffba72f7e08acf031ce19deaca841afb161543..4f607165c9d059ba95b8cf1748b1880d9995bafb 100644 (file)
@@ -2,9 +2,15 @@
 
 namespace App\Http\Controllers\Admin;
 
-class QuizCrudController extends \App\Http\Controllers\Admin\Base\QuizController
+class QuizCrudController extends \Cubist\Backpack\Magic\Controllers\CubistMagicController
 {
-    use \Cubist\Backpack\Magic\Operations\CreateOperation;
+    use \App\Http\Controllers\Admin\Operations\Quiz\PreviewOperation;
+       use \App\Http\Controllers\Admin\Operations\Quiz\DownloadOperation;
+       use \App\Http\Controllers\Admin\Operations\Quiz\LogOperation;
+       use \App\Http\Controllers\Admin\Operations\Quiz\ReportOperation;
+       use \App\Http\Controllers\Admin\Operations\Quiz\ImportOperation;
+       use \Cubist\Backpack\Magic\Operations\CreateOperation;
+       use \Cubist\Backpack\Http\Controllers\Operations\CloneEditOperation;
        use \Cubist\Backpack\Magic\Operations\UpdateOperation;
        use \Backpack\CRUD\app\Http\Controllers\Operations\CloneOperation;
        use \Backpack\CRUD\app\Http\Controllers\Operations\DeleteOperation;
index ed29dd62b7e053de893818d0788dd3abaa26e0b5..26618a5942d382c994c5eaed4ce41649d59f17cf 100644 (file)
@@ -5,6 +5,7 @@ namespace App\Http\Controllers\Admin;
 class QuizatttemptCrudController extends \Cubist\Backpack\Magic\Controllers\CubistMagicController
 {
     use \Cubist\Backpack\Magic\Operations\CreateOperation;
+       use \Cubist\Backpack\Http\Controllers\Operations\CloneEditOperation;
        use \Cubist\Backpack\Magic\Operations\UpdateOperation;
        use \Cubist\Backpack\Http\Controllers\Operations\BulkPublishOperation;
        use \Backpack\CRUD\app\Http\Controllers\Operations\CloneOperation;
index ecaf1ef1261127718d9381527f447a229c174196..7c540c9a3fe97b7079ae1c3c39aa6cb9a80e7837 100644 (file)
@@ -5,6 +5,7 @@ namespace App\Http\Controllers\Admin;
 class QuiztranslationCrudController extends \Cubist\Backpack\Magic\Controllers\CubistMagicController
 {
     use \Cubist\Backpack\Magic\Operations\CreateOperation;
+       use \Cubist\Backpack\Http\Controllers\Operations\CloneEditOperation;
        use \Cubist\Backpack\Magic\Operations\UpdateOperation;
        use \Cubist\Backpack\Http\Controllers\Operations\BulkPublishOperation;
        use \Backpack\CRUD\app\Http\Controllers\Operations\CloneOperation;
index 090c8ebac53c993a8ff84882affa97dca46340cc..31d2f3cfe05c0e34982bb630a531ebc9e0e590f3 100644 (file)
@@ -5,6 +5,7 @@ namespace App\Http\Controllers\Admin;
 class SettingsCrudController extends \Cubist\Backpack\Magic\Controllers\CubistMagicController
 {
     use \Cubist\Backpack\Magic\Operations\CreateOperation;
+       use \Cubist\Backpack\Http\Controllers\Operations\CloneEditOperation;
        use \Cubist\Backpack\Magic\Operations\UpdateOperation;
        use \Cubist\Backpack\Http\Controllers\Operations\BulkPublishOperation;
        use \Backpack\CRUD\app\Http\Controllers\Operations\CloneOperation;
index 723e1752da0e5973ca7ec07da44222dccf7b161d..78dfdd5bba0082917ff8ece3cda46c462ea708f3 100644 (file)
@@ -5,6 +5,7 @@ namespace App\Http\Controllers\Admin;
 class SignatureCrudController extends \Cubist\Backpack\Magic\Controllers\CubistMagicController
 {
     use \Cubist\Backpack\Magic\Operations\CreateOperation;
+       use \Cubist\Backpack\Http\Controllers\Operations\CloneEditOperation;
        use \Cubist\Backpack\Magic\Operations\UpdateOperation;
        use \Backpack\CRUD\app\Http\Controllers\Operations\CloneOperation;
        use \Backpack\CRUD\app\Http\Controllers\Operations\DeleteOperation;
index aaf3d306bd23d5633d951459813de033f794ef78..099786f093c0a8be789de34078caff38187b7b1b 100644 (file)
@@ -5,6 +5,7 @@ namespace App\Http\Controllers\Admin;
 class ToolboxTranslateCrudController extends \Cubist\Backpack\Magic\Controllers\CubistMagicController
 {
     use \Cubist\Backpack\Magic\Operations\CreateOperation;
+       use \Cubist\Backpack\Http\Controllers\Operations\CloneEditOperation;
        use \Cubist\Backpack\Magic\Operations\UpdateOperation;
        use \Cubist\Backpack\Http\Controllers\Operations\BulkPublishOperation;
        use \Backpack\CRUD\app\Http\Controllers\Operations\CloneOperation;
index 35f235cd4969d44b1b2f0659c6ea8f0188f5d65b..a3c6a0931eda802aecba63cc171d3c0985a229d7 100644 (file)
@@ -7,6 +7,7 @@ class UsersCrudController extends \Cubist\Backpack\Magic\Controllers\CubistMagic
     use \Cubedesigners\UserDatabase\Operations\LoginasOperation;
        use \Cubedesigners\UserDatabase\Operations\CreateFromCompany;
        use \Cubist\Backpack\Magic\Operations\CreateOperation;
+       use \Cubist\Backpack\Http\Controllers\Operations\CloneEditOperation;
        use \Cubist\Backpack\Magic\Operations\UpdateOperation;
        use \Cubist\Backpack\Http\Controllers\Operations\ReviseOperation;
        
diff --git a/app/Jobs/QuizDownload.php b/app/Jobs/QuizDownload.php
new file mode 100644 (file)
index 0000000..6b149dd
--- /dev/null
@@ -0,0 +1,54 @@
+<?php
+
+namespace App\Jobs;
+
+use App\Services\ScormCloud;
+use Cubist\Util\Files\Files;
+use Cubist\Util\Zip;
+
+class QuizDownload extends DownloadBase
+{
+
+    protected $type = 'quiz';
+
+    public function handle()
+    {
+        try {
+            $compilepath = $this->entry->getFinalPath();
+            $this->entry->compile($compilepath, $this->user);
+
+            $fname = $this->_fname();
+            $dest = Files::mkdir(storage_path('app/public/quiz/download/')) . $fname;
+
+            Zip::archive($compilepath, $dest);
+            if (!file_exists($dest)) {
+                throw new \Exception('An error occured while compiling the quiz');
+            }
+
+            $url = $this->_url($fname);
+
+            $subject = __('Quiz ":title" (#:nb) prêt au téléchargement', ['title' => $this->entry->title, 'nb' => $this->entry->id]);
+            $body = __('Le fichier est disponible à l\'adresse suivante : <a href=":url">:url</a>', ['url' => $url]);
+
+            try {
+                if ($this->action === 'scormcloud') {
+                    $scormURL = ScormCloud::send($url, 'toolbox_' . $this->type . '_' . $this->entry->id);
+                    $body .= "<br><br>";
+                    $body .= __('Le package peut être testé sur SCORM Cloud : <a href=":url">:url</a>', ['url' => $scormURL]);
+                }
+            } catch (\Exception $e) {
+                $body .= "<br><br>";
+                $body .= __('Une erreur s\'est produite lors de l\'envoi sur SCORM Cloud (App ID :appid) : :error', ['error' => $e->getMessage(), 'appid' => env('SCORM_CLOUD_APP_ID')]);
+            }
+
+
+        } catch (\Exception $e) {
+            $subject = __('Erreur lors de la compilation du quiz :nb', ['nb' => $this->entry->id]);
+            $body = __('Détails de l\'erreur :message', ['message' => $e->getMessage() . ' at line ' . $e->getLine() . ' of ' . $e->getFile()]);
+        }
+
+        $this->sendEmail($subject, $body);
+    }
+
+
+}
index c2d72c9bd0495629a70d108cdfee8d0b4f8eee7e..e6dccaedaf6b8af85259da1ab9a1280942ad96e2 100644 (file)
@@ -12,6 +12,7 @@ use Cubist\Backpack\Magic\Fields\BunchOfFieldsMultiple;
 use Cubist\Backpack\Magic\Fields\Text;
 use Cubist\Backpack\Magic\Fields\Textarea;
 use Cubist\Scorm\Manifest;
+use Cubist\Scorm\Version;
 use Cubist\Util\Files\Files;
 use Cubist\Util\Files\VirtualDirectory;
 use Cubist\Util\Zip;
@@ -72,7 +73,7 @@ class ELearningPackage extends ToolboxModel
         $data = ['title' => $this->title, 'description' => $this->description, 'modules' => $modules];
 
         $vdir->file_put_contents('data.js', 'const DATA=' . json_encode($data) . ';');
-        $vdir->file_put_contents('imsmanifest.xml', new Manifest($this->title, Manifest::SCORM_2004, $organization, 'PACKAGE_' . $this->id));
+        $vdir->file_put_contents('imsmanifest.xml', new Manifest($this->title, Version::SCORM_2004, $organization, 'PACKAGE_' . $this->id));
         $vdir->copyDirectory(resource_path('elearningpackage/dist/css'), 'css');
         $vdir->copyDirectory(resource_path('elearningpackage/dist/js'), 'js');
         $vdir->copyDirectory(resource_path('elearningpackage/dist/fonts'), 'fonts');
index 278f930c0c2b10ce1225819adbd58dc17cbe19d4..dbc03bcbdadc7f65ef0bc3d1f65a94fada63a443 100644 (file)
@@ -1,20 +1,23 @@
 <?php
 
-
 namespace App\Models;
 
+use App\Http\Controllers\Admin\Operations\Quiz\DownloadOperation;
+use App\Http\Controllers\Admin\Operations\Quiz\ImportOperation;
+use App\Http\Controllers\Admin\Operations\Quiz\LogOperation;
+use App\Http\Controllers\Admin\Operations\Quiz\PreviewOperation;
+use App\Http\Controllers\Admin\Operations\Quiz\ReportOperation;
+use Cubist\Scorm\Manifest;
+use Cubist\Util\Files\VirtualDirectory;
 use App\Fields\SCORMVersion;
 use App\Http\Controllers\Admin\Base\QuizController;
 use App\Models\Base\ToolboxModel;
 use App\Models\Traits\SCORMVersionTrait;
-use Cubist\Scorm\Manifest;
-use Cubist\Util\Files\Files;
 use Spatie\MediaLibrary\MediaCollections\Models\Media;
 use Spatie\Image\Manipulations;
 
 class Quiz extends ToolboxModel
 {
-    protected $_baseController = QuizController::class;
 
     protected $table = 'quiz';
 
@@ -28,6 +31,8 @@ class Quiz extends ToolboxModel
 
     public $registerMediaConversionsUsingModelInstance = false;
 
+    protected $_operations = [PreviewOperation::class, DownloadOperation::class, LogOperation::class, ReportOperation::class, ImportOperation::class];
+
     use SCORMVersionTrait;
 
     protected static function _getColors()
@@ -233,18 +238,48 @@ class Quiz extends ToolboxModel
 
     /**
      * @param $quiz self
+     * @throws \JsonException
      */
-    public function compile($dest, $forceScorm = false)
+    public function compile($dest, $forceScorm = false, $user = null)
     {
-        // Clean existing dir and recreate it
-        if (file_exists($dest)) {
-            Files::rmdir($dest);
-        }
-        Files::mkdir($dest);
+        /*
+
+         $defaultModuleContent = ['content_title' => '', 'complete_when_opened' => false, 'mandatory' => true,
+             'quiz_id' => '', 'fb_id' => '', 'audio_id' => '', 'pdf_id' => '', 'video_id' => ''];
+
+
+
+         $modules = [];
+
+         foreach ($this->contents as $id => $content) {
+             $m = $this->_compileModule($id, array_merge($defaultModuleContent, $content), $vdir, $user);
+             if ($m !== false) {
+                 $modules[] = $m;
+             }
+         }
+
+         $data = ['title' => $this->title, 'description' => $this->description, 'modules' => $modules];
+
+         $vdir->file_put_contents('data.js', 'const DATA=' . json_encode($data) . ';');
+         $vdir->file_put_contents('imsmanifest.xml', new Manifest($this->title, Version::SCORM_2004, $organization, 'PACKAGE_' . $this->id));
+         $vdir->copyDirectory(resource_path('elearningpackage/dist/css'), 'css');
+         $vdir->copyDirectory(resource_path('elearningpackage/dist/js'), 'js');
+         $vdir->copyDirectory(resource_path('elearningpackage/dist/fonts'), 'fonts');
+         $vdir->copy(resource_path('elearningpackage/index.html'), 'index.html');
+         $vdir->sync(true);*/
 
-        // Extract template into the final dir
-        $from = resource_path('quiz') . '/';
-        `rsync -a --exclude '*.less' --exclude '*.less' $from $dest/`;
+        $wdir = resource_path('quiz');
+        $vdir = new VirtualDirectory($dest);
+
+
+        $data = $this->getData();
+        $json = json_encode($data, JSON_THROW_ON_ERROR);
+        $vdir->file_put_contents('data.js', 'var DATA=' . $json . ';');
+        $vdir->copy($wdir . '/index.html', 'index.html');
+        $vdir->copyDirectory($wdir . '/dist/css', 'css');
+        $vdir->copyDirectory($wdir . '/dist/js', 'js');
+        $vdir->copyDirectory($wdir . '/fonts', 'fonts');
+        $vdir->copyDirectory($wdir . '/assets', 'assets');
 
         // Copy assets
         $assets = ['banner' => 'banner.jpg', 'logo' => 'logo.png'];
@@ -258,30 +293,18 @@ class Quiz extends ToolboxModel
 
                     $path = $media->getPath($conversionName);
                     if (file_exists($path)) {
-                        copy($path, $dest . '/assets/' . $filename);
+                        $vdir->copy($path, 'assets/' . $filename);
                     }
                 }
             }
         }
 
-        $data = $this->getData();
-
-        $json = json_encode($data, JSON_THROW_ON_ERROR);
-
-        file_put_contents($dest . '/data.js', 'var DATA=' . $json . ';');
 
         if ($forceScorm || $this->getAttribute('scorm')) {
-            $scorm_js = '<script src="js/libs/scorm/apiwrapper.js"></script>
-<script src="js/scorm.js"></script>';
             $manifest = new Manifest($this->getAttribute('title'), $this->getAttribute('scorm_version'), $this->getAttribute('client') ?: backpack_user()->getCompanyNameAttribute(), $this->getAttribute('project') ?: 'Quiz');
-            file_put_contents($dest . '/imsmanifest.xml', $manifest);
-        } else {
-            $scorm_js = '';
-            unlink($dest . '/js/scorm.js');
-            unlink($dest . '/js/libs/scorm/apiwrapper.js');
+            $vdir->file_put_contents('imsmanifest.xml', $manifest);
         }
-
-        file_put_contents($dest . '/index.html', str_replace('$SCORM_JS', $scorm_js, file_get_contents($dest . '/index.html')));
+        $vdir->sync(true);
     }
 
     public function getData()
@@ -367,4 +390,9 @@ class Quiz extends ToolboxModel
         return parent::create($data);
     }
 
+    public function getFinalPath()
+    {
+        return protected_path('quiz/final/' . $this->id);
+    }
+
 }
index 33d1ee0b96ad3726994f8eeaff67cfcfced39fbb..6272b99b2ab831ef505f3ae5a56e2bbcdd49a001 100644 (file)
@@ -1,48 +1,13 @@
 require('./bootstrap');
-import {SCORM} from 'pipwerks-scorm-api-wrapper';
+require('../../scorm/scorm');
 
 window.savedState = {};
 window.currentModule = null;
 
-var SCORM_INITED = false;
-var SCORM_START_TIME = null;
-var SCORM_INTERACTION_TIMESTAMPS = [];
-var SCORM_CORRECT_ANSWERS = [];
-var SCORM_ID_TO_N = {};
-var SCORM_WEIGHTING = 0;
-var SCORM_QUESTIONS = [];
-var SCORM_SUCCESS_STATUS = 'unknown';
-var SCORM_SUCCESS_SCORE = 0;
-var SCORM_EVENTS_INITED = false;
-var SCORM_INTERACTIONS_INITED = false;
-var SCORM_LOCATION_INITED = false;
-var SCORM_OK = false;
-
-var _CMI12 = {
-    'location': 'cmi.core.lesson_location',
-    'status': "cmi.core.lesson_status",
-    'session_time': 'cmi.core.session_time',
-    'success_status': '',
-    'exit': 'cmi.core.exit',
-    'cmi.score.raw': 'cmi.core.score.raw',
-    'cmi.score.min': 'cmi.core.score.min',
-    'cmi.score.max': 'cmi.core.score.max',
-    'cmi.score.scaled': '',
-};
-
-var _CMI2004 = {
-    'location': 'cmi.location',
-    'status': 'cmi.completion_status',
-    'session_time': 'cmi.session_time',
-    'success_status': 'cmi.success_status',
-    'exit': 'cmi.exit',
-};
-
-
 $(function () {
     $("header #logo").html(getSpriteIcon('logo'));
     $("header #tile").html(getSpriteIcon('tile'));
-    initScorm();
+    initPackage();
     $(window).on('resize', function () {
         resize();
     });
@@ -57,58 +22,20 @@ function resize() {
     }
 }
 
-function _cmi(key) {
-    var res = null;
-    switch (SCORM.version) {
-        case "1.2" :
-            res = _CMI12[key];
-            break;
-        case '2004':
-            res = _CMI2004[key];
-            break;
-    }
-    if (res == undefined || res == null) {
-        res = key;
-    }
-    return res;
-}
-
-function initScorm() {
+function initPackage() {
     if (SCORM_INITED) {
         return;
     }
 
-    SCORM_INITED = true;
-    try {
-        if (SCORM.init()) {
-            SCORM_OK = true;
-        }
-    } catch (e) {
-
-    }
-
-    try {
-        if (FORCE_SCORM) {
-            SCORM_OK = true;
-        }
-    } catch (e) {
-
-    }
-
-    if (SCORM_OK) {
-        scormExit();
-        startScormTimer();
-        initScormEvents();
-    }
+    var res=initScorm();
     initState();
     setContents();
     initEvents();
     window.API = window.API_1484_11 = new SCORMFacade();
-    return SCORM_OK;
+    return res;
 }
 
 function initState() {
-
     if (SCORM_LOCATION_INITED) {
         return;
     }
@@ -145,47 +72,6 @@ function initState() {
         }
     });
 }
-
-function scormMarkAsComplete() {
-    if (!SCORM_OK) {
-        return;
-    }
-    scormExit();
-}
-
-
-function finishScorm() {
-    if (!SCORM_OK) {
-        return;
-    }
-    setSessionTime();
-    SCORM.save();
-    SCORM.quit();
-}
-
-function scormExit() {
-    if (!SCORM_OK) {
-        return;
-    }
-    var v = 'suspend';
-    setScormValue('exit', v);
-}
-
-function startScormTimer() {
-    SCORM_START_TIME = new Date();
-}
-
-function scormCompleteAndClose() {
-    scormComplete();
-    scormClose();
-}
-
-function scormClose() {
-    parent.close();
-    top.close();
-    window.close();
-}
-
 function initEvents() {
     $(document).on('click', '[data-id]:not([data-lock="locked"])', function () {
         openSubSCO($(this).data('id'));
@@ -389,61 +275,6 @@ function doubleQuestionMark() {
     // Using `this` for web workers & supports Browserify / Webpack.
 })(typeof window === 'undefined' ? this : window);
 
-
-function initScormEvents() {
-    if (!SCORM_OK || SCORM_EVENTS_INITED) {
-        return;
-    }
-    SCORM_EVENTS_INITED = true;
-
-    $(window).on('unload', function () {
-        finishScorm();
-    });
-
-    setInterval(function () {
-        SCORM.save();
-    }, 5000);
-}
-
-function setSCORMLocation(location) {
-    return setScormValue('cmi.location', JSON.stringify(location));
-}
-
-function setSCORMScore(score, max, min) {
-    if (min === undefined) {
-        min = 0;
-    }
-
-    setScormValue('cmi.core.score.raw', score);
-    setScormValue('cmi.core.score.min', min);
-    setScormValue('cmi.core.score.max', max);
-}
-
-
-function getScormValue(elementName) {
-    if (!SCORM_OK) {
-        return null;
-    }
-    var cmi = _cmi(elementName);
-    if (cmi == '') {
-        return null;
-    }
-    var result = SCORM.get(cmi);
-    return result;
-}
-
-function setScormValue(elementName, value) {
-    if (!SCORM_OK) {
-        return;
-    }
-    var cmi = _cmi(elementName);
-    if (cmi == '') {
-        return false;
-    }
-    var result = SCORM.set(cmi, value);
-    return result;
-}
-
 function getSpriteIcon(icon, attrs, dimensions) {
     var a = [];
     var iconSymbol = $('svg symbol[id="' + icon + '"]');
@@ -484,67 +315,6 @@ function getSpriteIcon(icon, attrs, dimensions) {
     return '<svg ' + a.join(' ') + ' aria-hidden="true"><use xlink:href="#' + icon + '" /></svg>';
 }
 
-
-function setSessionTime() {
-    if (!SCORM_OK) {
-        return;
-    }
-    var currentTime = new Date();
-    var sessionTime;
-
-    var endTime = currentTime.getTime()
-    var calculatedTime = endTime - SCORM_START_TIME;
-
-    if (SCORM.version == '1.2') {
-        var totalHours = Math.floor(calculatedTime / 1000 / 60 / 60);
-        calculatedTime = calculatedTime - totalHours * 1000 * 60 * 60
-        if (totalHours < 1000 && totalHours > 99) {
-            totalHours = "0" + totalHours;
-        } else if (totalHours < 100 && totalHours > 9) {
-            totalHours = "00" + totalHours;
-        } else if (totalHours < 10) {
-            totalHours = "000" + totalHours;
-        }
-
-        var totalMinutes = Math.floor(calculatedTime / 1000 / 60);
-        calculatedTime = calculatedTime - totalMinutes * 1000 * 60;
-        if (totalMinutes < 10) {
-            totalMinutes = "0" + totalMinutes;
-        }
-
-        var totalSeconds = Math.floor(calculatedTime / 1000);
-        if (totalSeconds < 10) {
-            totalSeconds = "0" + totalSeconds;
-        }
-        sessionTime = totalHours + ":" + totalMinutes + ":" + totalSeconds;
-        setScormValue('session_time', sessionTime);
-    } else {
-        setScormValue('session_time', scormSecondsToTimeInterval(calculatedTime / 1000));
-    }
-
-}
-
-function dateToScormTime(date) {
-    var res = date.toISOString();
-    var e = res.split('.');
-    e.pop();
-    return e.join('.');
-}
-
-function getScormTimeInterval(start, end) {
-    var diff = Math.round((end.getTime() - start.getTime()) / 1000);
-    return scormSecondsToTimeInterval(diff);
-}
-
-function scormSecondsToTimeInterval(diff) {
-    var diff = Math.round(diff);
-    var h = Math.floor(diff / 3600);
-    diff = diff % 3600;
-    var m = Math.floor(diff / 60);
-    var s = diff % 60;
-    return 'PT' + h + 'H' + m + 'M' + s + 'S';
-}
-
 function SCORMFacade() {
 }
 
diff --git a/resources/quiz/fonts/Roboto-Bold-webfont.woff b/resources/quiz/fonts/Roboto-Bold-webfont.woff
new file mode 100644 (file)
index 0000000..8c9b024
Binary files /dev/null and b/resources/quiz/fonts/Roboto-Bold-webfont.woff differ
diff --git a/resources/quiz/fonts/Roboto-Light-webfont.woff b/resources/quiz/fonts/Roboto-Light-webfont.woff
new file mode 100644 (file)
index 0000000..983083c
Binary files /dev/null and b/resources/quiz/fonts/Roboto-Light-webfont.woff differ
diff --git a/resources/quiz/fonts/Roboto-Regular-webfont.woff b/resources/quiz/fonts/Roboto-Regular-webfont.woff
new file mode 100644 (file)
index 0000000..7245f5c
Binary files /dev/null and b/resources/quiz/fonts/Roboto-Regular-webfont.woff differ
diff --git a/resources/quiz/fonts/RobotoCondensed-Bold-webfont.woff b/resources/quiz/fonts/RobotoCondensed-Bold-webfont.woff
new file mode 100644 (file)
index 0000000..f564612
Binary files /dev/null and b/resources/quiz/fonts/RobotoCondensed-Bold-webfont.woff differ
diff --git a/resources/quiz/fonts/RobotoCondensed-Light-webfont.woff b/resources/quiz/fonts/RobotoCondensed-Light-webfont.woff
new file mode 100644 (file)
index 0000000..6a8ae66
Binary files /dev/null and b/resources/quiz/fonts/RobotoCondensed-Light-webfont.woff differ
diff --git a/resources/quiz/fonts/RobotoCondensed-Regular-webfont.woff b/resources/quiz/fonts/RobotoCondensed-Regular-webfont.woff
new file mode 100644 (file)
index 0000000..dd61f24
Binary files /dev/null and b/resources/quiz/fonts/RobotoCondensed-Regular-webfont.woff differ
diff --git a/resources/quiz/fonts/fontawesome-webfont.woff b/resources/quiz/fonts/fontawesome-webfont.woff
new file mode 100644 (file)
index 0000000..400014a
Binary files /dev/null and b/resources/quiz/fonts/fontawesome-webfont.woff differ
diff --git a/resources/quiz/fonts/fontawesome-webfont.woff2 b/resources/quiz/fonts/fontawesome-webfont.woff2
new file mode 100644 (file)
index 0000000..4d13fc6
Binary files /dev/null and b/resources/quiz/fonts/fontawesome-webfont.woff2 differ
index 2cd105b3593eb94a0ab04b8b866f5e356ee95d01..ef0dceb215fe5e1122d2b8b0a0aa5d8795e74ee7 100644 (file)
@@ -7,7 +7,7 @@
     <meta name="width" content="1024">
     <meta name="height" content="768">
     <title></title>
-    <link type="text/css" rel="stylesheet" href="style/style.css">
+    <link type="text/css" rel="stylesheet" href="css/style.css">
 </head>
 <body>
 <div id="holder">
     </main>
 </div>
 <script src="data.js"></script>
-<script src="js/polyfill/cssvars.js"></script>
-<script src="js/libs/jquery.min.js"></script>
-<script src="js/libs/jquery.scrollTo.min.js"></script>
-<script src="js/libs/gsap/TweenMax.min.js"></script>
-<script src="js/libs/gsap/jquery.gsap.min.js"></script>
-$SCORM_JS
-<script src="js/main.js"></script>
+<script src="js/app.js"></script>
 </body>
 </html>
index 482a74535534ae747d67eec61cdcbc5bdd59dd77..76c12af5ee72ed50b176a3fbacbcac859c22380d 100644 (file)
@@ -1,12 +1,39 @@
-require('./bootstrap');
+window.$ = window.jQuery = require('jquery');
 import {SCORM} from 'pipwerks-scorm-api-wrapper';
 import {gsap} from "gsap";
 
+require('../../scorm/scorm');
+
 (function (global) {
     $(function () {
         var data;
         var score;
         var questionStatus = {};
+        var showReview, threshold, instantReview, logAttempts, displayScore, countQuestions, passedAction, failedAction,
+            logQuestions;
+
+        function initApp() {
+            if (SCORM_INITED) {
+                return;
+            }
+            var scormok = initScorm();
+            var defaultState = {q: 1};
+            var state;
+            if (!scormok) {
+                state = defaultState;
+            } else {
+                var currentLocation = getScormValue('location');
+                try {
+                    state = JSON.parse(currentLocation);
+                } catch (e) {
+                    state = defaultState;
+                }
+                if (state['q'] === undefined || state['q'] === null) {
+                    state = defaultState;
+                }
+            }
+            init(state);
+        }
 
         $(window).on('resize', resize);
 
@@ -73,7 +100,6 @@ import {gsap} from "gsap";
             document.documentElement.style.setProperty('--nok-color', DATA.nokColor);
             document.documentElement.style.setProperty('--review-background', DATA.reviewBackground);
             $('head').append('<style>:root{--main-color:' + DATA.mainColor + ';}</style>');
-            cssVars({});
 
             showReview = DATA.review;
             threshold = 0;
@@ -189,12 +215,9 @@ import {gsap} from "gsap";
             $(document).on('quizinit', function (event, state) {
                 init(state);
             });
-            if (DATA.scorm) {
-                initScormEvents();
-            } else {
-                SCORM = false;
-                $(document).trigger('quizinit', {q: 1});
-            }
+
+            initApp();
+
             resizeContainer();
             resize();
         }
@@ -211,7 +234,6 @@ import {gsap} from "gsap";
                 return false;
             });
 
-
             if (state.a) {
                 $.each($(state.a), function (k, v) {
                     var q = $('section.question[data-q="' + (k + 1) + '"]');
@@ -605,5 +627,3 @@ import {gsap} from "gsap";
         return {sprintf: y, vsprintf: e}
     }))
 }();
-
-require('./scorm');
diff --git a/resources/quiz/js/bootstrap.js b/resources/quiz/js/bootstrap.js
deleted file mode 100644 (file)
index 48c9cff..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-window._ = require('lodash');
-
-/**
- * We'll load the axios HTTP library which allows us to easily issue requests
- * to our Laravel back-end. This library automatically handles sending the
- * CSRF token as a header based on the value of the "XSRF" token cookie.
- */
-
-window.axios = require('axios');
-window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
-window.$ = window.jQuery = require('jquery');
diff --git a/resources/quiz/js/scorm.js b/resources/quiz/js/scorm.js
deleted file mode 100644 (file)
index 31692e9..0000000
+++ /dev/null
@@ -1,65 +0,0 @@
-SCORM = true;
-
-function initScormEvents() {
-    SCORM = doLMSInitialize();
-    var defaultState = {q: 1};
-    if (!SCORM) {
-        // No SCORM, we init at question 1
-        $(document).trigger('quizinit', defaultState);
-        return false;
-    }
-    $(window).on('unload', function () {
-        doLMSFinish();
-    });
-
-    var currentStatus = getScormValue('cmi.core.lesson_status');
-    if (currentStatus != 'passed' || currentStatus != 'completed') {
-        setScormValue('cmi.core.lesson_status', 'incomplete');
-    }
-
-    var savedState = getScormValue('cmi.core.lesson_location');
-    try {
-        if (savedState == '') {
-            savedState = defaultState;
-        } else {
-            savedState = JSON.parse(savedState);
-        }
-        $(document).trigger('quizinit', savedState);
-    } catch (err) {
-        console.error(err);
-    }
-
-    return true;
-}
-
-function setSCORMLocation(location) {
-    return setScormValue('cmi.core.lesson_location', JSON.stringify(location));
-}
-
-function setSCORMScore(score, max, min) {
-    if (min === undefined) {
-        min = 0;
-    }
-
-    setScormValue('cmi.core.score.raw', ((score - min) / (max - min)) * 100);
-    setScormValue('cmi.core.score.min', 0);
-    setScormValue('cmi.core.score.max', 100);
-}
-
-
-function getScormValue(elementName) {
-    if (!SCORM) {
-        return null;
-    }
-    var result = String(doLMSGetValue(elementName));
-    return result;
-}
-
-function setScormValue(elementName, value) {
-    if (!SCORM) {
-        return;
-    }
-    var result = doLMSSetValue(elementName, value);
-    doLMSCommit();
-    return result;
-}
index ec0991554962f9a7715abc6a518afd816d82d89e..1282e602dbd9bbe858ba2087260c11886af55ef9 100644 (file)
@@ -1,17 +1,17 @@
 @font-face
     font-family: 'RobotoCondensed'
-    src: url('fonts/RobotoCondensed-Regular-webfont.woff') format('woff')
+    src: url('../fonts/RobotoCondensed-Regular-webfont.woff') format('woff')
     font-weight: 400
     font-style: normal
 
 @font-face
     font-family: 'RobotoCondensed'
-    src: url('fonts/RobotoCondensed-Light-webfont.woff') format('woff')
+    src: url('../fonts/RobotoCondensed-Light-webfont.woff') format('woff')
     font-weight: 300
     font-style: normal
 
 @font-face
     font-family: 'RobotoCondensed'
-    src: url('fonts/RobotoCondensed-Bold-webfont.woff') format('woff')
+    src: url('../fonts/RobotoCondensed-Bold-webfont.woff') format('woff')
     font-weight: 700
     font-style: normal
diff --git a/resources/quiz/style/fonts/Roboto-Bold-webfont.woff b/resources/quiz/style/fonts/Roboto-Bold-webfont.woff
deleted file mode 100644 (file)
index 8c9b024..0000000
Binary files a/resources/quiz/style/fonts/Roboto-Bold-webfont.woff and /dev/null differ
diff --git a/resources/quiz/style/fonts/Roboto-Light-webfont.woff b/resources/quiz/style/fonts/Roboto-Light-webfont.woff
deleted file mode 100644 (file)
index 983083c..0000000
Binary files a/resources/quiz/style/fonts/Roboto-Light-webfont.woff and /dev/null differ
diff --git a/resources/quiz/style/fonts/Roboto-Regular-webfont.woff b/resources/quiz/style/fonts/Roboto-Regular-webfont.woff
deleted file mode 100644 (file)
index 7245f5c..0000000
Binary files a/resources/quiz/style/fonts/Roboto-Regular-webfont.woff and /dev/null differ
diff --git a/resources/quiz/style/fonts/RobotoCondensed-Bold-webfont.woff b/resources/quiz/style/fonts/RobotoCondensed-Bold-webfont.woff
deleted file mode 100644 (file)
index f564612..0000000
Binary files a/resources/quiz/style/fonts/RobotoCondensed-Bold-webfont.woff and /dev/null differ
diff --git a/resources/quiz/style/fonts/RobotoCondensed-Light-webfont.woff b/resources/quiz/style/fonts/RobotoCondensed-Light-webfont.woff
deleted file mode 100644 (file)
index 6a8ae66..0000000
Binary files a/resources/quiz/style/fonts/RobotoCondensed-Light-webfont.woff and /dev/null differ
diff --git a/resources/quiz/style/fonts/RobotoCondensed-Regular-webfont.woff b/resources/quiz/style/fonts/RobotoCondensed-Regular-webfont.woff
deleted file mode 100644 (file)
index dd61f24..0000000
Binary files a/resources/quiz/style/fonts/RobotoCondensed-Regular-webfont.woff and /dev/null differ
diff --git a/resources/quiz/style/fonts/fontawesome-webfont.woff b/resources/quiz/style/fonts/fontawesome-webfont.woff
deleted file mode 100644 (file)
index 400014a..0000000
Binary files a/resources/quiz/style/fonts/fontawesome-webfont.woff and /dev/null differ
diff --git a/resources/quiz/style/fonts/fontawesome-webfont.woff2 b/resources/quiz/style/fonts/fontawesome-webfont.woff2
deleted file mode 100644 (file)
index 4d13fc6..0000000
Binary files a/resources/quiz/style/fonts/fontawesome-webfont.woff2 and /dev/null differ
diff --git a/resources/scorm/scorm.js b/resources/scorm/scorm.js
new file mode 100644 (file)
index 0000000..31f8c73
--- /dev/null
@@ -0,0 +1,235 @@
+import {SCORM} from "pipwerks-scorm-api-wrapper";
+
+window.SCORM_INITED = false;
+window.SCORM_START_TIME = null;
+window.SCORM_INTERACTION_TIMESTAMPS = [];
+window.SCORM_CORRECT_ANSWERS = [];
+window.SCORM_ID_TO_N = {};
+window.SCORM_WEIGHTING = 0;
+window.SCORM_QUESTIONS = [];
+window.SCORM_SUCCESS_STATUS = 'unknown';
+window.SCORM_SUCCESS_SCORE = 0;
+window.SCORM_EVENTS_INITED = false;
+window.SCORM_INTERACTIONS_INITED = false;
+window.SCORM_LOCATION_INITED = false;
+window.SCORM_OK = false;
+
+window._CMI12 = {
+    'location': 'cmi.core.lesson_location',
+    'status': "cmi.core.lesson_status",
+    'session_time': 'cmi.core.session_time',
+    'success_status': '',
+    'exit': 'cmi.core.exit',
+    'cmi.score.raw': 'cmi.core.score.raw',
+    'cmi.score.min': 'cmi.core.score.min',
+    'cmi.score.max': 'cmi.core.score.max',
+    'cmi.score.scaled': '',
+};
+
+window._CMI2004 = {
+    'location': 'cmi.location',
+    'status': 'cmi.completion_status',
+    'session_time': 'cmi.session_time',
+    'success_status': 'cmi.success_status',
+    'exit': 'cmi.exit',
+};
+
+window.initScorm=function()
+{
+    if (SCORM_INITED) {
+        return;
+    }
+
+    SCORM_INITED = true;
+    try {
+        if (SCORM.init()) {
+            SCORM_OK = true;
+        }
+    } catch (e) {
+
+    }
+
+    try {
+        if (FORCE_SCORM) {
+            SCORM_OK = true;
+        }
+    } catch (e) {
+
+    }
+
+    if (SCORM_OK) {
+        scormExit();
+        startScormTimer();
+        initScormEvents();
+    }
+    return SCORM_OK;
+}
+
+window._cmi = function (key) {
+    var res = null;
+    switch (SCORM.version) {
+        case "1.2" :
+            res = _CMI12[key];
+            break;
+        case '2004':
+            res = _CMI2004[key];
+            break;
+    }
+    if (res == undefined || res == null) {
+        res = key;
+    }
+    return res;
+};
+
+window.initScormEvents = function () {
+    if (!SCORM_OK || SCORM_EVENTS_INITED) {
+        return;
+    }
+    SCORM_EVENTS_INITED = true;
+
+    $(window).on('unload', function () {
+        finishScorm();
+    });
+
+    setInterval(function () {
+        SCORM.save();
+    }, 5000);
+};
+
+window.setSCORMLocation = function (location) {
+    return setScormValue('cmi.location', JSON.stringify(location));
+};
+
+window.setSCORMScore = function (score, max, min) {
+    if (min === undefined) {
+        min = 0;
+    }
+
+    setScormValue('cmi.core.score.raw', score);
+    setScormValue('cmi.core.score.min', min);
+    setScormValue('cmi.core.score.max', max);
+};
+window.getScormValue = function (elementName) {
+    if (!SCORM_OK) {
+        return null;
+    }
+    var cmi = _cmi(elementName);
+    if (cmi == '') {
+        return null;
+    }
+    return result = SCORM.get(cmi);
+};
+
+window.setScormValue=function(elementName, value) {
+    if (!SCORM_OK) {
+        return;
+    }
+    var cmi = _cmi(elementName);
+    if (cmi == '') {
+        return false;
+    }
+    var result = SCORM.set(cmi, value);
+    return result;
+};
+
+
+window.scormMarkAsComplete=function() {
+    if (!SCORM_OK) {
+        return;
+    }
+    scormExit();
+};
+
+
+window.finishScorm=function() {
+    if (!SCORM_OK) {
+        return;
+    }
+    setSessionTime();
+    SCORM.save();
+    SCORM.quit();
+};
+
+window.scormExit = function () {
+    if (!SCORM_OK) {
+        return;
+    }
+    var v = 'suspend';
+    setScormValue('exit', v);
+}
+
+window.startScormTimer = function () {
+    SCORM_START_TIME = new Date();
+}
+
+window.scormCompleteAndClose = function () {
+    scormComplete();
+    scormClose();
+}
+
+window.scormClose = function () {
+    parent.close();
+    top.close();
+    window.close();
+};
+
+
+window.setSessionTime = function () {
+    if (!SCORM_OK) {
+        return;
+    }
+    var currentTime = new Date();
+    var sessionTime;
+
+    var endTime = currentTime.getTime()
+    var calculatedTime = endTime - SCORM_START_TIME;
+
+    if (SCORM.version == '1.2') {
+        var totalHours = Math.floor(calculatedTime / 1000 / 60 / 60);
+        calculatedTime = calculatedTime - totalHours * 1000 * 60 * 60
+        if (totalHours < 1000 && totalHours > 99) {
+            totalHours = "0" + totalHours;
+        } else if (totalHours < 100 && totalHours > 9) {
+            totalHours = "00" + totalHours;
+        } else if (totalHours < 10) {
+            totalHours = "000" + totalHours;
+        }
+
+        var totalMinutes = Math.floor(calculatedTime / 1000 / 60);
+        calculatedTime = calculatedTime - totalMinutes * 1000 * 60;
+        if (totalMinutes < 10) {
+            totalMinutes = "0" + totalMinutes;
+        }
+
+        var totalSeconds = Math.floor(calculatedTime / 1000);
+        if (totalSeconds < 10) {
+            totalSeconds = "0" + totalSeconds;
+        }
+        sessionTime = totalHours + ":" + totalMinutes + ":" + totalSeconds;
+        setScormValue('session_time', sessionTime);
+    } else {
+        setScormValue('session_time', scormSecondsToTimeInterval(calculatedTime / 1000));
+    }
+
+};
+
+window.dateToScormTime = function (date) {
+    var res = date.toISOString();
+    var e = res.split('.');
+    e.pop();
+    return e.join('.');
+};
+
+window.getScormTimeInterval = function (start, end) {
+    var diff = Math.round((end.getTime() - start.getTime()) / 1000);
+    return scormSecondsToTimeInterval(diff);
+};
+
+window.scormSecondsToTimeInterval = function (diff) {
+    var diff = Math.round(diff);
+    var h = Math.floor(diff / 3600);
+    diff = diff % 3600;
+    var m = Math.floor(diff / 60);
+    var s = diff % 60;
+    return 'PT' + h + 'H' + m + 'M' + s + 'S';
+};
index 377cf9b89f1fc6b799d5ce14fdcdb2bc2de00bd7..91606b1e591fa4bf0a12ebf873be58257b66dd1a 100644 (file)
@@ -1,2 +1,51 @@
-<a class="btn btn-sm btn-link" href="{{$crud->route}}/{{$entry->getKey()}}/download" data-toggle="tooltip"
-   title="{{__('Télécharger le quiz')}}"><i class="la la-arrow-circle-down"></i> {{__('Télécharger')}}</a>
+@once
+    @php
+        $showjs=false;
+        if($crud->getValue('seenExportJS')===null){
+        $showjs =true;
+        $crud->setValue('seenExportJS',true);
+        }
+    @endphp
+    @if($showjs)
+        <style>
+            a.exportquiz {
+                position: relative;
+            }
+
+            a.exportquiz select {
+                opacity: 0;
+                width: 100%;
+                height: 100%;
+                position: absolute;
+                top: 0;
+                left: 0;
+                cursor: pointer;
+            }
+        </style>
+        <script>
+            jQuery(document).ready(function ($) {
+                $('a.exportquiz').on('click', function () {
+                    return false;
+                });
+                $('a.exportquiz select').on('change', function () {
+                    var val = $(this).val();
+                    if (val <= 0 || val == null || val == 'null') {
+                        return;
+                    }
+                    var id = $(this).data('id');
+                    window.location = $(this).data('route') + "/" + id + "/download/" + val;
+                });
+            });
+        </script>
+    @endif
+@endonce
+
+<a class="btn btn-sm btn-link exportquiz" href="#"
+   data-toggle="tooltip"
+   title="{{__('Exporter le quiz')}}"><i class="la la-arrow-circle-down"></i> {{__('Exporter')}}
+    <select data-route="{{$crud->route}}" data-id="{{$entry->getKey()}}">
+        <option value="" selected style="display: none">--</option>
+        <option value="download">{{__('Télécharger')}}</option>
+        <option value="scormcloud">{{__('Tester sur Scorm Cloud')}}</option>
+    </select>
+</a>