]> _ Git - fluidbook-toolbox.git/commitdiff
wait #5845 @1
authorVincent Vanwaelscappel <vincent@cubedesigners.com>
Tue, 4 Apr 2023 10:23:26 +0000 (12:23 +0200)
committerVincent Vanwaelscappel <vincent@cubedesigners.com>
Tue, 4 Apr 2023 10:23:26 +0000 (12:23 +0200)
app/Http/Controllers/Admin/Operations/FluidbookCollection/PreviewOperation.php [new file with mode: 0644]
app/Http/Controllers/Admin/Operations/FluidbookPreviewOperation.php [new file with mode: 0644]
app/Http/Controllers/Admin/Operations/FluidbookPublication/PreviewOperation.php
app/Jobs/FluidbookCollectionDownload.php
app/Jobs/FluidbookCompiler.php
app/Models/FluidbookCollection.php
app/Models/FluidbookPublication.php
app/Models/Traits/CheckHash.php [new file with mode: 0644]
composer.lock
resources/pdfjs/custom/custom.js
resources/views/vendor/backpack/crud/buttons/fluidbook_collection/preview.blade.php [new file with mode: 0644]

diff --git a/app/Http/Controllers/Admin/Operations/FluidbookCollection/PreviewOperation.php b/app/Http/Controllers/Admin/Operations/FluidbookCollection/PreviewOperation.php
new file mode 100644 (file)
index 0000000..17a255d
--- /dev/null
@@ -0,0 +1,113 @@
+<?php
+
+namespace App\Http\Controllers\Admin\Operations\FluidbookCollection;
+
+use App\Http\Controllers\Admin\Operations\FluidbookPreviewOperation;
+use App\Jobs\FluidbookCollectionDownload;
+use App\Jobs\FluidbookCompiler;
+use App\Models\FluidbookCollection;
+use App\Models\FluidbookPublication;
+use App\Models\FluidbookTheme;
+use Cubist\Backpack\Http\Controllers\Base\XSendFileController;
+
+// __('!!Paramètres des fluidbooks')
+trait PreviewOperation
+{
+    use FluidbookPreviewOperation;
+
+    protected function setupPreviewRoutes($segment, $routeName, $controller)
+    {
+        $this->_baseRoutes($segment, $controller);
+    }
+
+    protected function setupPreviewDefaults()
+    {
+        $this->crud->addButtonFromView('line', 'preview', 'fluidbook_collection.preview', 'end');
+    }
+
+    protected function _preview($segment, $version, $id, $hash, $time = null, $path = null)
+    {
+        $q = request()->getQueryString();
+        if ($q) {
+            $q = '?' . $q;
+        }
+        $nointerface = !!request('nointerface', false);
+        $shortLoading = !!request('shortLoading', false);
+
+        self::_getCollectionAndTheme($id, $hash, $collection, $theme);
+
+        if (null === $time || ((null === $path || $path === 'index.html') && $time < (time() - 60) && !$nointerface && !$shortLoading)) {
+            $url = backpack_url($segment . '/preview/' . $id . '_' . $hash . '_' . time()) . '/' . $q;
+            return $this->loadingCompile($url, $id, $hash);
+        }
+
+        return $this->preview($version, $collection, $theme, $path);
+    }
+
+
+    /**
+     * @param $url string
+     * @param $id string
+     * @param $hash string
+     * @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\Routing\ResponseFactory|\Illuminate\Http\Response
+     */
+    public function loadingCompile($url, $id, $hash)
+    {
+        self::_getCollectionAndTheme($id, $hash, $collection, $theme);
+
+        $res = $this->_loadingCompile($theme, $collection->title, $url);
+        return response($res);
+    }
+
+    /**
+     * @param $version string
+     * @param $fluidbook FluidbookPublication
+     * @param $theme FluidbookTheme
+     * @param $path string
+     * @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\Routing\ResponseFactory|\Illuminate\Http\Response|object
+     * @throws \Exception
+     */
+    public function preview($version, $collection, $theme, $path = 'index.html')
+    {
+        $nointerface = !!request('nointerface', false);
+        $shortLoading = !!request('shortLoading', false);
+
+        $isScorm = $version === 'scorm';
+
+        $dest = $collection->getFinalPath($theme, $isScorm);
+        if ($path === 'index.html') {
+            $collection->incrementPreviewVisit();
+            $compiler = new FluidbookCollectionDownload($collection, 'export', backpack_user());
+            $compiler->compilePreview();
+        }
+
+        $relayPath = $dest . '/' . $path;
+        if (!file_exists($relayPath)) {
+            throw new \Exception($relayPath . ' not found');
+        }
+        return XSendFileController::sendfile($relayPath);
+    }
+
+    /**
+     * @param $id string
+     * @param $hash string
+     * @param $collection FluidbookCollection
+     * @param $theme FluidbookTheme
+     * @return void
+     */
+    protected static function _getCollectionAndTheme($id, $hash, &$collection, &$theme)
+    {
+        $collection = FluidbookCollection::find($id)->where('hash', $hash)->first();
+        if (null === $collection) {
+            abort(404);
+        }
+        foreach ($collection->publications as $pub) {
+            $firstFluidbookId = $pub['fluidbook'];
+            break;
+        }
+        $fluidbook = FluidbookPublication::find($firstFluidbookId);
+        if (!isset($theme)) {
+            $theme = $fluidbook->getTheme();
+        }
+    }
+}
diff --git a/app/Http/Controllers/Admin/Operations/FluidbookPreviewOperation.php b/app/Http/Controllers/Admin/Operations/FluidbookPreviewOperation.php
new file mode 100644 (file)
index 0000000..e50e6cb
--- /dev/null
@@ -0,0 +1,89 @@
+<?php
+
+namespace App\Http\Controllers\Admin\Operations;
+
+use App\Http\Middleware\CheckIfAdmin;
+use Cubist\Util\Graphics\Color;
+use Illuminate\Support\Facades\Route;
+
+trait FluidbookPreviewOperation
+{
+    protected function _baseRoutes($segment, $controller)
+    {
+        // Redirect to the url with a timestamp to prevent cache
+        Route::match(['get'], $segment . '/preview/{id}_{hash}', function ($id, $hash) use ($segment) {
+            return $this->_preview($segment, 'online', $id, $hash, null, 'index.html');
+        })->where('id', '([0-9]+)(-[0-9]+)?')
+            ->where('hash', '[0-9a-f]{32}')
+            ->withoutMiddleware([CheckIfAdmin::class]);
+
+        Route::match(['get'], $segment . '/preview/{id}_{hash}_{time}/{path?}', function ($id, $hash, $time, $path = 'index.html') use ($segment, $controller) {
+            return $this->_preview($segment, 'online', $id, $hash, $time, $path);
+        })->where('id', '([0-9]+)(-[0-9]+)?')
+            ->where('hash', '[0-9a-f]{32}')
+            ->where('path', '.*')
+            ->withoutMiddleware([CheckIfAdmin::class]);
+    }
+
+    protected function _loadingCompile($theme, $title, $url)
+    {
+        $bgcolor = Color::colorToCSS($theme->backgroundColor);
+        $scolor = Color::colorToCSS($theme->loadingSecColor);
+        $tcolor = $lcolor = Color::colorToCSS($theme->couleurL);
+        if ($tcolor == $bgcolor) {
+            $tcolor = $scolor;
+        }
+
+        $res = '<!DOCTYPE html><html>';
+        $res .= '<head>';
+        $res .= '<script type="text/javascript">function load(){
+    var url=\'' . $url . '\'+window.location.hash;
+    window.location=url;
+};
+</script>';
+        if (!request('shortLoading', false)) {
+            $res .= '<link href="https://fonts.googleapis.com/css2?family=Open+Sans&display=swap" rel="stylesheet">';
+            $res .= '<style>
+@keyframes loader-spin {
+  0% {
+    transform: rotate(0deg);
+  }
+  100% {
+    transform: rotate(360deg);
+  }
+}
+*{margin:0;padding:0;}
+html,body{height:100%;cursor: wait;font-family: "Open Sans", Arial;background-color:' . $bgcolor . ';}';
+            $res .= 'h2,h3{text-align:center;color:' . $tcolor . ';font-weight:400;position:relative;top:55%;}';
+            $res .= 'h2{font-size:16px;}';
+            $res .= 'h3{font-size:10px;}';
+            $res .= 'svg{position:absolute;top:calc(50% - 24px);left:calc(50% - 24px);
+        animation-name: loader-spin;
+        animation-duration: 1s;
+        animation-iteration-count: infinite;
+        animation-timing-function: linear;}';
+            $res .= '</style>';
+            $res .= '<title>' . $title . '</title>';
+            $res .= '</head>';
+            $res .= '<body onload="load();">';
+            $res .= '<svg width="48" height="48" id="interface-loader" viewBox="0 0 48 48">
+        <circle cx="24" cy="24" r="23" fill="' . $lcolor . '"></circle>
+        <circle class="animate" cx="24" cy="24" fill="none" stroke="' . $scolor . '" stroke-width="3" r="16"
+                stroke-dasharray="80 80"
+                transform="rotate(0 24 24)">
+        </circle>
+    </svg>';
+
+            $res .= '<h2>' . __('Compilation du fluidbook en cours') . '...</h2>';
+            $res .= '<h3>' . __('Cette étape ne sera pas nécessaire lorsque le fluidbook sera installé sur son emplacement définitif') . '</h3>';
+        } else {
+            $res .= '<script>load();</script>';
+            $res .= '</head>';
+            $res .= '<body>';
+        }
+
+        $res .= '</body>';
+        $res .= '</html>';
+        return $res;
+    }
+}
index 370d135ca0573d5208a84884547f10b8771bb1fb..e762e03170b77ce067ae1052f9c3fd3ac091dec9 100644 (file)
@@ -2,18 +2,19 @@
 
 namespace App\Http\Controllers\Admin\Operations\FluidbookPublication;
 
