]> _ Git - cubeextranet.git/commitdiff
wip #3733 @14
authorvincent@cubedesigners.com <vincent@cubedesigners.com@f5622870-0f3c-0410-866d-9cb505b7a8ef>
Tue, 1 Sep 2020 18:05:53 +0000 (18:05 +0000)
committervincent@cubedesigners.com <vincent@cubedesigners.com@f5622870-0f3c-0410-866d-9cb505b7a8ef>
Tue, 1 Sep 2020 18:05:53 +0000 (18:05 +0000)
inc/commons/class.common.tools.php
inc/ws/Metier/class.ws.book.parametres.php
inc/ws/Util/_common.php
inc/ws/Util/class.ws.articles.php [new file with mode: 0644]
inc/ws/Util/html5/master/class.ws.html5.compiler.php

index 217b0e6223fe96b41712d3216d9ef68ea18b2c0e..1ed6ffa448c8174d9649b7530b3ffa7057d96919 100644 (file)
@@ -1443,20 +1443,18 @@ class commonTools
         exit;
     }
 
-    public static function quizUpdater($args)
+    public static function articles($args)
     {
-        header('Location: https://toolbox.fluidbook.com/quiz/');
-        exit;
         global $core;
         $res = commonPage::barre();
         $res .= commonPage::tMain();
         $res .= commonPage::bh();
-        $res .= '<form action="' . SITE_PATH . 'tools/updateQuiz" method="post" class="notajax" enctype="multipart/form-data">';
+        $res .= '<form action="' . SITE_PATH . 'tools/articlesFixer" method="post" class="notajax" enctype="multipart/form-data">';
         $res .= '<table class="liste">';
-        $res .= '<tr><th><strong>' . __('Update your quiz') . '</strong></th></tr>';
-        $res .= '<tr><td>Please upload your quiz (ZIP file expected)</td></tr>';
-        $res .= '<tr class="odd"><td><input type="file" name="file[]" multiple /></td></tr>';
-        $res .= '<tr><td class="right"><a href="#" class="submit">' . $core->typo->BoutonOK(__('Proceed to update')) . '</a></td></td>';
+        $res .= '<tr><th colspan="2"><strong>' . __('Convertir les articles') . '</strong></th></tr>';
+        $res .= '<tr><td>Charger le zip contenant les xml et images</td><td ><input type="file" name="file" /></td></tr>';
+        $res .= '<tr class="odd"><td>Format d\'entrée</td><td><select name="format"><option value="business-immo">Business Immo</option></select></td></tr>';
+        $res .= '<tr><td colspan="2" class="right"><a href="#" class="submit">' . $core->typo->BoutonOK(__('Convertir')) . '</a></td></td>';
         $res .= '</table>';
         $res .= '</form>';
         $res .= '</div>';
@@ -1465,6 +1463,18 @@ class commonTools
         return $res;
     }
 
