]> _ Git - fluidbook-toolbox.git/commitdiff
wait #4211 @1
authorVincent Vanwaelscappel <vincent@cubedesigners.com>
Wed, 14 Jun 2023 07:19:50 +0000 (09:19 +0200)
committerVincent Vanwaelscappel <vincent@cubedesigners.com>
Wed, 14 Jun 2023 07:19:50 +0000 (09:19 +0200)
.env.dev
.env.prod
app/Fields/FluidbookTTSVoice.php [new file with mode: 0644]
app/Http/Controllers/Admin/Operations/Tools/TextToSpeech.php
app/Models/Traits/PublicationSettings.php

index 10b54ac7636d6e4a8bba55528cc1a90bdb539fa7..b11fb9d34ccec66500409d768740436b5c2f436d 100644 (file)
--- a/.env.dev
+++ b/.env.dev
@@ -53,6 +53,8 @@ SCORM_CLOUD_APP_ID=VAAROP2RFR
 SCORM_CLOUD_SECRET_KEY=7PHpD9pQk4aIjUnlKgF8s89CGlhkIucZHJ3zMXuQ
 SCORM_CLOUD_PREFIX=toolbox_dev_
 
+AZURE_API_KEY=28fdfcdcc7f141b29cd9db4afc5779c5
+
 BACKPACK_LOCALES=en,fr
 TIMEZONE=Europe/Paris
 
index 0842fbea9535a30812ea35cbb4e08f0df85fa781..abf8430ca437fc3985014d09c011a17db304da6a 100644 (file)
--- a/.env.prod
+++ b/.env.prod
@@ -52,6 +52,8 @@ SCORM_CLOUD_APP_ID=VAAROP2RFR
 SCORM_CLOUD_SECRET_KEY=7PHpD9pQk4aIjUnlKgF8s89CGlhkIucZHJ3zMXuQ
 SCORM_CLOUD_PREFIX=toolbox_
 
+AZURE_API_KEY=28fdfcdcc7f141b29cd9db4afc5779c5
+
 BACKPACK_LOCALES=en,fr
 TIMEZONE=Europe/Paris
 
diff --git a/app/Fields/FluidbookTTSVoice.php b/app/Fields/FluidbookTTSVoice.php
new file mode 100644 (file)
index 0000000..bf8d18e
--- /dev/null
@@ -0,0 +1,22 @@
+<?php
+
+namespace App\Fields;
+
+use Cubist\Azure\TTS\Api;
+use Cubist\Backpack\Magic\Fields\SelectFromArray;
+
+class FluidbookTTSVoice extends SelectFromArray
+{
+    public function getOptions()
+    {
+        $api = new Api(env('AZURE_API_KEY'));
+        $voices = $api->listVoices(true);
+        $res[''] = '';
+        foreach ($voices as $key => $voice) {
+            $res['azuretts:' . $voice['Locale'] . '/' . $voice['Gender'] . '/' . $voice['ShortName']] = 'Azure ' . $voice['DisplayName'] . ' (' . $voice['Locale'] . ')';
+        }
+        return array_merge($res, ['festival:voice_cmu_us_slt_arctic_hts' => 'Festival (en-US)',
+            'readspeaker:Sophie/en_us' => 'ReadSpeaker Sophie (en-US)',
+            'readspeaker:Marc/en_us' => 'ReadSpeaker Marc (en-US)']);
+    }
+}
index a55185c2afc444dc65e675e37b9e6b2cf558f484..da5ff13d0437fd03064965936ee8f317201c458c 100644 (file)
@@ -37,7 +37,7 @@ trait TextToSpeech
      */
     protected function _api()
     {
-        return new Api('28fdfcdcc7f141b29cd9db4afc5779c5');
+        return new Api(env('AZURE_API_KEY'));
     }
 
 }
index 9f7b5692d2fe5a9e8fb1c72e3ceb3a2ed3d0f57d..e9bffbb91a1460c73ca84f189d2c3670233821b6 100644 (file)
@@ -4,6 +4,7 @@ namespace App\Models\Traits;
 
 use App\Fields\FluidbookDevelopmentVersion;
 use App\Fields\FluidbookSignature;
+use App\Fields\FluidbookTTSVoice;
 use App\Fields\SCORMVersion;
 use App\Models\FluidbookExternalInstallServer;
 use Cubist\Backpack\Magic\Fields\Checkbox;
@@ -32,6 +33,18 @@ use Cubist\Backpack\Magic\Fields\Textarea;
 trait PublicationSettings
 {
     protected static $acceptImages = ['.svg', '.jpg', '.jpeg', '.png', '.gif'];
+    protected static $acceptXLSX = ['.xlsx'];
+    protected static $acceptCSS = ['.css'];
+    protected static $acceptAudio = ['.mp3', '.wav'];
+    protected static $acceptTabs = ['.zip', '.svg'];
+    protected static $acceptSVG = ['.svg'];
+    protected static $acceptVector = ['.ai', '.eps'];
+    protected static $acceptHTML = ['.html'];
+    protected static $acceptXML = ['.xml'];
+    protected static $acceptProductList = ['.xml', '.xlsx'];
+    protected static $acceptPDF = ['.pdf'];
+    protected static $acceptZip = ['.zip'];
+
 
     public function setSettingsFields()
     {
@@ -53,7 +66,6 @@ trait PublicationSettings
         $this->_general();
     }
 
-
     protected function _mainFeatures()
     {
         $this->addSettingField('bigsection_main_features', FormSuperSection::class, $this->__('Fonctionnalités'));
@@ -126,6 +138,18 @@ trait PublicationSettings
         $this->_demoLink();
     }
 
+    protected function _stats()
+    {
+        $this->addSettingField('bigsection_stats', FormSuperSection::class, $this->__('Statistiques et vie privée'));
+        $this->_statsFluidbook();
+        $this->_statsGoogleAnalytics();
+        $this->_statsGoogleTagManager();
+        $this->_statsTagCommander();
+        $this->_statsPiano();
+        $this->_statsCustom();
+        $this->_privacy();
+    }
+
     /**
      * @return array[]
      */
@@ -326,9 +350,7 @@ trait PublicationSettings
         ]);
         $this->addSettingField('seoAdvanced', FilesOrURL::class, $this->__('Données SEO par page'), [
             'v2' => '{"type":"freefile","default":"","editable":true,"label":"\\u00a7!\\u00a7Donn\\u00e9es SEO par page!\\u00a7!","grade":5,"fileFilter":{"name":"\\u00a7!\\u00a7Fichier Excel!\\u00a7! (.xlsx)","extensions":"*.xlsx"}}',
-            'accept' => [
-                0 => '.xlsx',
-            ],
+            'accept' => self::$acceptXLSX,
         ]);
 
     }
@@ -578,131 +600,34 @@ trait PublicationSettings
         $this->addSettingField('navExtraTooltip', LongText::class, $this->__('Infobulle ou label'), [
             'v2' => '{"type":"text","default":"","editable":true,"label":"\\u00a7!\\u00a7Infobulle ou label!\\u00a7!"}',
         ]);