+use App\Http\Controllers\Admin\Operations\FluidbookPreviewOperation;
 use App\Http\Middleware\CheckIfAdmin;
 use App\Jobs\FluidbookCompiler;
 use App\Models\FluidbookPublication;
 use App\Models\FluidbookTheme;
-use App\Models\User;
 use Cubist\Backpack\Http\Controllers\Base\XSendFileController;
-use Cubist\Util\Graphics\Color;
 use Illuminate\Support\Facades\Auth;
 use Illuminate\Support\Facades\Route;
+
 // __('!!Paramètres des fluidbooks')
 trait PreviewOperation
 {
+    use FluidbookPreviewOperation;
     protected function setupPreviewRoutes($segment, $routeName, $controller)
     {
         Route::match(['get'], $segment . '/preview/{version}/{id}_{hash}', function ($version, $id, $hash) use ($segment) {
@@ -31,19 +32,7 @@ trait PreviewOperation
             ->whereAlpha('version')
             ->withoutMiddleware([CheckIfAdmin::class]);
 
-        // Redirect to the url with a timestamp to prevent cache
-        Route::match(['get'], $segment . '/preview/{id}_{hash}', function ($id, $hash) use ($segment) {
-            return $this->_preview($segment, 'online', $id, $hash, null, 'index.html');
-        })->where('id', '([0-9]+)(-[0-9]+)?')
-            ->where('hash', '[0-9a-f]{32}')
-            ->withoutMiddleware([CheckIfAdmin::class]);
-
-        Route::match(['get'], $segment . '/preview/{id}_{hash}_{time}/{path?}', function ($id, $hash, $time, $path = 'index.html') use ($segment, $controller) {
-            return $this->_preview($segment, 'online', $id, $hash, $time, $path);
-        })->where('id', '([0-9]+)(-[0-9]+)?')
-            ->where('hash', '[0-9a-f]{32}')
-            ->where('path', '.*')
-            ->withoutMiddleware([CheckIfAdmin::class]);
+        $this->_baseRoutes($segment, $controller);
     }
 
     protected function setupPreviewDefaults()
@@ -104,64 +93,7 @@ trait PreviewOperation
     public function loadingCompile($url, $id, $hash)
     {
         self::_getFluidbookAndTheme($id, $hash, $fluidbook, $theme);
-
-        $bgcolor = Color::colorToCSS($theme->backgroundColor);
-        $scolor = Color::colorToCSS($theme->loadingSecColor);
-        $tcolor = $lcolor = Color::colorToCSS($theme->couleurL);
-        if ($tcolor == $bgcolor) {
-            $tcolor = $scolor;
-        }
-
-        $res = '<!DOCTYPE html><html>';
-        $res .= '<head>';
-        $res .= '<script type="text/javascript">function load(){
-    var url=\'' . $url . '\'+window.location.hash;
-    window.location=url;
-};
-</script>';
-        if (!request('shortLoading', false)) {
-            $res .= '<link href="https://fonts.googleapis.com/css2?family=Open+Sans&display=swap" rel="stylesheet">';
-            $res .= '<style>
-@keyframes loader-spin {
-  0% {
-    transform: rotate(0deg);
-  }
-  100% {
-    transform: rotate(360deg);
-  }
-}
-*{margin:0;padding:0;}
-html,body{height:100%;cursor: wait;font-family: "Open Sans", Arial;background-color:' . $bgcolor . ';}';
-            $res .= 'h2,h3{text-align:center;color:' . $tcolor . ';font-weight:400;position:relative;top:55%;}';
-            $res .= 'h2{font-size:16px;}';
-            $res .= 'h3{font-size:10px;}';
-            $res .= 'svg{position:absolute;top:calc(50% - 24px);left:calc(50% - 24px);
-        animation-name: loader-spin;
-        animation-duration: 1s;
-        animation-iteration-count: infinite;
-        animation-timing-function: linear;}';
-            $res .= '</style>';
-            $res .= '<title>' . $fluidbook->title . '</title>';
-            $res .= '</head>';
-            $res .= '<body onload="load();">';
-            $res .= '<svg width="48" height="48" id="interface-loader" viewBox="0 0 48 48">
-        <circle cx="24" cy="24" r="23" fill="' . $lcolor . '"></circle>
-        <circle class="animate" cx="24" cy="24" fill="none" stroke="' . $scolor . '" stroke-width="3" r="16"
-                stroke-dasharray="80 80"
-                transform="rotate(0 24 24)">
-        </circle>
-    </svg>';
-
-            $res .= '<h2>' . __('Compilation du fluidbook en cours') . '...</h2>';
-            $res .= '<h3>' . __('Cette étape ne sera pas nécessaire lorsque le fluidbook sera installé sur son emplacement définitif') . '</h3>';
-        } else {
-            $res .= '<script>load();</script>';
-            $res .= '</head>';
-            $res .= '<body>';
-        }
-
-        $res .= '</body>';
-        $res .= '</html>';
+        $res = $this->_loadingCompile($theme, $fluidbook->title, $url);
         return response($res);
     }
 
index 341de97a4bda586784b7575edbc3b3340be5b578..e043c772a443c68ed6cc74d8fadf80a28e3959d6 100644 (file)
@@ -60,8 +60,8 @@ class FluidbookCollectionDownload extends DownloadBase
 
     protected function _compile()
     {
-        $compilepath = protected_path('collection/final/' . $this->entry->id);
-        $this->compile($compilepath);
+        $compilepath = $this->entry->getFinalPath();
+        $this->compile($compilepath, true);
         return $compilepath;
     }
 
@@ -70,10 +70,12 @@ class FluidbookCollectionDownload extends DownloadBase
      * @return void
      * @throws \Exception
      */
-    public function compile($path)
+    public function compile($path, $empty = true)
     {
         $data = $this->entry->getPageData();
-        $path = Files::emptyDir($path);
+        if ($empty) {
+            $path = Files::emptyDir($path);
+        }
 
         PHP::neverStop();
         if ($data->type === 'scorm_multilang') {
@@ -85,6 +87,13 @@ class FluidbookCollectionDownload extends DownloadBase
         }
     }
 
+    public function compilePreview()
+    {
+        $compilepath = $this->entry->getFinalPath();
+        $this->compile($compilepath, false);
+        return $compilepath;
+    }
+
     protected function getCollectionGlobalSettings()
     {
         $options = [];
index 19a08dbb4cfc7c536b52aba94aad9ab320f216ac..216e3a5426a1739b54076af577fe0d671b809e1a 100644 (file)
@@ -397,6 +397,7 @@ class FluidbookCompiler extends Base implements CompilerInterface
         return $default;
     }
 
+
     public function setSetting($key, $value)
     {
         if ($this->themeSettings->has($key)) {
@@ -671,7 +672,7 @@ class FluidbookCompiler extends Base implements CompilerInterface
         $file = $cdir . $this->fluidbookSettings->basketReferences;
         $this->config->basketReferences = ExcelToArray::excelToArrayKeyVars($file);
 
-        $this->getLinksAndRulers($links,$rulers);
+        $this->getLinksAndRulers($links, $rulers);
 
         foreach ($links as $link) {
             if ($link['type'] == '12') {
@@ -685,8 +686,9 @@ class FluidbookCompiler extends Base implements CompilerInterface
         }
     }
 
-    public function getLinksAndRulers(&$links, &$rulers){
-        Links::getLinksAndRulers($this->book_id, $links, $rulers,'latest',true);
+    public function getLinksAndRulers(&$links, &$rulers)
+    {
+        Links::getLinksAndRulers($this->book_id, $links, $rulers, 'latest', true);
     }
 
     public function writeMIFCart()
@@ -2687,21 +2689,18 @@ height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
 
 
         if ($renderer === 'pdfjs') {
-            $this->vdir->copyDirectory(WS_COMPILE_ASSETS . '/pdfjs', 'pdfjs');
+            $resource = resource_path('pdfjs/dist-min');
         } else if ($renderer === 'pdfjs-legacy') {
-            $this->vdir->copyDirectory(WS_COMPILE_ASSETS . '/pdfjs-legacy', 'pdfjs');
+            $resource = resource_path('pdfjs/legacy-min');
         }
+        $this->vdir->copyDirectory($resource, 'pdfjs');
 
         $css = '.article #sidebarContainer, .article .toolbar {display:none !important;}';
         $css .= '.article .pdfViewer{padding:0 !important;}';
         $css .= '.article #viewerContainer{top:0 !important;overflow:visible !important;}';
         $css .= '.article{--page-border:0;--page-margin:0;--body-bg-color:transparent;}';
         $css .= '.openFile,.rotateCw,.rotateCcw,.rotateCcw + .horizontalToolbarSeparator{display:none !important;}' . $this->fluidbookSettings->PDFJSCSS;
-
-        $js = 'window.addEventListener("load", function() {PDFViewerApplication.preferences.set("externalLinkTarget", 2);});';
-
-        $this->vdir->file_put_contents('pdfjs/web/viewer.css', file_get_contents(WS_COMPILE_ASSETS . '/' . $renderer . '/web/viewer.css') . $css);
-        $this->vdir->file_put_contents('pdfjs/web/viewer.js', $js . ";\n" . file_get_contents(WS_COMPILE_ASSETS . '/' . $renderer . '/web/viewer.js'));
+        $this->vdir->file_put_contents('pdfjs/web/viewer.css', file_get_contents($resource . '/web/viewer.css') . $css);
     }
 
     protected function writeJs()
index c48efe4451c4700779670b5a09759cb86a120221..d9287a5ff4699cbf388640617c0290dae657171d 100644 (file)
@@ -4,24 +4,32 @@ namespace App\Models;
 
 use App\Fields\FluidbookExportVersion;
 use App\Http\Controllers\Admin\Operations\FluidbookCollection\DownloadOperation;
+use App\Http\Controllers\Admin\Operations\FluidbookCollection\PreviewOperation;
 use App\Models\Base\ToolboxModel;
+use App\Models\Traits\CheckHash;
+use App\Slack\Slack;
 use App\SubForms\CollectionPublication;
 use App\SubForms\Fluidbook_Setting;
 use Cubist\Backpack\Magic\Fields\BunchOfFieldsMultiple;
 use Cubist\Backpack\Magic\Fields\Checkbox;
 use Cubist\Backpack\Magic\Fields\ExternalPath;
+use Cubist\Backpack\Magic\Fields\Hidden;
+use Cubist\Backpack\Magic\Fields\Integer;
 use Cubist\Backpack\Magic\Fields\SelectFromArray;
 use Cubist\Backpack\Magic\Fields\Text;
+use Cubist\Util\Files\Files;
 
 // __('!! Collections de fluidbooks')
 class FluidbookCollection extends ToolboxModel
 {
+    use CheckHash;
+
     protected $table = 'fluidbook_collection';
     protected $_options = ['name' => 'fluidbook-collection',
         'singular' => 'collection',
         'plural' => 'collections'];
 
-    protected $_operations = [DownloadOperation::class];
+    protected $_operations = [PreviewOperation::class, DownloadOperation::class];
 
     protected static $_permissionBase = 'fluidbook-collection';
 
@@ -33,17 +41,50 @@ class FluidbookCollection extends ToolboxModel
 
         $this->addOwnerField();
 
+        $this->addField('hash', Hidden::class);
         $this->addField('type', SelectFromArray::class, __('Type'), ['column' => true, 'options' => ['export' => __('Export'), 'export_multilang' => __('Export multilingue'), 'scorm_multilang' => __('SCORM multilingue')]]);
         $this->addField('locale_switch', Checkbox::class, __('Permettre le changement de langue'), ['when' => ['type' => ['scorm_multilang', 'export_multilang']], 'default' => true]);
         $this->addField('version', FluidbookExportVersion::class, __('Version'), ['when' => ['type' => 'export']]);
         $this->addField('publications', BunchOfFieldsMultiple::class, __('Publications'), ['bunch' => CollectionPublication::class, 'edit_label' => '%fluidbook > %dir']);
         $this->addField('install', ExternalPath::class, 'Installer sur un serveur externe', ['default' => '', 'fake' => true, 'translatable' => false, 'store_in' => 'settings', 'servers_model' => FluidbookExternalInstallServer::class, ['when' => ['type' => ['export', 'export_multilang']]]]);
         $this->addField('override_settings', BunchOfFieldsMultiple::class, __('Redéfinir les paramètres lors de l\'export'), ['bunch' => Fluidbook_Setting::class]);
+        $this->addField('visits_counter', Integer::class, 'Compteur de visites', ['read_only' => true, 'default' => 0, 'column' => true, 'column_label' => '<i class="las la-chart-area" title="' . __('Compteur de visites du lien de préview') . '"></i>', 'searchLogic' => false]);
     }
 
     public function getFinalPath()
     {
-        return protected_path('fluidbookcollection/final/' . $this->id);
+        return Files::mkdir(protected_path('fluidbookcollection/final/' . $this->id));
+    }
+
+    public function onSaving(): bool
+    {
+        $this->checkHash();
+        return parent::onSaving();
     }
 
+
+    public function allowsPreview()
+    {
+        return !!$this->hash;
+    }
+
+    public function incrementPreviewVisit()
+    {
+        if (!backpack_user()) {
+            $this->visits_counter++;
+            $this->saveQuietly();
+            if ($this->visits_counter % 20 === 0) {
+                /** @var \App\Models\User $owner */
+                $owner = \App\Models\User::withoutGlobalScopes()->find($this->owner);
+                $actions = [];
+
+                Slack::send(Slack::fluidbookPreviewAlertsChannel,
+                    'Collection #' . $this->id . ' : ' . $this->visits_counter . ' visites',
+                    'Le lien de preview de _' . $this->title . '_ a été utilisé à *' . $this->visits_counter . '* reprises par des visiteurs non authentifiés sur la toolbox. La collection se trouve sur le compte de ' . $owner->getNameWithCompanyAttribute() . '.',
+                    $actions,
+                    false
+                );
+            }
+        }
+    }
 }
index 2c47c7283939409a6a8369f3f9eed81d2d700224..264e89630bbed8c610b495fc9b9b998207ee072f 100644 (file)
@@ -22,6 +22,7 @@ use App\Http\Controllers\Admin\Operations\FluidbookPublication\PreviewOperation;
 use App\Http\Controllers\Admin\Operations\FluidbookPublication\StatsOperation;
 use App\Jobs\FluidbookImagesPreprocess;
 use App\Models\Base\ToolboxSettingsModel;
+use App\Models\Traits\CheckHash;
 use App\Models\Traits\PublicationSettings;
 use App\Models\Traits\SCORMVersionTrait;
 use App\Slack\Slack;
@@ -67,6 +68,7 @@ class FluidbookPublication extends ToolboxSettingsModel
 
     use PublicationSettings;
     use SCORMVersionTrait;
+    use CheckHash;
 
     public function setFields()
     {
@@ -227,12 +229,7 @@ class FluidbookPublication extends ToolboxSettingsModel
         return parent::onSaved();
     }
 
-    protected function checkHash()
-    {
-        if (!$this->hash) {
-            $this->hash = md5(uniqid('fluidbook_hash'), false);
-        }
-    }
+
 
     protected function setComposedAttributes()
     {
diff --git a/app/Models/Traits/CheckHash.php b/app/Models/Traits/CheckHash.php
new file mode 100644 (file)
index 0000000..b9ffa79
--- /dev/null
@@ -0,0 +1,13 @@
+<?php
+
+namespace App\Models\Traits;
+
+trait CheckHash
+{
+    protected function checkHash()
+    {
+        if (!$this->hash) {
+            $this->hash = md5(uniqid('fluidbook_hash'), false);
+        }
+    }
+}
index 53d4d0f311cdefda93d80ecb1076f8f8616b518b..01a454e98a479cbdf12c5ac668140eac32c4d1fb 100644 (file)
             "source": {
                 "type": "git",
                 "url": "git://git.cubedesigners.com/cubist_util.git",
-                "reference": "64323281a1250182f95e7187d13ad15239ff37a7"
+                "reference": "f1358972d45952ca2c2b1ff83aba404fabb3d076"
             },
             "dist": {
                 "type": "tar",
-                "url": "https://composer.cubedesigners.com/dist/cubist/util/cubist-util-dev-master-91a58a.tar",
-                "reference": "64323281a1250182f95e7187d13ad15239ff37a7",
-                "shasum": "ae5742d5912900a0fec3d8682eb5cceec2d6956f"
+                "url": "https://composer.cubedesigners.com/dist/cubist/util/cubist-util-dev-master-49ab63.tar",
+                "reference": "f1358972d45952ca2c2b1ff83aba404fabb3d076",
+                "shasum": "fdec46a6c1c5f65735e4432a0bc30afccca55136"
             },
             "require": {
                 "cubist/net": "dev-master",
                 }
             ],
             "description": "Utilities class",
-            "time": "2023-03-22T17:16:53+00:00"
+            "time": "2023-03-31T15:50:03+00:00"
         },
         {
             "name": "cviebrock/eloquent-sluggable",
             "source": {
                 "type": "git",
                 "url": "git://git.cubedesigners.com/fluidbook_tools.git",
-                "reference": "33472af7df33e6095bf65bdcefbd1162207ab3a6"
+                "reference": "00cdc67442f8c8bab53ec58593baa3adf705bd74"
             },
             "dist": {
                 "type": "tar",
-                "url": "https://composer.cubedesigners.com/dist/fluidbook/tools/fluidbook-tools-dev-master-0eb654.tar",
-                "reference": "33472af7df33e6095bf65bdcefbd1162207ab3a6",
-                "shasum": "bad96b39e6fa52960158a1ef50a9da61dd909b5f"
+                "url": "https://composer.cubedesigners.com/dist/fluidbook/tools/fluidbook-tools-dev-master-a050b1.tar",
+                "reference": "00cdc67442f8c8bab53ec58593baa3adf705bd74",
+                "shasum": "99be90d75de1395ab7c1c4410ba023998730f2cb"
             },
             "require": {
                 "barryvdh/laravel-debugbar": "^3.6",
                 }
             ],
             "description": "Fluidbook Tools",
-            "time": "2023-03-30T09:40:52+00:00"
+            "time": "2023-04-03T13:12:37+00:00"
         },
         {
             "name": "genealabs/laravel-model-caching",
         },
         {
             "name": "php-http/discovery",
-            "version": "1.15.2",
+            "version": "1.15.3",
             "source": {
                 "type": "git",
                 "url": "https://github.com/php-http/discovery.git",
-                "reference": "5cc428320191ac1d0b6520034c2dc0698628ced5"
+                "reference": "3ccd28dd9fb34b52db946abea1b538568e34eae8"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/php-http/discovery/zipball/5cc428320191ac1d0b6520034c2dc0698628ced5",
-                "reference": "5cc428320191ac1d0b6520034c2dc0698628ced5",
+                "url": "https://api.github.com/repos/php-http/discovery/zipball/3ccd28dd9fb34b52db946abea1b538568e34eae8",
+                "reference": "3ccd28dd9fb34b52db946abea1b538568e34eae8",
                 "shasum": ""
             },
             "require": {
             ],
             "support": {
                 "issues": "https://github.com/php-http/discovery/issues",
-                "source": "https://github.com/php-http/discovery/tree/1.15.2"
+                "source": "https://github.com/php-http/discovery/tree/1.15.3"
             },
-            "time": "2023-02-11T08:28:41+00:00"
+            "time": "2023-03-31T14:40:37+00:00"
         },
         {
             "name": "php-http/httplug",
         },
         {
             "name": "symfony/console",
-            "version": "v5.4.21",
+            "version": "v5.4.22",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/console.git",
-                "reference": "c77433ddc6cdc689caf48065d9ea22ca0853fbd9"
+                "reference": "3cd51fd2e6c461ca678f84d419461281bd87a0a8"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/console/zipball/c77433ddc6cdc689caf48065d9ea22ca0853fbd9",
-                "reference": "c77433ddc6cdc689caf48065d9ea22ca0853fbd9",
+                "url": "https://api.github.com/repos/symfony/console/zipball/3cd51fd2e6c461ca678f84d419461281bd87a0a8",
+                "reference": "3cd51fd2e6c461ca678f84d419461281bd87a0a8",
                 "shasum": ""
             },
             "require": {
             "homepage": "https://symfony.com",
             "keywords": [
                 "cli",
-                "command line",
+                "command-line",
                 "console",
                 "terminal"
             ],
             "support": {
-                "source": "https://github.com/symfony/console/tree/v5.4.21"
+                "source": "https://github.com/symfony/console/tree/v5.4.22"
             },
             "funding": [
                 {
                     "type": "tidelift"
                 }
             ],
-            "time": "2023-02-25T16:59:41+00:00"
+            "time": "2023-03-25T09:27:28+00:00"
         },
         {
             "name": "symfony/css-selector",
         },
         {
             "name": "symfony/event-dispatcher",
-            "version": "v6.2.7",
+            "version": "v6.2.8",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/event-dispatcher.git",
-                "reference": "404b307de426c1c488e5afad64403e5f145e82a5"
+                "reference": "04046f35fd7d72f9646e721fc2ecb8f9c67d3339"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/404b307de426c1c488e5afad64403e5f145e82a5",
-                "reference": "404b307de426c1c488e5afad64403e5f145e82a5",
+                "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/04046f35fd7d72f9646e721fc2ecb8f9c67d3339",
+                "reference": "04046f35fd7d72f9646e721fc2ecb8f9c67d3339",
                 "shasum": ""
             },
             "require": {
             "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them",
             "homepage": "https://symfony.com",
             "support": {
-                "source": "https://github.com/symfony/event-dispatcher/tree/v6.2.7"
+                "source": "https://github.com/symfony/event-dispatcher/tree/v6.2.8"
             },
             "funding": [
                 {
                     "type": "tidelift"
                 }
             ],
-            "time": "2023-02-14T08:44:56+00:00"
+            "time": "2023-03-20T16:06:02+00:00"
         },
         {
             "name": "symfony/event-dispatcher-contracts",
         },
         {
             "name": "symfony/http-client",
-            "version": "v6.2.7",
+            "version": "v6.2.8",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/http-client.git",
-                "reference": "0a5be6cbc570ae23b51b49d67341f378629d78e4"
+                "reference": "66391ba3a8862c560e1d9134c96d9bd2a619b477"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/http-client/zipball/0a5be6cbc570ae23b51b49d67341f378629d78e4",
-                "reference": "0a5be6cbc570ae23b51b49d67341f378629d78e4",
+                "url": "https://api.github.com/repos/symfony/http-client/zipball/66391ba3a8862c560e1d9134c96d9bd2a619b477",
+                "reference": "66391ba3a8862c560e1d9134c96d9bd2a619b477",
                 "shasum": ""
             },
             "require": {
             ],
             "description": "Provides powerful methods to fetch HTTP resources synchronously or asynchronously",
             "homepage": "https://symfony.com",