+    public static function articlesFixer()
+    {
+        $a = new wsArticles($_FILES['file']['tmp_name'], $_POST['format']);
+        $a->fix();
+        $a->outputZip();
+    }
+
+    public static function quizUpdater($args)
+    {
+        header('Location: https://toolbox.fluidbook.com/quiz/');
+    }
+
     /**
      * @param $tmp_name
      * @param $name
index 9197bd8e360624a2ed1b4209870e96209542d499..d146ad8c21ed6295688cf18f7c7064c340cc54dd 100644 (file)
@@ -33,8 +33,8 @@ class wsBookParametres extends wsParametres
         $basketFilter->extensions = '*.xml;*.xlsx';
 
         $articlesFilter = new stdClass();
-        $articlesFilter->name = __('Liste d\'articles') . ' (*.xml, *.zip)';
-        $articlesFilter->extensions = '*.xml;*.zip';
+        $articlesFilter->name = __('Liste d\'articles') . ' (*.xml)';
+        $articlesFilter->extensions = '*.xml';
 
         $htmlFilter = new stdClass();
         $htmlFilter->name = __('Fichier HTML') . ' (*.html)';
@@ -495,7 +495,7 @@ class wsBookParametres extends wsParametres
         $this->fields['articlesShare'] = ['type' => 'boolean', 'default' => true, 'editable' => true, 'label' => __('Activer le partage'), 'grade' => 3];
         $this->fields['articlesStyle'] = ['type' => 'combo', 'default' => true, 'editable' => true, 'label' => __('Style'), 'grade' => 3, 'datas' => ['Défaut' => 'default', 'Atlantic' => 'atlantic', 'Harmonie Mutuelle' => 'harmonie-mutuelle', 'Business Immo' => 'business-immo']];
 
-        $this->forms['articles'] = ['label' => __('Articles'), 'fieldsnames' => ['articlesFile', 'articlesFormat', 'articlesImages', '|', 'articlesShare', '|', 'articlesStyle', 'articlesFont']];
+        $this->forms['articles'] = ['label' => __('Articles'), 'fieldsnames' => ['articlesFile',  'articlesImages', '|', 'articlesShare', '|', 'articlesStyle', 'articlesFont']];
 
         //.
         $this->fields['externalArchives'] = array('type' => 'freefile', 'default' => '', 'editable' => true,
index 6ed4809081d21c787b7bc100bc59ae0dc3b4fefa..da09712d99b9ad7f20935f2f29d485bcbcefebdf 100644 (file)
@@ -1,21 +1,22 @@
 <?php\r
 \r
-include_once(dirname(__FILE__) . '/packager/_common.php');\r
-$__autoload['wsSWF2HTML'] = dirname(__FILE__) . '/class.ws.swf2html.php';\r
-$__autoload['wsSWF2HTMLTag'] = dirname(__FILE__) . '/class.ws.swf2html.php';\r
-$__autoload['wsSWF2HTMLText'] = dirname(__FILE__) . '/class.ws.swf2html.php';\r
-$__autoload['wsSWF2HTMLFont'] = dirname(__FILE__) . '/class.ws.swf2html.php';\r
-$__autoload['wsSWF2HTMLParagraph'] = dirname(__FILE__) . '/class.ws.swf2html.php';\r
-$__autoload['wsSWF2HTMLMatrix'] = dirname(__FILE__) . '/class.ws.swf2html.php';\r
-$__autoload['wsSecureSWF'] = dirname(__FILE__) . '/class.ws.secure.swf.php';\r
-$__autoload['wsTools'] = dirname(__FILE__) . '/class.ws.tools.php';\r
-$__autoload['wsHTML5AppCompiler'] = dirname(__FILE__) . '/html5/app/class.ws.html5.app.compiler.php';\r
-$__autoload['wsPDFFontExtractor'] = dirname(__FILE__) . '/fontextractor/class.ws.pdf.fontextractor.php';\r
-$__autoload['wsPDFFont'] = dirname(__FILE__) . '/fontextractor/class.ws.pdf.font.php';\r
-$__autoload['wsSVN'] = dirname(__FILE__) . '/class.ws.svn.php';\r
-$__autoload['wsLinks'] = dirname(__FILE__) . '/class.ws.links.php';\r
-$__autoload['wsExporter'] = dirname(__FILE__) . '/class.ws.exporter.php';\r
-$__autoload['wsPDFConvert'] = dirname(__FILE__) . '/class.ws.pdf.convert.php';\r
-$__autoload['wsUtil'] = dirname(__FILE__) . '/class.ws.util.php';\r
+include_once(__DIR__ . '/packager/_common.php');\r
+$__autoload['wsSWF2HTML'] = __DIR__. '/class.ws.swf2html.php';\r
+$__autoload['wsSWF2HTMLTag'] = __DIR__. '/class.ws.swf2html.php';\r
+$__autoload['wsSWF2HTMLText'] = __DIR__. '/class.ws.swf2html.php';\r
+$__autoload['wsSWF2HTMLFont'] = __DIR__. '/class.ws.swf2html.php';\r
+$__autoload['wsSWF2HTMLParagraph'] = __DIR__. '/class.ws.swf2html.php';\r
+$__autoload['wsSWF2HTMLMatrix'] = __DIR__. '/class.ws.swf2html.php';\r
+$__autoload['wsSecureSWF'] = __DIR__. '/class.ws.secure.swf.php';\r
+$__autoload['wsTools'] = __DIR__. '/class.ws.tools.php';\r
+$__autoload['wsHTML5AppCompiler'] = __DIR__. '/html5/app/class.ws.html5.app.compiler.php';\r
+$__autoload['wsPDFFontExtractor'] = __DIR__. '/fontextractor/class.ws.pdf.fontextractor.php';\r
+$__autoload['wsPDFFont'] = __DIR__. '/fontextractor/class.ws.pdf.font.php';\r
+$__autoload['wsSVN'] = __DIR__. '/class.ws.svn.php';\r
+$__autoload['wsLinks'] = __DIR__. '/class.ws.links.php';\r
+$__autoload['wsExporter'] = __DIR__. '/class.ws.exporter.php';\r
+$__autoload['wsPDFConvert'] = __DIR__. '/class.ws.pdf.convert.php';\r
+$__autoload['wsUtil'] = __DIR__. '/class.ws.util.php';\r
+$__autoload['wsArticles'] = __DIR__. '/class.ws.articles.php';\r
 \r
-$__autoload['wsHTML5'] = dirname(__FILE__) . '/html5/class.ws.html5.php';\r
+$__autoload['wsHTML5'] = __DIR__. '/html5/class.ws.html5.php';\r
diff --git a/inc/ws/Util/class.ws.articles.php b/inc/ws/Util/class.ws.articles.php
new file mode 100644 (file)
index 0000000..0bc9c15
--- /dev/null
@@ -0,0 +1,291 @@
+<?php\r
+\r
+class wsArticles\r
+{\r
+    /**\r
+     * @var string\r
+     */\r
+    protected $_file;\r
+\r
+    /**\r
+     * @var string\r
+     */\r
+    protected $_format;\r
+\r
+\r
+    /**\r
+     * @var ZipArchive\r
+     */\r
+    protected $_zip;\r
+\r
+    /**\r
+     * @var string\r
+     */\r
+    protected $_wdir;\r
+\r
+    /**\r
+     * @var string\r
+     */\r
+    protected $_outzip;\r
+\r
+    public function __construct($file, $format)\r
+    {\r
+        $this->setFile($file);\r
+        $this->setFormat($format);\r
+        $this->_wdir = CubeIT_Files::tmpdir();\r
+        if (!file_exists($this->_wdir)) {\r
+            mkdir($this->_wdir, 0777, true);\r
+        }\r
+    }\r
+\r
+    /**\r
+     * @param string $file\r
+     */\r
+    public function setFile(string $file): void\r
+    {\r
+        $this->_file = $file;\r
+        $this->_zip = new ZipArchive();\r
+        $this->_zip->open($this->_file);\r
+    }\r
+\r
+    /**\r
+     * @param string $format\r
+     */\r
+    public function setFormat(string $format): void\r
+    {\r
+        $this->_format = $format;\r
+    }\r
+\r
+    /**\r
+     * @return string\r
+     */\r
+    public function getFile(): string\r
+    {\r
+        return $this->_file;\r
+    }\r
+\r
+    /**\r
+     * @return string\r
+     */\r
+    public function getFormat(): string\r
+    {\r
+        return $this->_format;\r
+    }\r
+\r
+    public function fix()\r
+    {\r
+        if ($this->getFormat() === 'business-immo') {\r
+            $this->parseArticlesBusinessImmo();\r
+        }\r
+    }\r
+\r
+    public function outputZip()\r
+    {\r
+        $this->_outzip = CubeIT_Files::tempnam() . '.zip';\r
+        CubeIT_Util_Zip::archive($this->_wdir, $this->_outzip);\r
+\r
+        cubeHTTP::relayFile($this->_outzip);\r
+    }\r
+\r
+    public function parseArticlesBusinessImmo()\r
+    {\r
+        $assets = [];\r
+        $xmls = [];\r
+\r
+        for ($i = 0; $i <= $this->_zip->numFiles; $i++) {\r
+            $fname = $this->_zip->getNameIndex($i);\r
+            if (stristr($fname, '__MACOSX')) {\r
+                continue;\r
+            }\r
+            if (!preg_match('/\.xml$/', $fname)) {\r
+                $c = $this->_zip->getFromIndex($i);\r
+                if (!$c) {\r
+                    continue;\r
+                }\r
+                $e = explode('/', $fname);\r
+                array_pop($e);\r
+                $dir = implode('/', $e);\r
+                $fname = $this->_normalizeAssetName($fname, false);\r
+                $final = $i . '-' . $fname;\r
+                $assets[$fname] = ['index' => $i, 'dir' => $dir, 'final' => $final];\r
+                file_put_contents($this->_wdir . '/' . $final, $c);\r
+            } else {\r
+                $xmls[$fname] = $i;\r
+            }\r
+        }\r
+\r
+        $resdom = new DOMDocument('1.0', 'UTF-8');\r
+        $resdom->loadXML('<articles></articles>');\r
+\r
+        foreach ($xmls as $name => $index) {\r
+            $e = explode('/', $name);\r
+            array_pop($e);\r
+            $dir = implode('/', $e);\r
+            $dirassets = $dir . '/images';\r
+\r
+            $thisassets = [];\r
+\r
+            foreach ($assets as $aname => $asset) {\r
+                if ($asset['dir'] !== $dirassets) {\r
+                    continue;\r
+                }\r
+                $thisassets[$aname] = $asset;\r
+            }\r
+\r
+            $xmlContent = $this->_zip->getFromIndex($index);\r
+            $xmlContent = preg_replace_callback('/\<\/?[A-Za-z]+\/?\>/u', function ($w) {\r
+                return mb_strtolower($w[0]);\r
+            }, $xmlContent);\r
+\r
+            $xmlContent = preg_replace_callback('/&#(x?)([0-9a-fA-F]+);/', function ($w) {\r
+                if ($w[1] === 'x') {\r
+                    return mb_chr(hexdec($w[2]));\r
+                } else {\r
+                    return mb_chr($w[2]);\r
+                }\r
+            }, $xmlContent);\r
+            $xmlContent = preg_replace('/\<image\>([^\>]*)\<\/image\>/', '<legend>$1</legend>', $xmlContent);\r
+            $xmlContent = preg_replace_callback('/\<image([^>]*)link="(https:\/\/youtu\.be[^"]+)"([^\>]*)\>(\<\/image\>)?/', function ($w) {\r
+                return '<youtube link="' . $w[2] . '" />';\r
+            }, $xmlContent);\r
+            $xmlContent = str_replace(' ', ' ', $xmlContent);\r
+            $xmlContent = $this->_normalizeAssetNameInXML($xmlContent, $thisassets);\r
+            $sx = simplexml_load_string($xmlContent);\r
+\r
+            foreach ($sx->xpath('//article') as $k => $item) {\r
+                if ($item->count()) {\r
+                    $dom = dom_import_simplexml($item);\r
+                    $this->_fixNested($dom);\r
+                    $clone = $resdom->importNode($dom, true);\r
+\r
+                    $resdom->documentElement->appendChild($clone);\r
+\r
+                    $xpath = new DOMXPath($resdom);\r
+                    $titles = $xpath->query('./title', $clone);\r
+                    if ($titles->count() > 0) {\r
+                        foreach ($titles as $title) {\r
+                            $id = mb_strtolower(trim(mb_substr(CubeIT_Text::str2URL(trim($title->nodeValue)), 0, 100), '-'));\r
+                            break;\r
+                        }\r
+                    } else {\r
+                        $id = 'article-' . $index . '-' . $k;\r
+                    }\r
+                    $url = $id . '.html';\r
+                    $clone->setAttribute('id', $id);\r
+                    $clone->setAttribute('url', $url);\r
+\r
+                    $dom->parentNode->removeChild($dom);\r
+                }\r
+            }\r
+        }\r
+\r
+        $file = $this->_wdir . '/articles.xml';\r
+        $resdom->encoding = 'UTF-8';\r
+        $xml = tidy_repair_string($resdom->saveXML(), ['input-xml' => 1, 'indent' => 1, 'wrap' => 0], 'UTF-8');\r
+        file_put_contents($file, $xml);\r
+    }\r
+\r
+    /**\r
+     * @param $article DOMElement\r
+     */\r
+    protected function _fixNested(&$article)\r
+    {\r
+\r
+        $count = $article->childNodes->count();\r
+        for ($i = 0; $i < $count; $i++) {\r
+            $child = $article->childNodes->item($i);\r
+            if ($this->_hasNested($child)) {\r
+                $scount = $child->childNodes->count();\r
+                for ($j = 0; $j < $scount; $j++) {\r
+                    $schild = $child->childNodes->item($j);\r
+                    $article->insertBefore($schild, $child);\r
+                    $j--;\r
+                    $scount--;\r
+                }\r
+\r
+                if ($child->childNodes->count() === 0) {\r
+                    $article->removeChild($child);\r
+                }\r
+                $i = 0;\r
+                $count = $article->childNodes->count();\r
+            } else {\r
+            }\r
+        }\r
+    }\r
+\r
+    protected function _hasNested($child)\r
+    {\r
+        if ($this->_isText($child, true)) {\r
+            return false;\r
+        }\r
+        if ($child->tagName === 'encadre') {\r
+            return false;\r
+        }\r
+        $count = $child->childNodes->count();\r
+        if ($count > 1) {\r
+            return true;\r
+        }\r
+        if (!$count) {\r
+            return false;\r
+        }\r
+        return $count === 1 && !($child->childNodes->item(0) instanceof DOMText);\r
+    }\r
+\r
+    /**\r
+     * @param $node DOMNode\r
+     */\r
+    protected function _isText($node, $strict = false)\r
+    {\r
+        if ($node->nodeType === XML_TEXT_NODE || $node instanceof DOMText) {\r
+            return true;\r
+        }\r
+        if ($strict) {\r
+            return false;\r
+        }\r
+\r
+        if (null === $node->childNodes) {\r
+            return true;\r
+        }\r
+\r
+        $count = $node->childNodes->count();\r
+        for ($i = 0; $i < $count; $i++) {\r
+            $child = $node->childNodes->item($i);\r
+            if (!$this->_isText($child, true)) {\r
+                return false;\r
+            }\r
+        }\r
+        return true;\r
+    }\r
+\r
+    protected function _normalizeAssetNameInXML($xml, $assets)\r
+    {\r
+        return preg_replace_callback('/file:\/\/\/[^\"]*/um', function ($matches) use ($assets) {\r
+            $aname = $this->_normalizeAssetName($matches[0], false);\r
+            return $assets[$aname]['final'];\r
+        }, $xml);\r
+    }\r
+\r
+    protected function _normalizeAssetName($fname, $withDir = false)\r
+    {\r
+        if (!$withDir) {\r
+            $e = explode('/', $fname);\r
+            $fname = array_pop($e);\r
+        }\r
+        $fname = rawurldecode($fname);\r
+        $fname = str_replace('_opt.', '.', $fname);\r
+        $fname = CubeIT_Text::removeAccents($fname, true);\r
+        $fname = str_replace('é', 'e', $fname);\r
+        $fname = str_replace('ç', 'c', $fname);\r
+        $fname = preg_replace("/[\s©\(\)\&]/u", "_", $fname);\r
+        $fname = trim(mb_strtolower($fname));\r
+        return $fname;\r
+    }\r
+\r
+    public function __destruct()\r
+    {\r
+        CubeIT_Files::rmdir($this->_wdir);\r
+        if ($this->_outzip && file_exists($this->_outzip)) {\r
+            unlink($this->_outzip);\r
+        }\r
+    }\r
+}
\ No newline at end of file
index 40c484c4f291abf8916b7ca0651d141de7ae5f79..f2ad47d269cf6d80f8845166cdfc19b579a9ce32 100644 (file)
@@ -2523,51 +2523,6 @@ class wsHTML5Compiler
         return $innerXML;
     }
 