-        $this->addSettingField('', FormSeparator::class);
-        $this->addSettingField('navExtraIcon1', FilesOrURL::class, $this->__('Icône supplémentaire') . ' 1', [
-            'v2' => '{"type":"freefile","default":"","editable":true,"label":"\\u00a7!\\u00a7Ic\\u00f4ne suppl\\u00e9mentaire!\\u00a7! 1","fileFilter":{"name":"\\u00a7!\\u00a7Images!\\u00a7! (*.svg, *.jpg, *.png, *.gif)","extensions":"*.svg;*.jpg;*.png;*.gif"}}',
-            'accept' => self::$acceptImages,
-        ]);
-        $this->addSettingField('navExtraType1', SelectFromArray::class, $this->__('Type'), [
-            'v2' => '{"type":"combo","datas":{"\\u00a7!\\u00a7Ic\\u00f4ne + Label!\\u00a7!":"icon","\\u00a7!\\u00a7Image!\\u00a7!":"image"},"default":"icon","editable":true,"label":"\\u00a7!\\u00a7Type!\\u00a7!"}',
-            'options' => [
-                'icon' => $this->__('Icône + Label'),
-                'image' => $this->__('Image'),
-            ],
-            'default' => 'icon',
-        ]);
-        $this->addSettingField('navExtraLink1', LongText::class, $this->__('URL') . ' 1', [
-            'v2' => '{"type":"text","default":"","editable":true,"label":"\\u00a7!\\u00a7URL!\\u00a7! 1"}',
-        ]);
-        $this->addSettingField('navExtraVisibility1', SelectFromArray::class, $this->__('Visible') . ' 1', [
-            'v2' => '{"type":"combo","datas":{"\\u00a7!\\u00a7Navigation horizontale!\\u00a7!":"horizontal","\\u00a7!\\u00a7Navigation burger!\\u00a7!":"burger","\\u00a7!\\u00a7Navigations horizontale et burger!\\u00a7!":"both"},"default":"both","editable":true,"label":"\\u00a7!\\u00a7Visible!\\u00a7! 1"}',
-            'options' => [
-                'horizontal' => $this->__('Navigation horizontale'),
-                'burger' => $this->__('Navigation burger'),
-                'both' => $this->__('Navigations horizontale et burger'),
-            ],
-            'default' => 'both',
-        ]);
-        $this->addSettingField('', FormSeparator::class);
-        $this->addSettingField('navExtraIcon2', FilesOrURL::class, $this->__('Icône supplémentaire') . ' 2', [
-            'v2' => '{"type":"freefile","default":"","editable":true,"label":"\\u00a7!\\u00a7Ic\\u00f4ne suppl\\u00e9mentaire!\\u00a7! 2","fileFilter":{"name":"\\u00a7!\\u00a7Images!\\u00a7! (*.svg, *.jpg, *.png, *.gif)","extensions":"*.svg;*.jpg;*.png;*.gif"}}',
-            'accept' => self::$acceptImages,
-        ]);
-        $this->addSettingField('navExtraType2', SelectFromArray::class, $this->__('Type'), [
-            'v2' => '{"type":"combo","datas":{"\\u00a7!\\u00a7Ic\\u00f4ne + Label!\\u00a7!":"icon","\\u00a7!\\u00a7Image!\\u00a7!":"image"},"default":"icon","editable":true,"label":"\\u00a7!\\u00a7Type!\\u00a7!"}',
-            'options' => [
-                'icon' => $this->__('Icône + Label'),
-                'image' => $this->__('Image'),
-            ],
-            'default' => 'icon',
-        ]);
-        $this->addSettingField('navExtraLink2', LongText::class, $this->__('URL') . ' 2', [
-            'v2' => '{"type":"text","default":"","editable":true,"label":"\\u00a7!\\u00a7URL!\\u00a7! 2"}',
-        ]);
-        $this->addSettingField('navExtraVisibility2', SelectFromArray::class, $this->__('Visible') . ' 2', [
-            'v2' => '{"type":"combo","datas":{"\\u00a7!\\u00a7Navigation horizontale!\\u00a7!":"horizontal","\\u00a7!\\u00a7Navigation burger!\\u00a7!":"burger","\\u00a7!\\u00a7Navigations horizontale et burger!\\u00a7!":"both"},"default":"both","editable":true,"label":"\\u00a7!\\u00a7Visible!\\u00a7! 2"}',
-            'options' => [
-                'horizontal' => $this->__('Navigation horizontale'),
-                'burger' => $this->__('Navigation burger'),
-                'both' => $this->__('Navigations horizontale et burger'),
-            ],
-            'default' => 'both',
-        ]);
-        $this->addSettingField('', FormSeparator::class);
-        $this->addSettingField('navExtraIcon3', FilesOrURL::class, $this->__('Icône supplémentaire') . ' 3', [
-            'v2' => '{"type":"freefile","default":"","editable":true,"label":"\\u00a7!\\u00a7Ic\\u00f4ne suppl\\u00e9mentaire!\\u00a7! 3","fileFilter":{"name":"\\u00a7!\\u00a7Images!\\u00a7! (*.svg, *.jpg, *.png, *.gif)","extensions":"*.svg;*.jpg;*.png;*.gif"}}',
-            'accept' => self::$acceptImages,
-        ]);
-        $this->addSettingField('navExtraType3', SelectFromArray::class, $this->__('Type'), [
-            'v2' => '{"type":"combo","datas":{"\\u00a7!\\u00a7Ic\\u00f4ne + Label!\\u00a7!":"icon","\\u00a7!\\u00a7Image!\\u00a7!":"image"},"default":"icon","editable":true,"label":"\\u00a7!\\u00a7Type!\\u00a7!"}',
-            'options' => [
-                'icon' => $this->__('Icône + Label'),
-                'image' => $this->__('Image'),
-            ],
-            'default' => 'icon',
-        ]);
-        $this->addSettingField('navExtraLink3', LongText::class, $this->__('URL') . ' 3', [
-            'v2' => '{"type":"text","default":"","editable":true,"label":"\\u00a7!\\u00a7URL!\\u00a7! 3"}',
-        ]);
-        $this->addSettingField('navExtraVisibility3', SelectFromArray::class, $this->__('Visible') . ' 3', [
-            'v2' => '{"type":"combo","datas":{"\\u00a7!\\u00a7Navigation horizontale!\\u00a7!":"horizontal","\\u00a7!\\u00a7Navigation burger!\\u00a7!":"burger","\\u00a7!\\u00a7Navigations horizontale et burger!\\u00a7!":"both"},"default":"both","editable":true,"label":"\\u00a7!\\u00a7Visible!\\u00a7! 3"}',
-            'options' => [
-                'horizontal' => $this->__('Navigation horizontale'),
-                'burger' => $this->__('Navigation burger'),
-                'both' => $this->__('Navigations horizontale et burger'),
-            ],
-            'default' => 'both',
-        ]);
-        $this->addSettingField('', FormSeparator::class);
-        $this->addSettingField('navExtraIcon4', FilesOrURL::class, $this->__('Icône supplémentaire') . ' 4', [
-            'v2' => '{"type":"freefile","default":"","editable":true,"label":"\\u00a7!\\u00a7Ic\\u00f4ne suppl\\u00e9mentaire!\\u00a7! 4","fileFilter":{"name":"\\u00a7!\\u00a7Images!\\u00a7! (*.svg, *.jpg, *.png, *.gif)","extensions":"*.svg;*.jpg;*.png;*.gif"}}',
-            'accept' => self::$acceptImages,
-        ]);
-        $this->addSettingField('navExtraType4', SelectFromArray::class, $this->__('Type'), [
-            'v2' => '{"type":"combo","datas":{"\\u00a7!\\u00a7Ic\\u00f4ne + Label!\\u00a7!":"icon","\\u00a7!\\u00a7Image!\\u00a7!":"image"},"default":"icon","editable":true,"label":"\\u00a7!\\u00a7Type!\\u00a7!"}',
-            'options' => [
-                'icon' => $this->__('Icône + Label'),
-                'image' => $this->__('Image'),
-            ],
-            'default' => 'icon',
-        ]);
-        $this->addSettingField('navExtraLink4', LongText::class, $this->__('URL') . ' 4', [
-            'v2' => '{"type":"text","default":"","editable":true,"label":"\\u00a7!\\u00a7URL!\\u00a7! 4"}',
-        ]);
-        $this->addSettingField('navExtraVisibility4', SelectFromArray::class, $this->__('Visible') . ' 4', [
-            'v2' => '{"type":"combo","datas":{"\\u00a7!\\u00a7Navigation horizontale!\\u00a7!":"horizontal","\\u00a7!\\u00a7Navigation burger!\\u00a7!":"burger","\\u00a7!\\u00a7Navigations horizontale et burger!\\u00a7!":"both"},"default":"both","editable":true,"label":"\\u00a7!\\u00a7Visible!\\u00a7! 4"}',
-            'options' => [
-                'horizontal' => $this->__('Navigation horizontale'),
-                'burger' => $this->__('Navigation burger'),
-                'both' => $this->__('Navigations horizontale et burger'),
-            ],
-            'default' => 'both',
-        ]);
-        $this->addSettingField('', FormSeparator::class);
-        $this->addSettingField('navExtraIcon5', FilesOrURL::class, $this->__('Icône supplémentaire') . ' 5', [
-            'v2' => '{"type":"freefile","default":"","editable":true,"label":"\\u00a7!\\u00a7Ic\\u00f4ne suppl\\u00e9mentaire!\\u00a7! 5","fileFilter":{"name":"\\u00a7!\\u00a7Images!\\u00a7! (*.svg, *.jpg, *.png, *.gif)","extensions":"*.svg;*.jpg;*.png;*.gif"}}',
-            'accept' => self::$acceptImages,
-        ]);
-        $this->addSettingField('navExtraType5', SelectFromArray::class, $this->__('Type'), [
-            'v2' => '{"type":"combo","datas":{"\\u00a7!\\u00a7Ic\\u00f4ne + Label!\\u00a7!":"icon","\\u00a7!\\u00a7Image!\\u00a7!":"image"},"default":"icon","editable":true,"label":"\\u00a7!\\u00a7Type!\\u00a7!"}',
-            'options' => [
-                'icon' => $this->__('Icône + Label'),
-                'image' => $this->__('Image'),
-            ],
-            'default' => 'icon',
-        ]);
-        $this->addSettingField('navExtraLink5', LongText::class, $this->__('URL') . ' 5', [
-            'v2' => '{"type":"text","default":"","editable":true,"label":"\\u00a7!\\u00a7URL!\\u00a7! 5"}',
-        ]);
-        $this->addSettingField('navExtraVisibility5', SelectFromArray::class, $this->__('Visible') . ' 5', [
-            'v2' => '{"type":"combo","datas":{"\\u00a7!\\u00a7Navigation horizontale!\\u00a7!":"horizontal","\\u00a7!\\u00a7Navigation burger!\\u00a7!":"burger","\\u00a7!\\u00a7Navigations horizontale et burger!\\u00a7!":"both"},"default":"both","editable":true,"label":"\\u00a7!\\u00a7Visible!\\u00a7! 5"}',
-            'options' => [
-                'horizontal' => $this->__('Navigation horizontale'),
-                'burger' => $this->__('Navigation burger'),
-                'both' => $this->__('Navigations horizontale et burger'),
-            ],
-            'default' => 'both',
-        ]);
+
+        for ($i = 1; $i <= 5; $i++) {
+            $this->addSettingField('', FormSeparator::class);
+            $this->addSettingField('navExtraIcon' . $i, FilesOrURL::class, $this->__('Icône supplémentaire') . ' ' . $i, [
+                'v2' => '{"type":"freefile","default":"","editable":true,"label":"\\u00a7!\\u00a7Ic\\u00f4ne suppl\\u00e9mentaire!\\u00a7! ' . $i . '","fileFilter":{"name":"\\u00a7!\\u00a7Images!\\u00a7! (*.svg, *.jpg, *.png, *.gif)","extensions":"*.svg;*.jpg;*.png;*.gif"}}',
+                'accept' => self::$acceptImages,
+            ]);
+            $this->addSettingField('navExtraType' . $i, SelectFromArray::class, $this->__('Type'), [
+                'v2' => '{"type":"combo","datas":{"\\u00a7!\\u00a7Ic\\u00f4ne + Label!\\u00a7!":"icon","\\u00a7!\\u00a7Image!\\u00a7!":"image"},"default":"icon","editable":true,"label":"\\u00a7!\\u00a7Type!\\u00a7!"}',
+                'options' => [
+                    'icon' => $this->__('Icône + Label'),
+                    'image' => $this->__('Image'),
+                ],
+                'default' => 'icon',
+            ]);
+            $this->addSettingField('navExtraLink' . $i, LongText::class, $this->__('URL') . ' ' . $i, [
+                'v2' => '{"type":"text","default":"","editable":true,"label":"\\u00a7!\\u00a7URL!\\u00a7! ' . $i . '"}',
+            ]);
+            $this->addSettingField('navExtraVisibility' . $i, SelectFromArray::class, $this->__('Visible') . ' ' . $i, [
+                'v2' => '{"type":"combo","datas":{"\\u00a7!\\u00a7Navigation horizontale!\\u00a7!":"horizontal","\\u00a7!\\u00a7Navigation burger!\\u00a7!":"burger","\\u00a7!\\u00a7Navigations horizontale et burger!\\u00a7!":"both"},"default":"both","editable":true,"label":"\\u00a7!\\u00a7Visible!\\u00a7! ' . $i . '"}',
+                'options' => [
+                    'horizontal' => $this->__('Navigation horizontale'),
+                    'burger' => $this->__('Navigation burger'),
+                    'both' => $this->__('Navigations horizontale et burger'),
+                ],
+                'default' => 'both',
+            ]);
+        }
         $this->addSettingField('', FormSeparator::class);
         $this->addSettingField('mobileBottomNav', LongText::class, $this->__('Barre de boutons en bas'), [
             'v2' => '{"type":"text","default":"","editable":"true","label":"\\u00a7!\\u00a7Barre de boutons en bas!\\u00a7!","hint":"\\u00a7!\\u00a7Identifiants des boutons s\\u00e9par\\u00e9s par des virgules!\\u00a7!"}',
@@ -1167,10 +1092,7 @@ trait PublicationSettings
         $this->addSettingField('tabsHTML5', FilesOrURL::class, $this->__('Fichier d\'onglets') . ' (SVG, ZIP)', [
             'v2' => '{"type":"freefile","default":"","editable":true,"label":"\\u00a7!\\u00a7Fichier d\'onglets!\\u00a7! (SVG, ZIP)","grade":3,"hint":"\\u00a7!\\u00a7Charger un zip rendra les options ci-dessous inop\\u00e9rantes!\\u00a7!","fileFilter":{"name":"\\u00a7!\\u00a7Fichier onglets!\\u00a7! (*.svg, *.zip)","extensions":"*.svg;*.zip"}}',
             'hint' => $this->__('Charger un zip rendra les options ci-dessous inopérantes'),
-            'accept' => [
-                0 => '.zip',
-                1 => '.svg',
-            ],
+            'accept' => self::$acceptTabs,
         ]);
         $this->addSettingField('tabsPages', LongText::class, $this->__('Pages des onglets'), [
             'v2' => '{"type":"text","label":"\\u00a7!\\u00a7Pages des onglets!\\u00a7!","editable":true,"default":"","grade":5,"hint":"\\u00a7!\\u00a7Num\\u00e9ros de pages s\\u00e9paras par une virgule!\\u00a7!"}',
@@ -1321,9 +1243,7 @@ trait PublicationSettings
         $this->addSettingField('section_multimedia', FormSection::class, $this->__('Multimedia'));
         $this->addSettingField('textPopupStylesheet', FilesOrURL::class, $this->__('Feuille de style des popups de texte'), [
             'v2' => '{"type":"freefile","default":"","editable":true,"label":"\\u00a7!\\u00a7Feuille de style des popups de texte!\\u00a7!","fileFilter":{"name":"\\u00a7!\\u00a7Feuille de style!\\u00a7! (.css)","extensions":"*.css"}}',
-            'accept' => [
-                0 => '.css',
-            ],
+            'accept' => self::$acceptCSS,
         ]);
         $this->addSettingField('textPopupWidth', Integer::class, $this->__('Largeur optimale des popups'), [
             'v2' => '{"type":"integer","default":600,"editable":true,"label":"\\u00a7!\\u00a7Largeur optimale des popups!\\u00a7!"}',
@@ -1467,10 +1387,7 @@ trait PublicationSettings
         $this->addSettingField('product_zoom_references', FilesOrURL::class, $this->__('Références produits'), [
             'v2' => '{"type":"freefile","default":"","editable":true,"label":"\\u00a7!\\u00a7R\\u00e9f\\u00e9rences produits!\\u00a7!","grade":3,"fileFilter":{"name":"\\u00a7!\\u00a7Liste de produits!\\u00a7!","extensions":" (*.xml, *.xlsx)"},"hint":"\\u00a7!\\u00a7Fichier contenant les r\\u00e9f\\u00e9rences produits!\\u00a7!","dir":"commerce"}',
             'hint' => $this->__('Fichier contenant les références produits'),
-            'accept' => [
-                0 => '.xml',
-                1 => '.xlsx',
-            ],
+            'accept' => self::$acceptProductList,
             'destination' => 'commerce',
         ]);
         $this->addSettingField('', FormSeparator::class);
@@ -1482,135 +1399,53 @@ trait PublicationSettings
             ],
             'default' => 'right',
         ]);
-        $this->addSettingField('', FormSeparator::class);
-        $this->addSettingField('product_zoom_label_1', LongText::class, $this->__('Label du bouton') . ' 1', [
-            'v2' => '{"type":"text","label":"\\u00a7!\\u00a7Label du bouton!\\u00a7! 1","default":"","editable":true,"grade":3}',
-        ]);
-        $this->addSettingField('product_zoom_tooltip_1', LongText::class, $this->__('Label de l\'infobulle') . ' 1', [
-            'v2' => '{"type":"text","label":"\\u00a7!\\u00a7Label de l\'infobulle!\\u00a7! 1","default":"","editable":true,"grade":3}',
-        ]);
-        $this->addSettingField('product_zoom_icon_1', SelectFromArray::class, $this->__('Icône du bouton') . ' 1', [
-            'v2' => '{"type":"combo","datas":{"\\u00a7!\\u00a7Aucune!\\u00a7!":"none","\\u00a7!\\u00a7Partage!\\u00a7!":"nav-share","\\u00a7!\\u00a7Ecran!\\u00a7!":"icon-site","\\u00a7!\\u00a7T\\u00e9l\\u00e9charger!\\u00a7!":"nav-download","PDF":"icon-pdf","Video":"icon-video","\\u00a7!\\u00a7Lien!\\u00a7!":"icon-link","\\u00a7!\\u00a7Code!\\u00a7!":"icon-code","\\u00a7!\\u00a7QR code!\\u00a7!":"icon-qr","\\u00a7!\\u00a7Article!\\u00a7!":"icon-article","\\u00a7!\\u00a7Panier (sac)!\\u00a7!":"nav-basket","\\u00a7!\\u00a7Panier (caddie)!\\u00a7!":"nav-cart-caddie","\\u00a7!\\u00a7Panier (wishlist)!\\u00a7!":"nav-cart-wishlist","\\u00a7!\\u00a7360\\u00b0!\\u00a7!":"icon-360","\\u00a7!\\u00a7Photo!\\u00a7!":"icon-photo"},"label":"\\u00a7!\\u00a7Ic\\u00f4ne du bouton!\\u00a7! 1","default":"click","editable":true,"grade":3}',
-            'options' => [
-                'none' => $this->__('Aucune'),
-                'nav-share' => $this->__('Partage'),
-                'icon-site' => $this->__('Ecran'),
-                'nav-download' => $this->__('Télécharger'),
-                'icon-pdf' => 'PDF',
-                'icon-video' => 'Video',
-                'icon-link' => $this->__('Lien'),
-                'icon-code' => $this->__('Code'),
-                'icon-qr' => $this->__('QR code'),
-                'icon-article' => $this->__('Article'),
-                'nav-basket' => $this->__('Panier (sac)'),
-                'nav-cart-caddie' => $this->__('Panier (caddie)'),
-                'nav-cart-wishlist' => $this->__('Panier (wishlist)'),
-                'icon-360' => $this->__('360°'),
-                'icon-photo' => $this->__('Photo'),
-            ],
-            'default' => 'none',
-        ]);
-        $this->addSettingField('product_zoom_action_1', SelectFromArray::class, $this->__('Action du bouton') . ' 1', [
-            'v2' => '{"type":"combo","datas":{"\\u00a7!\\u00a7D\\u00e9sactiver!\\u00a7!":"disabled","\\u00a7!\\u00a7Ouvrir un lien!\\u00a7!":"link","\\u00a7!\\u00a7Ouvrir en iframe!\\u00a7!":"iframe","\\u00a7!\\u00a7Copier un contenu!\\u00a7!":"copy","\\u00a7!\\u00a7Web Vid\\u00e9o!\\u00a7!":"video","\\u00a7!\\u00a7Vid\\u00e9o!\\u00a7!":"videofile","\\u00a7!\\u00a7Vid\\u00e9o (boucle, sans contr\\u00f4le)!\\u00a7!":"videogif","\\u00a7!\\u00a7Voir une image!\\u00a7!":"image","\\u00a7!\\u00a7Ajouter au panier!\\u00a7!":"addtocart"},"label":"\\u00a7!\\u00a7Action du bouton!\\u00a7! 1","default":"disabled","editable":true,"grade":3}',
-            'options' => [
-                'disabled' => $this->__('Désactiver'),
-                'link' => $this->__('Ouvrir un lien'),
-                'iframe' => $this->__('Ouvrir en iframe'),
-                'copy' => $this->__('Copier un contenu'),
-                'video' => $this->__('Web Vidéo'),
-                'videofile' => $this->__('Vidéo'),
-                'videogif' => $this->__('Vidéo (boucle, sans contrôle)'),
-                'image' => $this->__('Voir une image'),
-                'addtocart' => $this->__('Ajouter au panier'),
-            ],
-            'default' => 'disabled',
-        ]);
-        $this->addSettingField('', FormSeparator::class);
-        $this->addSettingField('product_zoom_label_2', LongText::class, $this->__('Label du bouton') . ' 2', [
-            'v2' => '{"type":"text","label":"\\u00a7!\\u00a7Label du bouton!\\u00a7! 2","default":"","editable":true,"grade":3}',
-        ]);
-        $this->addSettingField('product_zoom_tooltip_2', LongText::class, $this->__('Label de l\'infobulle') . ' 2', [
-            'v2' => '{"type":"text","label":"\\u00a7!\\u00a7Label de l\'infobulle!\\u00a7! 2","default":"","editable":true,"grade":3}',
-        ]);
-        $this->addSettingField('product_zoom_icon_2', SelectFromArray::class, $this->__('Icône du bouton') . ' 2', [
-            'v2' => '{"type":"combo","datas":{"\\u00a7!\\u00a7Aucune!\\u00a7!":"none","\\u00a7!\\u00a7Partage!\\u00a7!":"nav-share","\\u00a7!\\u00a7Ecran!\\u00a7!":"icon-site","\\u00a7!\\u00a7T\\u00e9l\\u00e9charger!\\u00a7!":"nav-download","PDF":"icon-pdf","Video":"icon-video","\\u00a7!\\u00a7Lien!\\u00a7!":"icon-link","\\u00a7!\\u00a7Code!\\u00a7!":"icon-code","\\u00a7!\\u00a7QR code!\\u00a7!":"icon-qr","\\u00a7!\\u00a7Article!\\u00a7!":"icon-article","\\u00a7!\\u00a7Panier (sac)!\\u00a7!":"nav-basket","\\u00a7!\\u00a7Panier (caddie)!\\u00a7!":"nav-cart-caddie","\\u00a7!\\u00a7Panier (wishlist)!\\u00a7!":"nav-cart-wishlist","\\u00a7!\\u00a7360\\u00b0!\\u00a7!":"icon-360","\\u00a7!\\u00a7Photo!\\u00a7!":"icon-photo"},"label":"\\u00a7!\\u00a7Ic\\u00f4ne du bouton!\\u00a7! 2","default":"click","editable":true,"grade":3}',
-            'options' => [
-                'none' => $this->__('Aucune'),
-                'nav-share' => $this->__('Partage'),
-                'icon-site' => $this->__('Ecran'),
-                'nav-download' => $this->__('Télécharger'),
-                'icon-pdf' => 'PDF',
-                'icon-video' => 'Video',
-                'icon-link' => $this->__('Lien'),
-                'icon-code' => $this->__('Code'),
-                'icon-qr' => $this->__('QR code'),
-                'icon-article' => $this->__('Article'),
-                'nav-basket' => $this->__('Panier (sac)'),
-                'nav-cart-caddie' => $this->__('Panier (caddie)'),
-                'nav-cart-wishlist' => $this->__('Panier (wishlist)'),
-                'icon-360' => $this->__('360°'),
-                'icon-photo' => $this->__('Photo'),
-            ],
-            'default' => 'none',
-        ]);
-        $this->addSettingField('product_zoom_action_2', SelectFromArray::class, $this->__('Action du bouton') . ' 2', [
-            'v2' => '{"type":"combo","datas":{"\\u00a7!\\u00a7D\\u00e9sactiver!\\u00a7!":"disabled","\\u00a7!\\u00a7Ouvrir un lien!\\u00a7!":"link","\\u00a7!\\u00a7Ouvrir en iframe!\\u00a7!":"iframe","\\u00a7!\\u00a7Copier un contenu!\\u00a7!":"copy","\\u00a7!\\u00a7Web Vid\\u00e9o!\\u00a7!":"video","\\u00a7!\\u00a7Vid\\u00e9o!\\u00a7!":"videofile","\\u00a7!\\u00a7Vid\\u00e9o (boucle, sans contr\\u00f4le)!\\u00a7!":"videogif","\\u00a7!\\u00a7Voir une image!\\u00a7!":"image","\\u00a7!\\u00a7Ajouter au panier!\\u00a7!":"addtocart"},"label":"\\u00a7!\\u00a7Action du bouton!\\u00a7! 2","default":"disabled","editable":true,"grade":3}',
-            'options' => [
-                'disabled' => $this->__('Désactiver'),
-                'link' => $this->__('Ouvrir un lien'),
-                'iframe' => $this->__('Ouvrir en iframe'),
-                'copy' => $this->__('Copier un contenu'),
-                'video' => $this->__('Web Vidéo'),
-                'videofile' => $this->__('Vidéo'),
-                'videogif' => $this->__('Vidéo (boucle, sans contrôle)'),
-                'image' => $this->__('Voir une image'),
-                'addtocart' => $this->__('Ajouter au panier'),
-            ],
-            'default' => 'disabled',
-        ]);
-        $this->addSettingField('', FormSeparator::class);
-        $this->addSettingField('product_zoom_label_3', LongText::class, $this->__('Label du bouton') . ' 3', [
-            'v2' => '{"type":"text","label":"\\u00a7!\\u00a7Label du bouton!\\u00a7! 3","default":"","editable":true,"grade":3}',
-        ]);
-        $this->addSettingField('product_zoom_tooltip_3', LongText::class, $this->__('Label de l\'infobulle') . ' 3', [
-            'v2' => '{"type":"text","label":"\\u00a7!\\u00a7Label de l\'infobulle!\\u00a7! 3","default":"","editable":true,"grade":3}',
-        ]);
-        $this->addSettingField('product_zoom_icon_3', SelectFromArray::class, $this->__('Icône du bouton') . ' 3', [
-            'v2' => '{"type":"combo","datas":{"\\u00a7!\\u00a7Aucune!\\u00a7!":"none","\\u00a7!\\u00a7Partage!\\u00a7!":"nav-share","\\u00a7!\\u00a7Ecran!\\u00a7!":"icon-site","\\u00a7!\\u00a7T\\u00e9l\\u00e9charger!\\u00a7!":"nav-download","PDF":"icon-pdf","Video":"icon-video","\\u00a7!\\u00a7Lien!\\u00a7!":"icon-link","\\u00a7!\\u00a7Code!\\u00a7!":"icon-code","\\u00a7!\\u00a7QR code!\\u00a7!":"icon-qr","\\u00a7!\\u00a7Article!\\u00a7!":"icon-article","\\u00a7!\\u00a7Panier (sac)!\\u00a7!":"nav-basket","\\u00a7!\\u00a7Panier (caddie)!\\u00a7!":"nav-cart-caddie","\\u00a7!\\u00a7Panier (wishlist)!\\u00a7!":"nav-cart-wishlist","\\u00a7!\\u00a7360\\u00b0!\\u00a7!":"icon-360","\\u00a7!\\u00a7Photo!\\u00a7!":"icon-photo"},"label":"\\u00a7!\\u00a7Ic\\u00f4ne du bouton!\\u00a7! 3","default":"click","editable":true,"grade":3}',
-            'options' => [
-                'none' => $this->__('Aucune'),
-                'nav-share' => $this->__('Partage'),
-                'icon-site' => $this->__('Ecran'),
-                'nav-download' => $this->__('Télécharger'),
-                'icon-pdf' => 'PDF',
-                'icon-video' => 'Video',
-                'icon-link' => $this->__('Lien'),
-                'icon-code' => $this->__('Code'),
-                'icon-qr' => $this->__('QR code'),
-                'icon-article' => $this->__('Article'),
-                'nav-basket' => $this->__('Panier (sac)'),
-                'nav-cart-caddie' => $this->__('Panier (caddie)'),
-                'nav-cart-wishlist' => $this->__('Panier (wishlist)'),
-                'icon-360' => $this->__('360°'),
-                'icon-photo' => $this->__('Photo'),
-            ],
-            'default' => 'none',
-        ]);
-        $this->addSettingField('product_zoom_action_3', SelectFromArray::class, $this->__('Action du bouton') . ' 3', [
-            'v2' => '{"type":"combo","datas":{"\\u00a7!\\u00a7D\\u00e9sactiver!\\u00a7!":"disabled","\\u00a7!\\u00a7Ouvrir un lien!\\u00a7!":"link","\\u00a7!\\u00a7Ouvrir en iframe!\\u00a7!":"iframe","\\u00a7!\\u00a7Copier un contenu!\\u00a7!":"copy","\\u00a7!\\u00a7Web Vid\\u00e9o!\\u00a7!":"video","\\u00a7!\\u00a7Vid\\u00e9o!\\u00a7!":"videofile","\\u00a7!\\u00a7Vid\\u00e9o (boucle, sans contr\\u00f4le)!\\u00a7!":"videogif","\\u00a7!\\u00a7Voir une image!\\u00a7!":"image","\\u00a7!\\u00a7Ajouter au panier!\\u00a7!":"addtocart"},"label":"\\u00a7!\\u00a7Action du bouton!\\u00a7! 3","default":"disabled","editable":true,"grade":3}',
-            'options' => [
-                'disabled' => $this->__('Désactiver'),
-                'link' => $this->__('Ouvrir un lien'),
-                'iframe' => $this->__('Ouvrir en iframe'),
-                'copy' => $this->__('Copier un contenu'),
-                'video' => $this->__('Web Vidéo'),
-                'videofile' => $this->__('Vidéo'),
-                'videogif' => $this->__('Vidéo (boucle, sans contrôle)'),
-                'image' => $this->__('Voir une image'),
-                'addtocart' => $this->__('Ajouter au panier'),
-            ],
-            'default' => 'disabled',
-        ]);
+
+
+        for ($i = 1; $i <= 3; $i++) {
+            $this->addSettingField('', FormSeparator::class);
+            $this->addSettingField('product_zoom_label_' . $i, LongText::class, $this->__('Label du bouton') . ' ' . $i, [
+                'v2' => '{"type":"text","label":"\\u00a7!\\u00a7Label du bouton!\\u00a7! ' . $i . '","default":"","editable":true,"grade":3}',
+            ]);
+            $this->addSettingField('product_zoom_tooltip_' . $i, LongText::class, $this->__('Label de l\'infobulle') . ' ' . $i, [
+                'v2' => '{"type":"text","label":"\\u00a7!\\u00a7Label de l\'infobulle!\\u00a7! ' . $i . '","default":"","editable":true,"grade":3}',
+            ]);
+            $this->addSettingField('product_zoom_icon_' . $i, SelectFromArray::class, $this->__('Icône du bouton') . ' ' . $i, [
+                'v2' => '{"type":"combo","datas":{"\\u00a7!\\u00a7Aucune!\\u00a7!":"none","\\u00a7!\\u00a7Partage!\\u00a7!":"nav-share","\\u00a7!\\u00a7Ecran!\\u00a7!":"icon-site","\\u00a7!\\u00a7T\\u00e9l\\u00e9charger!\\u00a7!":"nav-download","PDF":"icon-pdf","Video":"icon-video","\\u00a7!\\u00a7Lien!\\u00a7!":"icon-link","\\u00a7!\\u00a7Code!\\u00a7!":"icon-code","\\u00a7!\\u00a7QR code!\\u00a7!":"icon-qr","\\u00a7!\\u00a7Article!\\u00a7!":"icon-article","\\u00a7!\\u00a7Panier (sac)!\\u00a7!":"nav-basket","\\u00a7!\\u00a7Panier (caddie)!\\u00a7!":"nav-cart-caddie","\\u00a7!\\u00a7Panier (wishlist)!\\u00a7!":"nav-cart-wishlist","\\u00a7!\\u00a7360\\u00b0!\\u00a7!":"icon-360","\\u00a7!\\u00a7Photo!\\u00a7!":"icon-photo"},"label":"\\u00a7!\\u00a7Ic\\u00f4ne du bouton!\\u00a7! ' . $i . '","default":"click","editable":true,"grade":3}',
+                'options' => [
+                    'none' => $this->__('Aucune'),
+                    'nav-share' => $this->__('Partage'),
+                    'icon-site' => $this->__('Ecran'),
+                    'nav-download' => $this->__('Télécharger'),
+                    'icon-pdf' => 'PDF',
+                    'icon-video' => 'Video',
+                    'icon-link' => $this->__('Lien'),
+                    'icon-code' => $this->__('Code'),
+                    'icon-qr' => $this->__('QR code'),
+                    'icon-article' => $this->__('Article'),
+                    'nav-basket' => $this->__('Panier (sac)'),
+                    'nav-cart-caddie' => $this->__('Panier (caddie)'),
+                    'nav-cart-wishlist' => $this->__('Panier (wishlist)'),
+                    'icon-360' => $this->__('360°'),
+                    'icon-photo' => $this->__('Photo'),
+                ],
+                'default' => 'none',
+            ]);
+            $this->addSettingField('product_zoom_action_' . $i, SelectFromArray::class, $this->__('Action du bouton') . ' ' . $i, [
+                'v2' => '{"type":"combo","datas":{"\\u00a7!\\u00a7D\\u00e9sactiver!\\u00a7!":"disabled","\\u00a7!\\u00a7Ouvrir un lien!\\u00a7!":"link","\\u00a7!\\u00a7Ouvrir en iframe!\\u00a7!":"iframe","\\u00a7!\\u00a7Copier un contenu!\\u00a7!":"copy","\\u00a7!\\u00a7Web Vid\\u00e9o!\\u00a7!":"video","\\u00a7!\\u00a7Vid\\u00e9o!\\u00a7!":"videofile","\\u00a7!\\u00a7Vid\\u00e9o (boucle, sans contr\\u00f4le)!\\u00a7!":"videogif","\\u00a7!\\u00a7Voir une image!\\u00a7!":"image","\\u00a7!\\u00a7Ajouter au panier!\\u00a7!":"addtocart"},"label":"\\u00a7!\\u00a7Action du bouton!\\u00a7! 1","default":"disabled","editable":true,"grade":3}',
+                'options' => [
+                    'disabled' => $this->__('Désactiver'),
+                    'link' => $this->__('Ouvrir un lien'),
+                    'iframe' => $this->__('Ouvrir en iframe'),
+                    'copy' => $this->__('Copier un contenu'),
+                    'video' => $this->__('Web Vidéo'),
+                    'videofile' => $this->__('Vidéo'),
+                    'videogif' => $this->__('Vidéo (boucle, sans contrôle)'),
+                    'image' => $this->__('Voir une image'),
+                    'addtocart' => $this->__('Ajouter au panier'),
+                ],
+                'default' => 'disabled',
+            ]);
+        }
         $this->addSettingField('', FormSeparator::class);
         $this->addSettingField('product_share_enabled', Checkbox::class, $this->__('Activer le bouton de partage'), [
             'v2' => '{"type":"boolean","default":true,"editable":true,"grade":"3","label":"\\u00a7!\\u00a7Activer le bouton de partage!\\u00a7!"}',
@@ -1643,20 +1478,10 @@ trait PublicationSettings
         $this->addSettingField('section_accessibility', FormBigSection::class, $this->__('Accessibilité'));
         $this->addSettingField('audiodescriptionTexts', FilesOrURL::class, $this->__('Contenus textuels pour l\'audiodescription ou les lecteurs d\'écran'), [
             'v2' => '{"type":"freefile","default":"","editable":true,"label":"\\u00a7!\\u00a7Contenus textuels pour l\'audiodescription ou les lecteurs d\'\\u00e9cran!\\u00a7!","fileFilter":{"name":"\\u00a7!\\u00a7Fichier Excel!\\u00a7! (.xlsx)","extensions":"*.xlsx"}}',
-            'accept' => [
-                0 => '.xlsx',
-            ],
+            'accept' => self::$acceptXLSX,
         ]);
-        $this->addSettingField('audiodescriptionVoice', SelectFromArray::class, $this->__('Voix pour l\'audiodescription'), [
+        $this->addSettingField('audiodescriptionVoice', FluidbookTTSVoice::class, $this->__('Voix pour l\'audiodescription'), [
             'v2' => '{"type":"combo","datasFunc":"wsDroits::getTTSVoices()","datas":{"":"","Festival (en-US)":"festival:voice_cmu_us_slt_arctic_hts","ReadSpeaker Sophie (en-US)":"readspeaker:Sophie\\/en_us","ReadSpeaker Marc (en-US)":"readspeaker:Marc\\/en_us","Azure Denise (fr-FR)":"azuretts:fr-FR\\/Female\\/fr-FR-DeniseNeural","Azure Henry (fr-FR)":"azuretts:fr-FR\\/Male\\/fr-FR-MaleNeural"},"grade":5,"editable":true,"default":"","label":"\\u00a7!\\u00a7Voix pour l\'audiodescription!\\u00a7!"}',
-            'options' => [
-                '' => '',
-                'festival:voice_cmu_us_slt_arctic_hts' => 'Festival (en-US)',
-                'readspeaker:Sophie/en_us' => 'ReadSpeaker Sophie (en-US)',
-                'readspeaker:Marc/en_us' => 'ReadSpeaker Marc (en-US)',
-                'azuretts:fr-FR/Female/fr-FR-DeniseNeural' => 'Azure Denise (fr-FR)',
-                'azuretts:fr-FR/Male/fr-FR-MaleNeural' => 'Azure Henry (fr-FR)',
-            ],
         ]);
 
     }
@@ -1704,9 +1529,7 @@ trait PublicationSettings
         $this->addSettingField('section_articles', FormSection::class, $this->__('Articles'));
         $this->addSettingField('articlesFile', FilesOrURL::class, 'Articles', [
             'v2' => '{"type":"freefile","default":"","editable":true,"label":"Articles","grade":3,"fileFilter":{"name":"\\u00a7!\\u00a7Liste d\'articles!\\u00a7! (*.xml)","extensions":"*.xml"}}',
-            'accept' => [
-                0 => '.xml',
-            ],
+            'accept' => self::$acceptXML,
         ]);
         $this->addSettingField('articlesImages', FilesOrURL::class, $this->__('Images des articles'), [
             'v2' => '{"type":"freefile","label":"\\u00a7!\\u00a7Images des articles!\\u00a7!","default":"","hint":"\\u00a7!\\u00a7Les noms des fichiers doivent \\u00eatre les m\\u00eame que ceux des balises <image> du XML!\\u00a7!","editable":true,"grade":3,"dir":"articles","multiple":true}',
@@ -1849,10 +1672,7 @@ trait PublicationSettings
         $this->addSettingField('basketReferences', FilesOrURL::class, $this->__('Références produits'), [
             'v2' => '{"type":"freefile","default":"","editable":true,"label":"\\u00a7!\\u00a7R\\u00e9f\\u00e9rences produits!\\u00a7!","grade":3,"fileFilter":{"name":"\\u00a7!\\u00a7Liste de produits!\\u00a7!","extensions":" (*.xml, *.xlsx)"},"hint":"\\u00a7!\\u00a7Fichier contenant les r\\u00e9f\\u00e9rences produits!\\u00a7!","dir":"commerce"}',
             'hint' => $this->__('Fichier contenant les références produits'),
-            'accept' => [
-                0 => '.xml',
-                1 => '.xlsx',
-            ],
+            'accept' => self::$acceptProductList,
             'destination' => 'commerce',
         ]);
         $this->addSettingField('basketImages', FilesOrURL::class, $this->__('Images des produits'), [
@@ -1945,9 +1765,7 @@ trait PublicationSettings
         $this->addSettingField('', FormSeparator::class);
         $this->addSettingField('secureClientSidePassword', FilesOrURL::class, 'Sécurisation par mot de passe côté client', [
             'v2' => '{"type":"freefile","label":"S\\u00e9curisation par mot de passe c\\u00f4t\\u00e9 client","editable":true,"default":"","grade":3,"fileFilter":{"name":"\\u00a7!\\u00a7Fichier HTML!\\u00a7! (*.html)","extensions":"*.html"}}',
-            'accept' => [
-                0 => '.html',
-            ],
+            'accept' => self::$acceptHTML,
         ]);
         $this->addSettingField('secureClientSidePasswordCredentials', Textarea::class, 'Utilisateurs / mots de passe', [
             'v2' => '{"type":"textarea","label":"Utilisateurs \\/ mots de passe","editable":true,"default":"","grade":3,"hint":"Format user:password par ligne"}',
@@ -2193,36 +2011,45 @@ trait PublicationSettings
         $this->_scorm();
     }
 
-    protected function _stats()
+    protected function _statsCustom()
     {
-        $this->addSettingField('bigsection_stats', FormSuperSection::class, $this->__('Statistiques et vie privée'));
-        $this->addSettingField('section_fluidbookStats', FormSection::class, $this->__('Statistiques Fluidbook'));
-        $this->addSettingField('stats', Checkbox::class, $this->__('Activer les statistiques Fluidbook'), [
-            'v2' => '{"type":"boolean","default":true,"editable":true,"label":"\\u00a7!\\u00a7Activer les statistiques Fluidbook!\\u00a7!","grade":3}',
-            'default' => true,
-        ]);
-        $this->addSettingField('', FormSeparator::class);
-        $this->addSettingField('stats_score', Checkbox::class, $this->__('Afficher les scores dans les rapports'), [
-            'v2' => '{"type":"boolean","default":true,"editable":true,"label":"\\u00a7!\\u00a7Afficher les scores dans les rapports!\\u00a7!","grade":5}',
-            'default' => true,
+        $this->addSettingField('section_stats_custom', FormSection::class, __('Autres services'));
+        $this->addSettingField('googleAnalyticsCustom', Code::class, $this->__('Code Stats personnalisé (placé avant fermeture de head)'), [
+            'v2' => '{"type":"textarea","default":"","editable":true,"label":"\\u00a7!\\u00a7Code Stats personnalis\\u00e9 (plac\\u00e9 avant fermeture de head)!\\u00a7!"}',
+            'fake' => true,
+            'language' => 'html',
+            'store_in' => 'settings',
+            'translatable' => false,
         ]);
-        $this->addSettingField('stats_exclude_ip', Textarea::class, $this->__('Exclure les IP suivantes des statistiques'), [
-            'v2' => '{"type":"textarea","default":"","editable":true,"label":"\\u00a7!\\u00a7Exclure les IP suivantes des statistiques!\\u00a7!","grade":3,"hint":"\\u00a7!\\u00a7Indiquer une adresse ip de la forme www.xxx.yyy.zzz par ligne!\\u00a7!"}',
-            'hint' => $this->__('Indiquer une adresse ip de la forme www.xxx.yyy.zzz par ligne'),
+        $this->addSettingField('statsCustom', Code::class, $this->__('Code Stats personnalisé (placé avant fermeture du body)'), [
+            'v2' => '{"type":"textarea","default":"","editable":true,"label":"\\u00a7!\\u00a7Code Stats personnalis\\u00e9 (plac\\u00e9 avant fermeture du body)!\\u00a7!"}',
+            'language' => 'html',
         ]);
-        $this->addSettingField('section_gaStats', FormSection::class, $this->__('Google Analytics'));
-        $this->addSettingField('googleAnalytics', LongText::class, $this->__('Code Google Analytics'), [
-            'v2' => '{"type":"text","default":"","editable":true,"label":"\\u00a7!\\u00a7Code Google Analytics!\\u00a7!","size":10}',
+        $this->addSettingField('anchorsAliases', LongText::class, $this->__('Alias des ancres'), [
+            'v2' => '{"type":"text","default":"","editable":true,"label":"\\u00a7!\\u00a7Alias des ancres!\\u00a7!"}',
         ]);
-        $this->addSettingField('section_gtmStats', FormSection::class, $this->__('Google Tag Manager'));
-        $this->addSettingField('googleTagManager', LongText::class, $this->__('Code Google Tag Manager'), [
-            'v2' => '{"type":"text","default":"","editable":true,"label":"\\u00a7!\\u00a7Code Google Tag Manager!\\u00a7!","size":10}',
+        $this->addSettingField('relay_url_params', LongText::class, $this->__('Relayer les paramètres d\'url dans les liens sortants'), [
+            'v2' => '{"type":"text","default":"","editable":true,"label":"\\u00a7!\\u00a7Relayer les param\\u00e8tres d\'url dans les liens sortants!\\u00a7!","grade":3}',
         ]);
-        $this->addSettingField('gtag_additional_code', Code::class, $this->__('Code additionnel Google Tag Manager'), [
-            'v2' => '{"type":"textarea","default":"","editable":true,"label":"\\u00a7!\\u00a7Code additionnel Google Tag Manager!\\u00a7!"}',
+    }
+
+    protected function _statsPiano()
+    {
+        $this->addSettingField('section_piano', FormSection::class, __('Piano Analytics'));
+        $this->addSettingField('xiti', Code::class, $this->__('Code Piano Analytics'), [
+            'v2' => '{"type":"textarea","default":"","editable":true,"label":"\\u00a7!\\u00a7Code global!\\u00a7!"}',
             'language' => 'html',
         ]);
+        $this->addSettingField('piano_plan', FilesOrURL::class, 'Plan de taggage', [
+            'v2' => '{"type":"freefile","default":"","editable":true,"label":"Plan de taggage","grade":3}',
+        ]);
+        $this->addSettingField('xiti_page', Textarea::class, $this->__('Modèle code xiti (pour tags relatifs aux pages)'), [
+            'v2' => '{"type":"textarea","default":"","editable":true,"label":"\\u00a7!\\u00a7Mod\\u00e8le code xiti (pour tags relatifs aux pages)!\\u00a7!"}',
+        ]);
+    }
 
+    protected function _statsTagCommander()
+    {
         $this->addSettingField('section_tc', FormSection::class, $this->__('Tag Commander'));
         $this->addSettingField('tagcommander_id', LongText::class, 'Tag commander ID', [
             'v2' => '{"type":"text","default":"","editable":true,"label":"Tag commander ID","grade":3}',
@@ -2245,36 +2072,45 @@ trait PublicationSettings
         $this->addSettingField('tagcommander_default_vars', Textarea::class, 'Variables par défaut', [
             'v2' => '{"type":"textarea","default":"","editable":true,"label":"Variables par d\\u00e9faut","grade":3}',
         ]);
-        $this->addSettingField('section_piano', FormSection::class, __('Piano Analytics'));
-        $this->addSettingField('xiti', Code::class, $this->__('Code Piano Analytics'), [
-            'v2' => '{"type":"textarea","default":"","editable":true,"label":"\\u00a7!\\u00a7Code global!\\u00a7!"}',
-            'language' => 'html',
-        ]);
-        $this->addSettingField('piano_plan', FilesOrURL::class, 'Plan de taggage', [
-            'v2' => '{"type":"freefile","default":"","editable":true,"label":"Plan de taggage","grade":3}',
-        ]);
-        $this->addSettingField('xiti_page', Textarea::class, $this->__('Modèle code xiti (pour tags relatifs aux pages)'), [
-            'v2' => '{"type":"textarea","default":"","editable":true,"label":"\\u00a7!\\u00a7Mod\\u00e8le code xiti (pour tags relatifs aux pages)!\\u00a7!"}',
+    }
+
+    protected function _statsGoogleTagManager()
+    {
+        $this->addSettingField('section_gtmStats', FormSection::class, $this->__('Google Tag Manager'));
+        $this->addSettingField('googleTagManager', LongText::class, $this->__('Code Google Tag Manager'), [
+            'v2' => '{"type":"text","default":"","editable":true,"label":"\\u00a7!\\u00a7Code Google Tag Manager!\\u00a7!","size":10}',
         ]);
-        $this->addSettingField('section_stats_custom', FormSection::class, __('Autres services'));
-        $this->addSettingField('googleAnalyticsCustom', Code::class, $this->__('Code Stats personnalisé (placé avant fermeture de head)'), [
-            'v2' => '{"type":"textarea","default":"","editable":true,"label":"\\u00a7!\\u00a7Code Stats personnalis\\u00e9 (plac\\u00e9 avant fermeture de head)!\\u00a7!"}',
-            'fake' => true,
+        $this->addSettingField('gtag_additional_code', Code::class, $this->__('Code additionnel Google Tag Manager'), [
+            'v2' => '{"type":"textarea","default":"","editable":true,"label":"\\u00a7!\\u00a7Code additionnel Google Tag Manager!\\u00a7!"}',
             'language' => 'html',
-            'store_in' => 'settings',
-            'translatable' => false,
         ]);
-        $this->addSettingField('statsCustom', Code::class, $this->__('Code Stats personnalisé (placé avant fermeture du body)'), [
-            'v2' => '{"type":"textarea","default":"","editable":true,"label":"\\u00a7!\\u00a7Code Stats personnalis\\u00e9 (plac\\u00e9 avant fermeture du body)!\\u00a7!"}',
-            'language' => 'html',
+    }
+
+    protected function _statsGoogleAnalytics()
+    {
+        $this->addSettingField('section_gaStats', FormSection::class, $this->__('Google Analytics'));
+        $this->addSettingField('googleAnalytics', LongText::class, $this->__('Code Google Analytics'), [
+            'v2' => '{"type":"text","default":"","editable":true,"label":"\\u00a7!\\u00a7Code Google Analytics!\\u00a7!","size":10}',
         ]);
-        $this->addSettingField('anchorsAliases', LongText::class, $this->__('Alias des ancres'), [
-            'v2' => '{"type":"text","default":"","editable":true,"label":"\\u00a7!\\u00a7Alias des ancres!\\u00a7!"}',
+    }
+
+    protected function _statsFluidbook()
+    {
+        $this->addSettingField('section_fluidbookStats', FormSection::class, $this->__('Statistiques Fluidbook'));
+        $this->addSettingField('stats', Checkbox::class, $this->__('Activer les statistiques Fluidbook'), [
+            'v2' => '{"type":"boolean","default":true,"editable":true,"label":"\\u00a7!\\u00a7Activer les statistiques Fluidbook!\\u00a7!","grade":3}',
+            'default' => true,
         ]);
-        $this->addSettingField('relay_url_params', LongText::class, $this->__('Relayer les paramètres d\'url dans les liens sortants'), [
-            'v2' => '{"type":"text","default":"","editable":true,"label":"\\u00a7!\\u00a7Relayer les param\\u00e8tres d\'url dans les liens sortants!\\u00a7!","grade":3}',
+        $this->addSettingField('', FormSeparator::class);
+        $this->addSettingField('stats_score', Checkbox::class, $this->__('Afficher les scores dans les rapports'), [
+            'v2' => '{"type":"boolean","default":true,"editable":true,"label":"\\u00a7!\\u00a7Afficher les scores dans les rapports!\\u00a7!","grade":5}',
+            'default' => true,
+        ]);
+        $this->addSettingField('stats_exclude_ip', Textarea::class, $this->__('Exclure les IP suivantes des statistiques'), [
+            'v2' => '{"type":"textarea","default":"","editable":true,"label":"\\u00a7!\\u00a7Exclure les IP suivantes des statistiques!\\u00a7!","grade":3,"hint":"\\u00a7!\\u00a7Indiquer une adresse ip de la forme www.xxx.yyy.zzz par ligne!\\u00a7!"}',
+            'hint' => $this->__('Indiquer une adresse ip de la forme www.xxx.yyy.zzz par ligne'),
         ]);
-        $this->_privacy();
     }
 
+
 }