+            "keywords": [
+                "http"
+            ],
             "support": {
-                "source": "https://github.com/symfony/http-client/tree/v6.2.7"
+                "source": "https://github.com/symfony/http-client/tree/v6.2.8"
             },
             "funding": [
                 {
                     "type": "tidelift"
                 }
             ],
-            "time": "2023-02-21T10:54:55+00:00"
+            "time": "2023-03-31T09:14:44+00:00"
         },
         {
             "name": "symfony/http-client-contracts",
         },
         {
             "name": "symfony/http-foundation",
-            "version": "v5.4.21",
+            "version": "v5.4.22",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/http-foundation.git",
-                "reference": "3bb6ee5582366c4176d5ce596b380117c8200bbf"
+                "reference": "05cd1acdd0e3ce8473aaba1d86c188321d85f313"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/http-foundation/zipball/3bb6ee5582366c4176d5ce596b380117c8200bbf",
-                "reference": "3bb6ee5582366c4176d5ce596b380117c8200bbf",
+                "url": "https://api.github.com/repos/symfony/http-foundation/zipball/05cd1acdd0e3ce8473aaba1d86c188321d85f313",
+                "reference": "05cd1acdd0e3ce8473aaba1d86c188321d85f313",
                 "shasum": ""
             },
             "require": {
             "description": "Defines an object-oriented layer for the HTTP specification",
             "homepage": "https://symfony.com",
             "support": {
-                "source": "https://github.com/symfony/http-foundation/tree/v5.4.21"
+                "source": "https://github.com/symfony/http-foundation/tree/v5.4.22"
             },
             "funding": [
                 {
                     "type": "tidelift"
                 }
             ],
-            "time": "2023-02-17T21:35:35+00:00"
+            "time": "2023-03-28T07:28:17+00:00"
         },
         {
             "name": "symfony/http-kernel",
-            "version": "v5.4.21",
+            "version": "v5.4.22",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/http-kernel.git",
-                "reference": "09c19fc7e4218fbcf73fe0330eea38d66064b775"
+                "reference": "2d3a8be2c756353627398827c409af6f126c096d"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/http-kernel/zipball/09c19fc7e4218fbcf73fe0330eea38d66064b775",
-                "reference": "09c19fc7e4218fbcf73fe0330eea38d66064b775",
+                "url": "https://api.github.com/repos/symfony/http-kernel/zipball/2d3a8be2c756353627398827c409af6f126c096d",
+                "reference": "2d3a8be2c756353627398827c409af6f126c096d",
                 "shasum": ""
             },
             "require": {
             "description": "Provides a structured process for converting a Request into a Response",
             "homepage": "https://symfony.com",
             "support": {
-                "source": "https://github.com/symfony/http-kernel/tree/v5.4.21"
+                "source": "https://github.com/symfony/http-kernel/tree/v5.4.22"
             },
             "funding": [
                 {
                     "type": "tidelift"
                 }
             ],
-            "time": "2023-02-28T13:19:09+00:00"
+            "time": "2023-03-31T11:54:37+00:00"
         },
         {
             "name": "symfony/mime",
         },
         {
             "name": "symfony/process",
-            "version": "v5.4.21",
+            "version": "v5.4.22",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/process.git",
-                "reference": "d4ce417ebcb0b7d090b4c178ed6d3accc518e8bd"
+                "reference": "4b850da0cc3a2a9181c1ed407adbca4733dc839b"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/process/zipball/d4ce417ebcb0b7d090b4c178ed6d3accc518e8bd",
-                "reference": "d4ce417ebcb0b7d090b4c178ed6d3accc518e8bd",
+                "url": "https://api.github.com/repos/symfony/process/zipball/4b850da0cc3a2a9181c1ed407adbca4733dc839b",
+                "reference": "4b850da0cc3a2a9181c1ed407adbca4733dc839b",
                 "shasum": ""
             },
             "require": {
             "description": "Executes commands in sub-processes",
             "homepage": "https://symfony.com",
             "support": {
-                "source": "https://github.com/symfony/process/tree/v5.4.21"
+                "source": "https://github.com/symfony/process/tree/v5.4.22"
             },
             "funding": [
                 {
                     "type": "tidelift"
                 }
             ],
-            "time": "2023-02-21T19:46:44+00:00"
+            "time": "2023-03-06T21:29:33+00:00"
         },
         {
             "name": "symfony/routing",
-            "version": "v5.4.21",
+            "version": "v5.4.22",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/routing.git",
-                "reference": "2ea0f3049076e8ef96eab203a809d6b2332f0361"
+                "reference": "c2ac11eb34947999b7c38fb4c835a57306907e6d"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/routing/zipball/2ea0f3049076e8ef96eab203a809d6b2332f0361",
-                "reference": "2ea0f3049076e8ef96eab203a809d6b2332f0361",
+                "url": "https://api.github.com/repos/symfony/routing/zipball/c2ac11eb34947999b7c38fb4c835a57306907e6d",
+                "reference": "c2ac11eb34947999b7c38fb4c835a57306907e6d",
                 "shasum": ""
             },
             "require": {
                 "url"
             ],
             "support": {
-                "source": "https://github.com/symfony/routing/tree/v5.4.21"
+                "source": "https://github.com/symfony/routing/tree/v5.4.22"
             },
             "funding": [
                 {
                     "type": "tidelift"
                 }
             ],