-
-    public function parseArticlesBusinessImmo($file)
-    {
-        $zip = new ZipArchive();
-        $zip->open($file);
-        $assets = [];
-        $xmls = [];
-        for ($i = 0; $i <= $zip->numFiles; $i++) {
-            $fname = $zip->getNameIndex($i);
-            if (stristr($fname, '__MACOSX')) {
-                continue;
-            }
-            if (!preg_match('/\.xml$/', $fname)) {
-                $assets[$this->_normalizeAssetName($fname, true)] = $i;
-            } else {
-                $xmls[$fname] = $i;
-            }
-        }
-
-        foreach ($xmls as $name => $index) {
-            $e = explode('/', $name);
-            array_pop($e);
-            $dir = implode('/', $e);
-            $xmlContent = $zip->getFromIndex($index);
-            $xmlContent = preg_replace_callback('/\<\/?[A-Za-z]+\/?\>/', function($w){
-                return mb_strtolower($w[0]);
-            }, $xmlContent);
-            $sx = simplexml_load_string($xmlContent);
-        }
-
-    }
-
-    protected function _normalizeAssetName($fname, $withDir = false)
-    {
-        if (!$withDir) {
-            $e = explode('/', $fname);
-            $fname = array_pop($e);
-        }
-        $fname = rawurldecode($fname);
-        $fname = trim(mb_strtolower($fname));
-        $fname = str_replace('_opt.', '.', $fname);
-        $fname = CubeIT_Text::removeAccents($fname);
-        return $fname;
-    }
-
     public function writeArticles()
     {
         $mapFonts = ['OpenSans' => 'Open Sans'];
@@ -2588,10 +2543,6 @@ class wsHTML5Compiler
             return;
         }
 
-        if ($this->book->parametres->articlesFormat === 'business-immo') {
-            $this->parseArticlesBusinessImmo($f);
-        }
-
         $this->addLess('articles');
         if ($this->book->parametres->articlesStyle !== 'default') {
             $this->lessVariables['articles-styles'] = $this->book->parametres->articlesStyle;
@@ -2601,88 +2552,72 @@ class wsHTML5Compiler
         $this->lessVariables['articles-font'] = $mapFonts[$this->book->parametres->articlesFont] ?? $this->book->parametres->articlesFont;
         $fontPath = $this->addFontKit($this->book->parametres->articlesFont);
 
-
         $svg = '<svg xmlns="http://www.w3.org/2000/svg" style="display: none;"><symbol id="nav-print" viewBox="0 0 512 512">
         <path d="m424 186l-39 0 0-114c0-9-6-15-14-15l-230 0c-8 0-14 6-14 15l0 114-39 0c-22 0-41 19-41 41l0 121c0 23 19 41 41 41l39 0 0 49c0 8 6 15 14 15l230 0c8 0 14-7 14-15l0-49 39 0c22 0 41-18 41-41l0-121c0-22-19-41-41-41z m-268-100l200 0 0 100-200 0z m200 340l-200 0 0-88 200 0z m80-76c0 6-6 12-12 12l-39 0 0-38c0-9-6-15-14-15l-230 0c-8 0-14 6-14 15l0 38-39 0c-6 0-12-6-12-12l0-121c0-6 6-12 12-12l336 0c6 0 12 6 12 12z m-278-96l-33 0c-8 0-14 6-14 14 0 8 6 15 14 15l35 0c8 0 14-7 14-15 0-8-8-14-16-14z m32 139l132 0c8 0 14-6 14-14 0-8-6-14-14-14l-132 0c-8 0-14 6-14 14 0 8 6 14 14 14z"/>
     </symbol></svg>';
 
-        $markupMap = ['category' => 'h3', 'subtitle' => 'h2', 'title' => 'h1', 'lead' => 'div.chapo', 'paragraph' => 'p', 'note' => 'div.note', 'quote' => 'blockquote', 'signature' => 'div.author', 'intertitle' => 'h2.inter', 'separator' => 'hr'];
 
         $x = simplexml_load_string(file_get_contents($f));
         $prevurl = '';
         foreach ($x->xpath('/articles/article') as $k => $a) {
             $url = (string)$a['url'];
-            $article = ['id' => (string)$a['id'],
-                'url' => $url,
-                'color' => (string)$a['color'],
-                'contents' => '',
-                'prev' => $prevurl,
-                'next' => ''];
-
-            if ($prevurl !== '') {
-                $list[count($list) - 1]['next'] = $url;
-            } else {
-                $firsturl = $url;
+            $id = (string)$a['id'];
+            $color = (string)$a['color'];
+            if (!$color) {
+                $color = '#000';
             }
 
-            $prevurl = $url;
-
-            $specificStyles = '## h3, ## figure figcaption{background-color:' . $article['color'] . '}';
-            $specificStyles .= '## .chapo, ## blockquote, ## a{color:' . $article['color'] . ';}';
+            $specificStyles = '## h3, ## figure figcaption{background-color:' . $color . '}';
+            $specificStyles .= '## .chapo, ## blockquote, ## a{color:' . $color . ';}';
 
-            $inner = '<article data-id="' . $article['id'] . '" class="menu-article" id="article_' . $article['id'] . '">';
-            $inner .= '<style type="text/css">' . str_replace('##', '#article_' . $article['id'], $specificStyles) . '</style>';
+            $inner = '<article data-id="$id" class="menu-article" id="article_$id">';
+            $inner .= '<style type="text/css">' . str_replace('##', '#article_$id', $specificStyles) . '</style>';
             $inner .= '<div class="actions">';
             if ($this->book->parametres->articlesShare && $this->book->parametres->share) {
-                $inner .= '<a data-id="' . $article['id'] . '" data-url="' . $url . '" href="#" class="articlesShare"><svg viewBox="0 0 512 512" class="nav-share nav-icon svg-icon"><use xlink:href="#nav-share"></use></svg></a>';
+                $inner .= '<a data-id="$id" data-url="$url" href="#" class="articlesShare"><svg viewBox="0 0 512 512" class="nav-share nav-icon svg-icon"><use xlink:href="#nav-share"></use></svg></a>';
             }
             $inner .= '<a href="#" class="articlesPrint"><svg viewBox="0 0 512 512" class="nav-print nav-icon svg-icon"><use xlink:href="#nav-print"></use></svg></a>';
             $inner .= '</div>';
 
+            $inner .= '<div class="articleBody">';
+
             $title = '';
             $lead = '';
             $image = '';
 
             foreach ($a->children() as $child) {
-                $tag = $child->getName();
-                if ($tag === 'image') {
+                $inner .= $this->_articleToHTML($child, $title, $lead, $image);
+            }
+            $inner .= '</div></article>';
 
-                    $file = (string)$child['file'];
-                    if ($image === '') {
-                        $image = 'articles/' . $file;
-                    }
-                    $filepath = $this->wdir . '/articles/' . $file;
-                    $this->vdir->copy($filepath, 'data/articles/' . $file);
-                    $legend = (string)$child;
-                    $caption = $legend ? '<figcaption>' . $legend . '</figcaption>' : '';
-                    if (file_exists($filepath)) {
-                        $dim = getimagesize($filepath);
-                    } else {
-                        $dim = [0 => 1024, 1 => 10];
-                    }
-                    $inner .= '<figure><img src="data/articles/' . $file . '" alt="' . $legend . '" width="' . $dim[0] . '" height="' . $dim[1] . '">' . $caption . '</figure>';
-                } else {
-                    $c = trim($this->SimpleXMLElement_innerXML($child));
-                    if (!$c) {
-                        continue;
-                    }
-                    if (!$title && $tag === 'title') {
-                        $title = $c;
-                    }
-                    if (!$lead && $tag === 'lead') {
-                        $lead = $c;
-                    }
-                    $m = $markupMap[$tag] ?? $tag;
-                    $e = explode('.', $m);
-                    $markup = $e[0];
-                    $class = '';
-                    if (count($e) === 2) {
-                        $class = ' class="' . $e[1] . '"';
-                    }
-                    $inner .= '<' . $markup . $class . '>' . $c . '</' . $markup . '>';
-                }
+            if (!$title) {
+                $title = 'Article sans titre ' . $k;
             }
-            $inner .= '</article>';
+
+            if (!$id) {
+                $id = CubeIT_Text::str2URL($title);
+            }
+
+            if (!$url) {
+                $url = $id . '.html';
+            }
+
+            $inner = str_replace(array('$id', '$url'), array($id, $url), $inner);
+
+            $article = ['id' => $id,
+                'url' => $url,
+                'color' => $color,
+                'contents' => '',
+                'prev' => $prevurl,
+                'next' => ''];
+
+            if ($prevurl !== '') {
+                $list[count($list) - 1]['next'] = $url;
+            } else {
+                $firsturl = $url;
+            }
+
+            $prevurl = $url;
 
             $article['contents'] = $inner;
             $content = '<html><head>';
@@ -2711,8 +2646,99 @@ class wsHTML5Compiler
         $this->config->articlesList = $idlist;
     }
 
+    /**
+     * @param $child SimpleXMLElement
+     * @param $title
+     * @param $lead
+     * @param $image
+     * @return string|void
+     * @throws Zend_Filter_Exception
+     */
+    protected function _articleToHTML($child, &$title, &$lead, &$image)
+    {
+        $markupMap = ['category' => 'h3',
+            'subtitle' => 'h2',
+            'legend' => 'figcaption',
+            'title' => 'h1',
+            'lead' => 'div.chapo',
+            'paragraph' => 'p',
+            'note' => 'div.note',
+            'quote' => 'blockquote',
+            'signature' => 'div.author',
+            'intertitle' => 'h2.inter',
+            'bigfont' => 'h2.bigfont',
+            'separator' => 'hr',
+            'link' => 'a'];
+
+        $attrsmap = ['a' => ['link' => 'href']];
+
+        $res = '';
+        $tag = $child->getName();
+        if ($tag === 'encadre') {
+            $res .= '<aside>';
+            foreach ($child->children() as $sub) {
+                $res .= $this->_articleToHTML($sub, $a1, $a2, $a3);
+            }
+            $res .= '</aside>';
+        } else if ($tag === 'youtube') {
+            $filter = new CubeIT_Filter_WebVideo();
+            $e = explode(':', $filter->filter((string)$child['link']));
+            $res .= '<div class="youtube"><iframe width="800" height="450" src="https://www.youtube.com/embed/' . $e[1] . '" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe></div>';
+        } else if ($tag === 'image') {
+            $srcattrs = ['href', 'src', 'file'];
+            $file = '';
+            foreach ($srcattrs as $srcattr) {
+                if (isset($child[$srcattr])) {
+                    $file = (string)$child[$srcattr];
+                    break;
+                }
+            }
+            if ($image === '') {
+                $image = 'articles/' . $file;
+            }
+            $filepath = $this->wdir . '/articles/' . $file;
+            $this->vdir->copy($filepath, 'data/articles/' . $file);
+            $legend = (string)$child;
+            $caption = $legend ? '<figcaption>' . $legend . '</figcaption>' : '';
+            if (file_exists($filepath)) {
+                $dim = getimagesize($filepath);
+            } else {
+                $dim = [0 => 1024, 1 => 10];
+            }
+            $res .= '<figure><img src="data/articles/' . $file . '" alt="' . $legend . '" width="' . $dim[0] . '" height="' . $dim[1] . '">' . $caption . '</figure>';
+        } else {
+            $c = trim($this->SimpleXMLElement_innerXML($child));
+            if (!$c) {
+                return;
+            }
+            if ($title === '' && $tag === 'title') {
+                $title = $c;
+            }
+            if ($lead === '' && $tag === 'lead') {
+                $lead = $c;
+            }
+            $m = $markupMap[$tag] ?? $tag;
+            $e = explode('.', $m);
+            $markup = $e[0];
+            $attrs = '';
+            if (count($e) === 2) {
+                $attrs .= ' class="' . $e[1] . '"';
+            }
+            if ($m === 'a') {
+                $attrs .= ' target="_blank"';
+            }
+            foreach ($child->attributes() as $name => $v) {
+                $n = $attrsmap[$m][$name] ?? $name;
+                $attrs .= ' ' . $n . '="' . htmlspecialchars($v) . '"';
+            }
+            $res .= '<' . $markup . $attrs . '>' . $c . '</' . $markup . '>';
+        }
+        return $res;
+    }
+
 }
 
+
 if (!function_exists('is_countable')) {
 
     function is_countable($c)