-            "time": "2023-02-16T09:33:00+00:00"
+            "time": "2023-03-14T14:59:20+00:00"
         },
         {
             "name": "symfony/serializer",
-            "version": "v6.2.7",
+            "version": "v6.2.8",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/serializer.git",
-                "reference": "df9599873fdc2540e6f4291f49be4fcc167e9cbf"
+                "reference": "db9d36470bf0990990fda9320b8b001bb582f075"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/serializer/zipball/df9599873fdc2540e6f4291f49be4fcc167e9cbf",
-                "reference": "df9599873fdc2540e6f4291f49be4fcc167e9cbf",
+                "url": "https://api.github.com/repos/symfony/serializer/zipball/db9d36470bf0990990fda9320b8b001bb582f075",
+                "reference": "db9d36470bf0990990fda9320b8b001bb582f075",
                 "shasum": ""
             },
             "require": {
             "conflict": {
                 "doctrine/annotations": "<1.12",
                 "phpdocumentor/reflection-docblock": "<3.2.2",
-                "phpdocumentor/type-resolver": "<1.4.0|>=1.7.0",
+                "phpdocumentor/type-resolver": "<1.4.0",
                 "symfony/dependency-injection": "<5.4",
                 "symfony/property-access": "<5.4",
                 "symfony/property-info": "<5.4",
             "description": "Handles serializing and deserializing data structures, including object graphs, into array structures or other formats like XML and JSON.",
             "homepage": "https://symfony.com",
             "support": {
-                "source": "https://github.com/symfony/serializer/tree/v6.2.7"
+                "source": "https://github.com/symfony/serializer/tree/v6.2.8"
             },
             "funding": [
                 {
                     "type": "tidelift"
                 }
             ],
-            "time": "2023-02-24T10:42:00+00:00"
+            "time": "2023-03-31T09:14:44+00:00"
         },
         {
             "name": "symfony/service-contracts",
         },
         {
             "name": "symfony/string",
-            "version": "v6.2.7",
+            "version": "v6.2.8",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/string.git",
-                "reference": "67b8c1eec78296b85dc1c7d9743830160218993d"
+                "reference": "193e83bbd6617d6b2151c37fff10fa7168ebddef"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/string/zipball/67b8c1eec78296b85dc1c7d9743830160218993d",
-                "reference": "67b8c1eec78296b85dc1c7d9743830160218993d",
+                "url": "https://api.github.com/repos/symfony/string/zipball/193e83bbd6617d6b2151c37fff10fa7168ebddef",
+                "reference": "193e83bbd6617d6b2151c37fff10fa7168ebddef",
                 "shasum": ""
             },
             "require": {
                 "utf8"
             ],
             "support": {
-                "source": "https://github.com/symfony/string/tree/v6.2.7"
+                "source": "https://github.com/symfony/string/tree/v6.2.8"
             },
             "funding": [
                 {
                     "type": "tidelift"
                 }
             ],
-            "time": "2023-02-24T10:42:00+00:00"
+            "time": "2023-03-20T16:06:02+00:00"
         },
         {
             "name": "symfony/translation",
-            "version": "v6.2.7",
+            "version": "v6.2.8",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/translation.git",
-                "reference": "90db1c6138c90527917671cd9ffa9e8b359e3a73"
+                "reference": "817535dbb1721df8b3a8f2489dc7e50bcd6209b5"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/translation/zipball/90db1c6138c90527917671cd9ffa9e8b359e3a73",
-                "reference": "90db1c6138c90527917671cd9ffa9e8b359e3a73",
+                "url": "https://api.github.com/repos/symfony/translation/zipball/817535dbb1721df8b3a8f2489dc7e50bcd6209b5",
+                "reference": "817535dbb1721df8b3a8f2489dc7e50bcd6209b5",
                 "shasum": ""
             },
             "require": {
             "description": "Provides tools to internationalize your application",
             "homepage": "https://symfony.com",
             "support": {
-                "source": "https://github.com/symfony/translation/tree/v6.2.7"
+                "source": "https://github.com/symfony/translation/tree/v6.2.8"
             },
             "funding": [
                 {
                     "type": "tidelift"
                 }
             ],
-            "time": "2023-02-24T10:42:00+00:00"
+            "time": "2023-03-31T09:14:44+00:00"
         },
         {
             "name": "symfony/translation-contracts",
         },
         {
             "name": "symfony/var-dumper",
-            "version": "v5.4.21",
+            "version": "v5.4.22",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/var-dumper.git",
-                "reference": "6c5ac3a1be8b849d59a1a77877ee110e1b55eb74"
+                "reference": "e2edac9ce47e6df07e38143c7cfa6bdbc1a6dcc4"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/var-dumper/zipball/6c5ac3a1be8b849d59a1a77877ee110e1b55eb74",
-                "reference": "6c5ac3a1be8b849d59a1a77877ee110e1b55eb74",
+                "url": "https://api.github.com/repos/symfony/var-dumper/zipball/e2edac9ce47e6df07e38143c7cfa6bdbc1a6dcc4",
+                "reference": "e2edac9ce47e6df07e38143c7cfa6bdbc1a6dcc4",
                 "shasum": ""
             },
             "require": {
                 "dump"
             ],
             "support": {
-                "source": "https://github.com/symfony/var-dumper/tree/v5.4.21"
+                "source": "https://github.com/symfony/var-dumper/tree/v5.4.22"
             },
             "funding": [
                 {
                     "type": "tidelift"
                 }
             ],
-            "time": "2023-02-23T10:00:28+00:00"
+            "time": "2023-03-25T09:27:28+00:00"
         },
         {
             "name": "symfony/yaml",
         },
         {
             "name": "phpdocumentor/type-resolver",
-            "version": "1.6.2",
+            "version": "1.7.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/phpDocumentor/TypeResolver.git",
-                "reference": "48f445a408c131e38cab1c235aa6d2bb7a0bb20d"
+                "reference": "dfc078e8af9c99210337325ff5aa152872c98714"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/48f445a408c131e38cab1c235aa6d2bb7a0bb20d",
-                "reference": "48f445a408c131e38cab1c235aa6d2bb7a0bb20d",
+                "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/dfc078e8af9c99210337325ff5aa152872c98714",
+                "reference": "dfc078e8af9c99210337325ff5aa152872c98714",
                 "shasum": ""
             },
             "require": {
+                "doctrine/deprecations": "^1.0",
                 "php": "^7.4 || ^8.0",
-                "phpdocumentor/reflection-common": "^2.0"
+                "phpdocumentor/reflection-common": "^2.0",
+                "phpstan/phpdoc-parser": "^1.13"
             },
             "require-dev": {
                 "ext-tokenizer": "*",
+                "phpbench/phpbench": "^1.2",
                 "phpstan/extension-installer": "^1.1",
                 "phpstan/phpstan": "^1.8",
                 "phpstan/phpstan-phpunit": "^1.1",
             "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names",
             "support": {
                 "issues": "https://github.com/phpDocumentor/TypeResolver/issues",
-                "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.6.2"
+                "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.7.1"
+            },
+            "time": "2023-03-27T19:02:04+00:00"
+        },
+        {
+            "name": "phpstan/phpdoc-parser",
+            "version": "1.16.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/phpstan/phpdoc-parser.git",
+                "reference": "e27e92d939e2e3636f0a1f0afaba59692c0bf571"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/e27e92d939e2e3636f0a1f0afaba59692c0bf571",
+                "reference": "e27e92d939e2e3636f0a1f0afaba59692c0bf571",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.2 || ^8.0"
+            },
+            "require-dev": {
+                "php-parallel-lint/php-parallel-lint": "^1.2",
+                "phpstan/extension-installer": "^1.0",
+                "phpstan/phpstan": "^1.5",
+                "phpstan/phpstan-phpunit": "^1.1",
+                "phpstan/phpstan-strict-rules": "^1.0",
+                "phpunit/phpunit": "^9.5",
+                "symfony/process": "^5.2"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "PHPStan\\PhpDocParser\\": [
+                        "src/"
+                    ]
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "description": "PHPDoc parser with support for nullable, intersection and generic types",
+            "support": {
+                "issues": "https://github.com/phpstan/phpdoc-parser/issues",
+                "source": "https://github.com/phpstan/phpdoc-parser/tree/1.16.1"
             },
-            "time": "2022-10-14T12:47:21+00:00"
+            "time": "2023-02-07T18:11:17+00:00"
         },
         {
             "name": "phpunit/php-code-coverage",
index bb13823c9b973efd36219f7d502b3ab08577f044..12b4809811858c6bd97ab5249939a23d7e3a61bb 100644 (file)
@@ -1,3 +1,3 @@
 $(function () {
-
+    PDFViewerApplication.preferences.set("externalLinkTarget", 2);
 });
diff --git a/resources/views/vendor/backpack/crud/buttons/fluidbook_collection/preview.blade.php b/resources/views/vendor/backpack/crud/buttons/fluidbook_collection/preview.blade.php
new file mode 100644 (file)
index 0000000..fb11ac0
--- /dev/null
@@ -0,0 +1,17 @@
+{{-- __('!! Paramètres des fluidbooks') --}}
+@php
+    $actions=['preview'=>['label'=>__('Version online'),'url'=>$crud->route.'/preview/'.$entry->getKey().'_'.$entry->hash,'target'=>'_blank']];
+@endphp
+
+@if($entry->allowsPreview())
+    <a class="btn btn-sm btn-link" target="{!! $actions['preview']['target'] !!}"
+       href="{!! $actions['preview']['url'] !!}"
+       @if(count($actions)>1)
+           data-context-actions="{{json_encode($actions)}}"
+       data-context-route="{{$crud->route}}/$id/edit/$action"
+       data-context-id="{{$entry->getKey()}}"
+       @endif
+       data-toggle="tooltip"
+       title="{{__('Voir la collection')}}"><i class="las la-eye"></i> {{__('Voir')}}
+    </a>
+@endif