]> _ Git - cubeextranet.git/commitdiff
wip #3335 @1.25
authorvincent@cubedesigners.com <vincent@cubedesigners.com@f5622870-0f3c-0410-866d-9cb505b7a8ef>
Thu, 16 Jan 2020 18:02:20 +0000 (18:02 +0000)
committervincent@cubedesigners.com <vincent@cubedesigners.com@f5622870-0f3c-0410-866d-9cb505b7a8ef>
Thu, 16 Jan 2020 18:02:20 +0000 (18:02 +0000)
inc/commons/class.common.tools.php
inc/ws/Util/class.ws.tools.php
quiz/imsmanifest.xml [new file with mode: 0644]
quiz/template.zip [new file with mode: 0644]

index 5fff8167d89c70a405e00289a0ab1d4e692ce962..4d35851d02f1c95ca2f76d156ac1aed14e11bbce 100644 (file)
@@ -872,7 +872,8 @@ class commonTools
     }
 
 
-    public static function importFluidbookSettings(){
+    public static function importFluidbookSettings()
+    {
         commonDroits::min(5);
         global $core;
         $res = commonPage::barre();
@@ -1409,7 +1410,7 @@ class commonTools
 
         $res = [];
         foreach ($xls->getAllSheets() as $sheet) {
-            $res[$sheet->getTitle()]=$sheet->toArray();
+            $res[$sheet->getTitle()] = $sheet->toArray();
         }
 
         ob_end_clean();
@@ -1417,4 +1418,505 @@ class commonTools
         header('Content-Type: application/json');
         die(json_encode($res));
     }
+
+    public static function quizUpdater($args)
+    {
+        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 .= '<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" /></td></tr>';
+        $res .= '<tr><td class="right"><a href="#" class="submit">' . $core->typo->BoutonOK(__('Proceed to update')) . '</a></td></td>';
+        $res .= '</table>';
+        $res .= '</form>';
+        $res .= '</div>';
+        $res .= commonPage::bf();
+        $res .= commonPage::bMain();
+        return $res;
+    }
+
+    public static function updateQuiz()
+    {
+        $tidySettings=['input-xml'=> 1, 'indent' => 1, 'wrap' => 0];
+
+        $template = ROOT . '/quiz/template.zip';
+        $temp = ROOT . '/quiz/quiz-' . md5(rand(0, 10000000)) . '.zip';
+        copy($template, $temp);
+
+        $assets = ['assets/logo.png', 'assets/banner.jpg'];
+
+        $uploaded = new ZipArchive();
+        $uploaded->open($_FILES['file']['tmp_name']);
+
+        $template = new ZipArchive();
+        $template->open($temp);
+
+        $data = simplexml_load_string($uploaded->getFromName('data.xml'));
+        $quizTitle = (string)$data->xpath('/quiz/title')[0];
+
+        // Replace assets
+        foreach ($assets as $asset) {
+            $template->addFromString($asset, $uploaded->getFromName($asset));
+        }
+        // *** Replace XML ***
+        // First, merge both xml
+        $oMX = new MergeXML();
+        $oMX->AddSource($template->getFromName('data.xml'));
+        $oMX->AddSource($uploaded->getFromName('data.xml'));
+        $template->deleteName('data.xml');
+        // Save data.xml in dest zip
+        $template->addFromString('data.xml', tidy_repair_string($oMX->Get(1), $tidySettings));
+        // *** IMS Manifest ***
+        $imsmanifest = file_get_contents(ROOT . '/quiz/imsmanifest.xml');
+        $imsmanifest = str_replace('$title', $quizTitle, tidy_repair_string($imsmanifest,$tidySettings));
+        $template->addFromString('imsmanifest.xml', $imsmanifest);
+        // *** Save zip ***
+        $template->close();
+
+        header('Content-Type: application/zip');
+        header('Content-Disposition: attachment; filename=' . $_FILES['file']['name']);
+        ob_end_clean();
+
+        echo file_get_contents($temp);
+        unlink($temp);
+        exit;
+    }
+
 }
+
+
+/**
+ * XML merging class
+ * Merge multiple XML sources
+ *
+ * @package     MergeXML
+ * @author      Vallo Reima
+ * @copyright   (C)2014
+ */
+class MergeXML
+{
+    private $cln = 'DOMDocument';
+    private $dom;       /* result DOM object */
+    private $dxp;       /* result xPath object */
+    private $nsp;       /* namespaces list */
+    private $nsd = '_'; /* default namespace prefix */
+    private $ovw = array(
+        'stay' => array(), /* do not overwrite me */
+        'keep' => array() /* do not overwrite existing */
+    );
+    private $join;      /* joining root name */
+    private $updn;      /* update nodes sequentially by name */
+    private $count = 0; /* adding counter */
+    private $error;     /* error info */
+    /**
+     * set (default) options, create result object
+     * @param array $opts -- stay, keep, join, updn, fmt, enc
+     */
+    public function __construct($opts = array())
+    {
+        $this->Protect($opts);
+        $this->join = !isset($opts['join']) ? 'root' : (string)$opts['join'];
+        $this->updn = !isset($opts['updn']) ? true : (bool)$opts['updn'];
+        $this->error = (object)array('code' => '', 'text' => '');
+        if (class_exists($this->cln)) {
+            $this->dom = new $this->cln();
+            $this->dom->preserveWhiteSpace = false;
+            $this->dom->formatOutput = !isset($opts['fmt']) ? true : (bool)$opts['fmt'];
+            $this->dom->encoding = !isset($opts['enc']) ? 'utf-8' : (string)$opts['enc'];
+        } else {
+            $this->Error('nod');
+        }
+    }
+
+    /**
+     * add XML file
+     * @param string $file -- pathed filename
+     * @param array $opts
+     * @return object|false
+     */
+    public function AddFile($file, $opts = array())
+    {
+        $this->Protect($opts);
+        $data = @file_get_contents($file);
+        if ($data === false) {
+            $rlt = $this->Error('nof');
+        } else if (empty($data)) {
+            $rlt = $this->Error('emf');
+        } else {
+            $rlt = $this->AddSource($data);
+        }
+        return $rlt;
+    }
+
+    /**
+     * add XML to result object
+     * @param string|object $xml
+     * @param array $opts
+     * @return mixed -- false - bad content
+     *                  object - result
+     */
+    public function AddSource($xml, $opts = array())
+    {
+        $this->Protect($opts);
+        if (is_object($xml)) {
+            if (get_class($xml) != $this->cln) {
+                $dom = false;
+            } else if ($this->dom->hasChildNodes()) {
+                $dom = $xml;
+            } else {
+                $this->dom = $xml;
+                $this->dom->formatOutput = true;
+                $dom = true;
+            }
+        } else if ($this->dom->hasChildNodes()) { /* not first */
+            $dom = new $this->cln();
+            $dom->preserveWhiteSpace = false;
+            if (!@$dom->loadXML($xml)) {
+                $dom = false;
+            }
+        } else { /* first slot */
+            $dom = @$this->dom->loadXML($xml) ? true : false;
+        }
+        if ($dom === false) {
+            $rlt = $this->Error('inv');
+        } else if ($dom === true && $this->NameSpaces()) {
+            $this->count = 1;
+            $rlt = $this->dom;
+        } else if (is_object($dom) && $this->CheckSource($dom)) {
+            $this->Merge($dom, '/');  /* add to existing */
+            $this->count++;
+            $rlt = $this->dom;
+        } else {
+            $rlt = false;
+        }
+        return $rlt;
+    }
+
+    /**
+     * set stay/keep
+     * @param array $opt -- options
+     */
+    private function Protect($opt)
+    {
+        foreach ($this->ovw as $key => $val) {
+            if (isset($opt[$key])) {
+                if (is_array($opt[$key])) {
+                    $this->ovw[$key] = array_merge($val, $opt[$key]);
+                } else if (!empty($opt[$key])) {
+                    array_push($this->ovw[$key], $opt[$key]);
+                }
+            } else if (empty($this->ovw[$key])) {
+                array_push($this->ovw[$key], 'all');
+            }
+        }
+    }
+
+    /**
+     * check/modify root and namespaces,
+     * @param object $dom source
+     * @return {bool}
+     */
+    private function CheckSource(&$dom)
+    {
+        $rlt = true;
+        if (strcasecmp($dom->encoding, $this->dom->encoding) !== 0) {
+            $rlt = $this->Error('enc');
+        } else if ($dom->documentElement->namespaceURI != $this->dom->documentElement->namespaceURI) { /* $dom->documentElement->lookupnamespaceURI(NULL) */
+            $rlt = $this->Error('nse');
+        } else if ($dom->documentElement->nodeName != $this->dom->documentElement->nodeName) {
+            if (!$this->join) {
+                $rlt = $this->Error('dif');
+            } else if (is_string($this->join)) {
+                $doc = new DOMDocument();
+                $doc->encoding = $this->dom->encoding;
+                $doc->preserveWhiteSpace = false;
+                $doc->formatOutput = true;
+                $xml = "<?xml version=\"{$this->dom->xmlVersion}\" encoding=\"{$this->dom->encoding}\"?><$this->join></$this->join>";
+                if (@$doc->loadXML($xml)) {
+                    $tmp = $doc->importNode($this->dom->documentElement, true);
+                    $doc->documentElement->appendChild($tmp);
+                    $this->dom = $doc;
+                    $this->join = true;
+                } else {
+                    $rlt = $this->Error('jne');
+                    $this->join = null;
+                }
+            }
+        }
+        if ($rlt) {
+            $doc = simplexml_import_dom($dom);
+            $rlt = $this->NameSpaces($doc->getDocNamespaces(true));
+        }
+        return $rlt;
+    }
+
+    /**
+     * register namespaces
+     * @param array $nsp -- additional namespaces
+     * @return bool
+     */
+    private function NameSpaces($nsp = array())
+    {
+        $doc = simplexml_import_dom($this->dom);
+        $nsps = $doc->getDocNamespaces(true);
+        foreach ($nsp as $pfx => $url) {
+            if (!isset($nsps[$pfx])) {
+                $this->dom->createAttributeNS($url, "$pfx:attr");
+                $nsps[$pfx] = $url;
+            }
+        }
+        $this->dxp = new DOMXPath($this->dom);
+        $this->nsp = array();
+        $rlt = true;
+        foreach ($nsps as $pfx => $url) {
+            if ($pfx == $this->nsd) {
+                $rlt = $this->Error('nse');
+                break;
+            } else if (empty($pfx)) {
+                $pfx = $this->nsd;
+            }
+            $this->nsp[$pfx] = $url;
+            $this->dxp->registerNamespace($pfx, $url);
+        }
+        return $rlt;
+    }
+
+    /**
+     * join 2 dom objects
+     * @param object $src -- current source node
+     * @param object $pth -- current source path
+     */
+    private function Merge($src, $pth)
+    {
+        $i = 0;
+        foreach ($src->childNodes as $node) {
+            $path = $this->GetNodePath($src->childNodes, $node, $pth, $i);
+            $obj = $this->Query($path);
+            if ($node->nodeType === XML_ELEMENT_NODE) {
+                $flg = true;  /* replace existing node by default */
+                if ($obj->length == 0 || $obj->item(0)->namespaceURI != $node->namespaceURI) { /* add node */
+                    $tmp = $this->dom->importNode($node, true);
+                    $this->Query($pth)->item(0)->appendChild($tmp);
+                } else {
+                    if (array_search($obj->item(0)->getAttribute('stay'), $this->ovw['stay']) !== false ||
+                        (array_search($node->getAttribute('keep'), $this->ovw['keep']) !== false &&
+                            array_search($obj->item(0)->getAttribute('keep'), $this->ovw['keep']) === false)) {
+                        $flg = false; /* don't replace */
+                    }
+                    if ($flg && $node->hasAttributes()) { /* add/replace attributes */
+                        foreach ($node->attributes as $attr) {
+                            $obj->item(0)->setAttribute($attr->nodeName, $attr->nodeValue);
+                        }
+                    }
+                }
+                if ($node->hasChildNodes() && $flg) {
+                    $this->Merge($node, $path); /* recurse to subnodes */
+                }
+            } else if ($node->nodeType === XML_TEXT_NODE || $node->nodeType === XML_COMMENT_NODE) { /* leaf node */
+                if ($obj->length == 0) {
+                    if ($node->nodeType === XML_TEXT_NODE) {
+                        $tmp = $this->dom->createTextNode($node->nodeValue);
+                    } else {
+                        $tmp = $this->dom->createComment($node->nodeValue);
+                    }
+                    $this->Query($pth)->item(0)->appendChild($tmp);
+                } else {
+                    $obj->item(0)->nodeValue = $node->nodeValue;
+                }
+            }
+            $i++;
+        }
+    }
+
+    /**
+     * form the node xPath expression
+     * @param {object} $nodes -- child nodes
+     * @param {object} $node -- current child
+     * @param {string} $pth -- parent path
+     * @param {int} $eln -- element sequence number
+     * @return {string} query path
+     */
+    private function GetNodePath($nodes, $node, $pth, $eln)
+    {
+        $j = 0;
+        if ($node->nodeType === XML_ELEMENT_NODE) {
+            $i = 0;
+            foreach ($nodes as $nde) {
+                if ($i > $eln) {
+                    break;
+                } else if (($this->updn && $nde->nodeType === $node->nodeType && $nde->nodeName === $node->nodeName && $nde->namespaceURI === $node->namespaceURI) ||
+                    (!$this->updn && $nde->nodeType !== XML_PI_NODE)) {
+                    $j++;
+                }
+                $i++;
+            }
+            if ($this->updn) {
+                if ($node->prefix) {
+                    $p = $node->prefix . ':';
+                } else if (isset($this->nsp[$this->nsd])) {
+                    $p = $this->nsd . ':';
+                } else {
+                    $p = '';
+                }
+                $p .= $node->localName;
+            } else {
+                $p = 'node()';
+            }
+        } else if ($node->nodeType === XML_TEXT_NODE || $node->nodeType === XML_COMMENT_NODE) {
+            $i = 0;
+            foreach ($nodes as $nde) {
+                if ($i > $eln) {
+                    break;
+                } else if ($nde->nodeType === $node->nodeType) {
+                    $j++;
+                }
+                $i++;
+            }
+            $p = $node->nodeType === XML_TEXT_NODE ? 'text()' : 'comment()';
+        } else {
+            $p = $pth;
+        }
+        if ($j > 0) {
+            $p = $pth . ($pth === '/' ? '' : '/') . $p . '[' . $j . ']';
+        }
+        return $p;
+    }
+
+    /**
+     * xPath query
+     * @param string $qry -- query statement
+     * @return object
+     */
+    public function Query($qry)
+    {
+        if ($this->join === true) {
+            $qry = "/{$this->dom->documentElement->nodeName}" . ($qry === '/' ? '' : $qry);
+        }
+        $rlt = $this->dxp->query($qry);
+        return $rlt;
+    }
+
+    /**
+     * get result
+     * @param {int} flg -- 0 - object
+     *                     1 - xml
+     *                     2 - html
+     * @return {mixed}
+     */
+    public function Get($flg = 0)
+    {
+        if ($flg == 0) {
+            $rlt = $this->dom;
+        } else {
+            $rlt = $this->dom->saveXML();
+            if ($flg == 2) {
+                $r = str_replace(' ', '&nbsp;', htmlspecialchars($rlt));
+                $rlt = str_replace(array("\r\n", "\n", "\r"), '<br />', $r);
+            }
+        }
+        return $rlt;
+    }
+
+    /**
+     * set error message
+     * @param string $err token
+     * @return false
+     */
+    private function Error($err = 'und')
+    {
+        $errs = array(
+            'nod' => "$this->cln is not supported",
+            'nof' => 'File not found',
+            'emf' => 'File is empty', /* possible delivery fault */
+            'inv' => 'Invalid XML source',
+            'enc' => 'Different encoding',
+            'dif' => 'Different root nodes',
+            'jne' => 'Invalid join parameter',
+            'nse' => 'Namespace incompatibility',
+            'und' => 'Undefined error');
+        $this->error->code = isset($errs[$err]) ? $err : 'und';
+        $this->error->text = $errs[$this->error->code];
+        return false;
+    }
+
+    /**
+     * get property value
+     * @param string $name
+     * @return mixed -- null - missing
+     */
+    public function __get($name)
+    {
+        return isset($this->$name) ? $this->$name : null;
+    }
+}
+
+/**
+ * Minify XML source
+ *
+ * @package Packer
+ * @author Vallo Reima
+ * @copyright (C)2015
+ */
+class PackXML
+{
+    private $input;
+    private $nodes = [];  /* node objects to remove */
+    /**
+     * @param string $source
+     * @param array $options
+     * @return mixed -- string - ok
+     */
+    public static function minify($source, $options = [])
+    {
+        $min = new self($source);
+        return $min->process();
+    }
+
+    /**
+     * @param string $input
+     */
+    public function __construct($input)
+    {
+        $this->input = $input;
+    }
+
+    /**
+     * minify
+     * @return string|false
+     */
+    private function process()
+    {
+        $dom = new DOMDocument();
+        $dom->preserveWhiteSpace = false;
+        $dom->formatOutput = false;
+        if (@$dom->loadXML($this->input)) {
+            $this->Detect($dom);  // fix excessive nodes
+            foreach ($this->nodes as $node) {
+                $node->parentNode->removeChild($node);  // remove fixed nodes
+            }
+            $rlt = $dom->saveXML(); // convert to string
+        } else {  // bad content
+            $rlt = false;
+        }
+        return $rlt;
+    }
+
+    /**
+     * collect excessive node objects
+     * @param object $root
+     */
+    private function Detect($root)
+    {
+        foreach ($root->childNodes as $node) {
+            if ($node->nodeType == XML_COMMENT_NODE || ($node->nodeType == XML_TEXT_NODE && trim($node->nodeValue) == '')) {
+                array_push($this->nodes, $node);  // comment or empty text
+            } else if ($node->nodeType == XML_ELEMENT_NODE) {
+                $this->Detect($node); // recurse subnodes
+            }
+        }
+    }
+}
\ No newline at end of file
index 9855f1e0087c22cdc5ef4fa4038eb8ea9c1765eb..e206e25c62243ec30ba670fcc026ab7d9604b075 100644 (file)
 class wsTools
 {
 
-       protected static $_r;
-       protected static $_i;
-       protected static $_e;
-       protected static $_u;
-
-       public static function encodeWebVideos($file, $dir = null, $async = true, $force = false, $format = 'all')
-       {
-               if (is_null($dir)) {
-                       $dir = dirname($file);
-               }
-
-               $videos = array('mp4', 'jpg');
-
-               if (is_string($format)) {
-                       if ($format == 'none') {
-                               $format = array();
-                       } elseif ($format == 'all') {
-                               $format = $videos;
-                       } else {
-                               $format = array($format);
-                       }
-               }
-
-               if (!$force) {
-                       $format = array();
-               }
-
-               $base = $dir . '/' . cubeFiles::getName($file);
-               $log = $base . '.log';
-
-               foreach ($videos as $v) {
-                       $vfile = $base . '.' . $v;
-                       if (!file_exists($vfile)) {
-                               $force = true;
-                       } else if (filemtime($file) > filemtime($vfile) || in_array($v, $format)) {
-                               $force = true;
-                               unlink($vfile);
-                       }
-               }
-
-               if (!$force && file_exists($log) && filemtime($log) > filemtime($file)) {
-                       return;
-               }
-
-               $webvideo = new cubeCommandLine('webvideo', $log);
-               $webvideo->setPath(CONVERTER_PATH);
-               if ($async) {
-                       $webvideo->setNohup(true);
-               }
-               $webvideo->setArg(null, $file);
-               $webvideo->setArg(null, $dir);
-               $webvideo->execute();
-       }
-
-       public static function colorizeAndRasterizeIcon($iconSet, $icon, $colors, $dest, $scale, &$w, &$h, $makepng = true)
-       {
-               // Init directory
-               if (is_string($colors)) {
-                       $colors = array('colorize' => $colors);
-               }
-               $hash = sha1(json_encode($colors));
-               foreach ($colors as $k => $v) {
-                       $colors[$k] = wsHTML5::colorToArray($colors[$k]);
-               }
-
-               $e = explode('-', $icon);
-               $type = $e[0];
-
-               if ($type == 'nav') {
-                       $svgRef = WS_ICONS . '/' . $iconSet . '/mobile/' . $icon . '.svg';
-                       if (!file_exists($svgRef)) {
-                               $iconSet = 1;
-                               $svgRef = WS_ICONS . '/' . $iconSet . '/mobile/' . $icon . '.svg';
-                       }
-
-                       $dirColorized = WS_ICONS . '/' . $iconSet . '/mobile/colorized/' . $hash . '/';
-                       $svgColorized = $dirColorized . '/' . $icon . '.svg';
-               } else {
-                       $dirColorized = WS_ICONS . '/' . $type . '/colorized/' . $hash;
-                       $svgRef = WS_ICONS . '/' . $type . '/' . $icon . '.svg';
-                       $svgColorized = $dirColorized . '/' . $icon . '.svg';
-               }
-
-               if (!file_exists($dirColorized)) {
-                       mkdir($dirColorized, 0777, true);
-               }
-
-
-               // SVG
-               $time = max(filemtime(__FILE__), filemtime($svgRef));
-               if (!file_exists($svgColorized) || filemtime($svgColorized) <= $time) {
-                       $svg = file_get_contents($svgRef);
-                       // Colorize it
-                       foreach ($colors as $k => $v) {
-                               $replace = "#" . $v['hex'];
-                               if ($v['opacity'] < 1) {
-                                       $replace .= '" stroke-opacity="' . $v['opacity'];
-                               }
-                               $svg = str_replace('$s' . $k, $replace, $svg);
-
-
-                               $replace = "#" . $v['hex'];
-                               if ($v['opacity'] < 1) {
-                                       $replace .= '" fill-opacity="' . $v['opacity'];
-                               }
-                               $svg = str_replace('$' . $k, $replace, $svg);
-                       }
-                       file_put_contents($svgColorized, $svg);
-               }
-               self::copy($svgColorized, $dest . '/' . $icon . '.svg');
-
-               // PNG
-               $png = $dirColorized . '/' . $icon . '.png';
-               $time = max(filemtime(__FILE__), filemtime($svgColorized));
-               if (!file_exists($png) || filemtime($png) <= $time) {
-                       $svg = simplexml_load_file($svgColorized);
-                       $w = (string)$svg['width'];
-                       $h = (string)$svg['height'];
-                       $w = rtrim($w, 'px');
-                       $h = rtrim($h, 'px');
-                       // Finally rasterize it
-                       $batik = new cubeCommandLine('inkscape');
-                       $batik->setArg('z');
-                       $batik->setArg('e', $png);
-                       $batik->setArg('w', floatval($w) * $scale);
-                       $batik->setArg('h', floatval($h) * $scale);
-                       $batik->setManualArg($svgColorized);
-                       $batik->execute();
-               }
-
-               if (file_exists($png)) {
-                       $dim = getimagesize($png);
-                       $w = $dim[0] / $scale;
-                       $h = $dim[1] / $scale;
-               }
-
-               if ($makepng) {
-                       self::copy($png, $dest . '/' . $icon . '.png');
-               }
-       }
-
-       public static function optimizeSVG($original, $optimized, $resolutions = [], $force = false)
-       {
-
-               if (!file_exists($original)) {
-                       return 'doesnt exists';
-               }
-
-               $baseoptimized = str_replace('%s', '', $optimized);
-
-               $expireoriginallimit = max(filemtime($original), filemtime(__FILE__));
-
-               $notexists = !file_exists($baseoptimized);
-               if (!$notexists) {
-                       $cleanerexpired = filemtime($baseoptimized) < filemtime('/usr/local/bin/svgcleaner');
-                       $originalexpired = filemtime($baseoptimized) < $expireoriginallimit;
-               } else {
-                       $cleanerexpired = false;
-                       $originalexpired = false;
-               }
-
-               $optimize = $force ||
-                       $notexists ||
-                       $cleanerexpired ||
-                       $originalexpired;
-
-               if ($optimize) {
-                       $cmd = "timeout -s 1 120 /usr/local/bin/svgcleaner --allow-bigger-file --paths-coordinates-precision 3 --copy-on-error --stdout $original";
-                       $svg = `$cmd`;
-                       if ($svg == '') {
-                               $svg = file_get_contents($original);
-                       }
-                       $svg = substr_replace($svg, ' preserveAspectRatio="none" ', 5, 0);
-
-                       $fname = $baseoptimized;
-                       file_put_contents($fname, $svg);
-
-                       if (count($resolutions) > 0) {
-                               foreach ($resolutions as $resolution) {
-                                       self::$_r = $resolution;
-                                       $osvg = preg_replace_callback('|\<image([^>]*)\>|', 'wsTools::optimizeRaster', $svg);
-                                       $osvg = preg_replace('/^<svg[^>]*>/', '$0<rect width="100%" height="100%" fill="white" />', $osvg);
-                                       $ofname = sprintf($optimized, '-' . $resolution);
-                                       file_put_contents($ofname, $osvg);
-                               }
-                       }
-                       return true;
-               }
-               return false;
-       }
-
-       public static function optimizeRaster($matches)
-       {
-               preg_match_all('/([a-z\:\-]*)="([^"]*)"/', $matches[1], $m);
-
-               foreach ($m[1] as $i => $key) {
-                       $attrs[$key] = $m[2][$i];
-               }
-               $e = explode(',', $attrs['xlink:href'], 2);
-               $image = imagecreatefromstring(base64_decode($e[1]));
-               $iw = imagesx($image);
-               $ih = imagesy($image);
-               $scale = 1;
-               if (isset($attrs['transform']) && strpos($attrs['transform'], 'matrix(') === 0) {
-                       preg_match_all('/([\d\-\.]+)/', $attrs['transform'], $ma);
-                       $values = $ma[0];
-                       $scale = max($values[0], $values[1]);
-               }
-
-               $resolutionScale = ($iw / $attrs['width']) * $scale * (self::$_r / 72);
-
-               $dw = round($resolutionScale * $iw);
-               $dh = round($resolutionScale * $ih);
-
-               $im = imagecreatetruecolor($dw, $dh);
-               imagecopyresampled($im, $image, 0, 0, 0, 0, $dw, $dh, $iw, $ih);
-               imagedestroy($image);
-
-               $tmp = CubeIT_Files::tempnam();
-               imagejpeg($im, $tmp, 85);
-               imagedestroy($im);
-
-               $attrs['xlink:href'] = 'data:image/jpeg;base64,' . base64_encode(file_get_contents($tmp));
-               unlink($tmp);
-
-               $res = '<image ';
-               foreach ($attrs as $k => $v) {
-                       $res .= $k . '="' . $v . '" ';
-               }
-               $res .= '/>';
-               return $res;
-       }
-
-       public static function copy($source, $dest)
-       {
-               if (!file_exists($source)) {
-                       return;
-               }
-               $dir = dirname($dest);
-               if (!file_exists($dir)) {
-                       mkdir($dir, 0777, true);
-               }
-               copy($source, $dest);
-               touch($dest, filemtime($source));
-       }
-
-       public static function pages2html($book_id, $page)
-       {
-               global $core;
-               if ($page % 2 == 1) {
-                       $page--;
-               }
-
-               $convert = [$page, $page + 1];
-
-               $dao = new wsDAOBook($core->con);
-               $pages = $dao->getPagesOfBook($book_id);
-
-               $svg = '';
-               $left = 0;
-               $css = '';
-               foreach ($convert as $item) {
-
-                       if (isset($pages[$item])) {
-                               $p = $pages[$item];
-                               $dir = wsDocument::getDir($p['document_id']);
-                               $f = $dir . '/html/fo' . $p['document_page'] . '-150.svg';
-
-                               if (!isset($width)) {
-                                       $s = CubeIT_Image::getimagesize($f);
-                                       $width = $s[0];
-                                       $height = $s[1];
-                               }
-
-                               $svg .= '<div class="page" id="p' . $item . '">';
-                               $svg .= self::_svg(file_get_contents($f), $item);
-                               $svg .= '</div>';
-                               $css .= '#p' . $item . '{left:' . $left . 'px;}';
-                               $left += ($width - 1);
-                       }
-               }
-
-               $res = '<html>';
-               $res .= '<head>';
-               $res .= '<meta name="width" value="' . $left . '">';
-               $res .= '<meta name="height" value="' . $height . '">';
-               $res .= '<!-- tweenmax --><script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/2.0.2/TweenMax.min.js"></script><!-- -->';
-               $res .= '<!-- jquery --><script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script><!-- -->';
-               $res .= '<script src="main.js"></script>';
-               $res .= '<style type="text/css">*{padding:0;margin:0}svg{display: block;}.page{position: absolute;top: 0;}.page svg{width:' . $width . 'px;height:' . $height . 'px;}' . $css . '</style></head>';
-               $res .= '<link href="main.css" type="text/css" rel="stylesheet" />';
-               $res .= '<body>';
-               $res .= $svg;
-               $res .= '</body>';
-               $res .= '</html>';
-
-               $n = 'fb_' . $book_id . '_' . $page . '.zip';
-               $zipname = '/tmp/' . $n;
-               $zip = new ZipArchive();
-               $zip->open($zipname, ZipArchive::CREATE);
-               $zip->addFromString('index.html', $res);
-               $zip->close();
-               return ['name' => $n, 'path' => $zipname];
-       }
-
-       protected static function _svg($c, $p)
-       {
-               self::$_i = 0;
-               self::$_e = 0;
-               self::$_u = 0;
-
-               $c = str_replace('id="', 'id="p' . $p . '-', $c);
-               $c = str_replace('url(#', 'url(#p' . $p . '-', $c);
-               $c = str_replace('xlink:href="#', 'xlink:href="#p' . $p . '-', $c);
-               $c = preg_replace_callback('/\<image([^\>]*)\>/m', function ($matches) use ($p) {
-                       self::$_i++;
-                       return '<g id="p' . $p . '-imageholder-' . self::$_i . '"><image id="p' . $p . '-image-' . self::$_i . '" ' . $matches[1] . '></g>';
-               }, $c);
-               $c = str_replace('</image>', '</image></g></g>', $c);
-
-               $c = preg_replace_callback('/<path /', function () use ($p) {
-                       self::$_e++;
-                       return '<path id="p' . $p . '-e-' . self::$_e . '" ';
-               }, $c);
-               $c = preg_replace_callback('/<use /', function () use ($p) {
-                       self::$_u++;
-                       return '<use id="p' . $p . '-u-' . self::$_u . '" ';
-               }, $c);
-
-               return $c;
-       }
-
-
-       public function chiffres()
-       {
-
-       }
+    protected static $_r;
+    protected static $_i;
+    protected static $_e;
+    protected static $_u;
+
+    public static function encodeWebVideos($file, $dir = null, $async = true, $force = false, $format = 'all')
+    {
+        if (is_null($dir)) {
+            $dir = dirname($file);
+        }
+
+        $videos = array('mp4', 'jpg');
+
+        if (is_string($format)) {
+            if ($format == 'none') {
+                $format = array();
+            } elseif ($format == 'all') {
+                $format = $videos;
+            } else {
+                $format = array($format);
+            }
+        }
+
+        if (!$force) {
+            $format = array();
+        }
+
+        $base = $dir . '/' . cubeFiles::getName($file);
+        $log = $base . '.log';
+
+        foreach ($videos as $v) {
+            $vfile = $base . '.' . $v;
+            if (!file_exists($vfile)) {
+                $force = true;
+            } else if (filemtime($file) > filemtime($vfile) || in_array($v, $format)) {
+                $force = true;
+                unlink($vfile);
+            }
+        }
+
+        if (!$force && file_exists($log) && filemtime($log) > filemtime($file)) {
+            return;
+        }
+
+        $webvideo = new cubeCommandLine('webvideo', $log);
+        $webvideo->setPath(CONVERTER_PATH);
+        if ($async) {
+            $webvideo->setNohup(true);
+        }
+        $webvideo->setArg(null, $file);
+        $webvideo->setArg(null, $dir);
+        $webvideo->execute();
+    }
+
+    public static function colorizeAndRasterizeIcon($iconSet, $icon, $colors, $dest, $scale, &$w, &$h, $makepng = true)
+    {
+        // Init directory
+        if (is_string($colors)) {
+            $colors = array('colorize' => $colors);
+        }
+        $hash = sha1(json_encode($colors));
+        foreach ($colors as $k => $v) {
+            $colors[$k] = wsHTML5::colorToArray($colors[$k]);
+        }
+
+        $e = explode('-', $icon);
+        $type = $e[0];
+
+        if ($type == 'nav') {
+            $svgRef = WS_ICONS . '/' . $iconSet . '/mobile/' . $icon . '.svg';
+            if (!file_exists($svgRef)) {
+                $iconSet = 1;
+                $svgRef = WS_ICONS . '/' . $iconSet . '/mobile/' . $icon . '.svg';
+            }
+
+            $dirColorized = WS_ICONS . '/' . $iconSet . '/mobile/colorized/' . $hash . '/';
+            $svgColorized = $dirColorized . '/' . $icon . '.svg';
+        } else {
+            $dirColorized = WS_ICONS . '/' . $type . '/colorized/' . $hash;
+            $svgRef = WS_ICONS . '/' . $type . '/' . $icon . '.svg';
+            $svgColorized = $dirColorized . '/' . $icon . '.svg';
+        }
+
+        if (!file_exists($dirColorized)) {
+            mkdir($dirColorized, 0777, true);
+        }
+
+
+        // SVG
+        $time = max(filemtime(__FILE__), filemtime($svgRef));
+        if (!file_exists($svgColorized) || filemtime($svgColorized) <= $time) {
+            $svg = file_get_contents($svgRef);
+            // Colorize it
+            foreach ($colors as $k => $v) {
+                $replace = "#" . $v['hex'];
+                if ($v['opacity'] < 1) {
+                    $replace .= '" stroke-opacity="' . $v['opacity'];
+                }
+                $svg = str_replace('$s' . $k, $replace, $svg);
+
+
+                $replace = "#" . $v['hex'];
+                if ($v['opacity'] < 1) {
+                    $replace .= '" fill-opacity="' . $v['opacity'];
+                }
+                $svg = str_replace('$' . $k, $replace, $svg);
+            }
+            file_put_contents($svgColorized, $svg);
+        }
+        self::copy($svgColorized, $dest . '/' . $icon . '.svg');
+
+        // PNG
+        $png = $dirColorized . '/' . $icon . '.png';
+        $time = max(filemtime(__FILE__), filemtime($svgColorized));
+        if (!file_exists($png) || filemtime($png) <= $time) {
+            $svg = simplexml_load_file($svgColorized);
+            $w = (string)$svg['width'];
+            $h = (string)$svg['height'];
+            $w = rtrim($w, 'px');
+            $h = rtrim($h, 'px');
+            // Finally rasterize it
+            $batik = new cubeCommandLine('inkscape');
+            $batik->setArg('z');
+            $batik->setArg('e', $png);
+            $batik->setArg('w', floatval($w) * $scale);
+            $batik->setArg('h', floatval($h) * $scale);
+            $batik->setManualArg($svgColorized);
+            $batik->execute();
+        }
+
+        if (file_exists($png)) {
+            $dim = getimagesize($png);
+            $w = $dim[0] / $scale;
+            $h = $dim[1] / $scale;
+        }
+
+        if ($makepng) {
+            self::copy($png, $dest . '/' . $icon . '.png');
+        }
+    }
+
+    public static function optimizeSVG($original, $optimized, $resolutions = [], $force = false)
+    {
+
+        if (!file_exists($original)) {
+            return 'doesnt exists';
+        }
+
+        $baseoptimized = str_replace('%s', '', $optimized);
+
+        $expireoriginallimit = max(filemtime($original), filemtime(__FILE__));
+
+        $notexists = !file_exists($baseoptimized);
+        if (!$notexists) {
+            $cleanerexpired = filemtime($baseoptimized) < filemtime('/usr/local/bin/svgcleaner');
+            $originalexpired = filemtime($baseoptimized) < $expireoriginallimit;
+        } else {
+            $cleanerexpired = false;
+            $originalexpired = false;
+        }
+
+        $optimize = $force ||
+            $notexists ||
+            $cleanerexpired ||
+            $originalexpired;
+
+        if ($optimize) {
+            $cmd = "timeout -s 1 120 /usr/local/bin/svgcleaner --allow-bigger-file --paths-coordinates-precision 3 --copy-on-error --stdout $original";
+            $svg = `$cmd`;
+            if ($svg == '') {
+                $svg = file_get_contents($original);
+            }
+            $svg = substr_replace($svg, ' preserveAspectRatio="none" ', 5, 0);
+
+            $fname = $baseoptimized;
+            file_put_contents($fname, $svg);
+
+            if (count($resolutions) > 0) {
+                foreach ($resolutions as $resolution) {
+                    self::$_r = $resolution;
+                    $osvg = preg_replace_callback('|\<image([^>]*)\>|', 'wsTools::optimizeRaster', $svg);
+                    $osvg = preg_replace('/^<svg[^>]*>/', '$0<rect width="100%" height="100%" fill="white" />', $osvg);
+                    $ofname = sprintf($optimized, '-' . $resolution);
+                    file_put_contents($ofname, $osvg);
+                }
+            }
+            return true;
+        }
+        return false;
+    }
+
+    public static function optimizeRaster($matches)
+    {
+        preg_match_all('/([a-z\:\-]*)="([^"]*)"/', $matches[1], $m);
+
+        foreach ($m[1] as $i => $key) {
+            $attrs[$key] = $m[2][$i];
+        }
+        $e = explode(',', $attrs['xlink:href'], 2);
+        $image = imagecreatefromstring(base64_decode($e[1]));
+        $iw = imagesx($image);
+        $ih = imagesy($image);
+        $scale = 1;
+        if (isset($attrs['transform']) && strpos($attrs['transform'], 'matrix(') === 0) {
+            preg_match_all('/([\d\-\.]+)/', $attrs['transform'], $ma);
+            $values = $ma[0];
+            $scale = max($values[0], $values[1]);
+        }
+
+        $resolutionScale = ($iw / $attrs['width']) * $scale * (self::$_r / 72);
+
+        $dw = round($resolutionScale * $iw);
+        $dh = round($resolutionScale * $ih);
+
+        $im = imagecreatetruecolor($dw, $dh);
+        imagecopyresampled($im, $image, 0, 0, 0, 0, $dw, $dh, $iw, $ih);
+        imagedestroy($image);
+
+        $tmp = CubeIT_Files::tempnam();
+        imagejpeg($im, $tmp, 85);
+        imagedestroy($im);
+
+        $attrs['xlink:href'] = 'data:image/jpeg;base64,' . base64_encode(file_get_contents($tmp));
+        unlink($tmp);
+
+        $res = '<image ';
+        foreach ($attrs as $k => $v) {
+            $res .= $k . '="' . $v . '" ';
+        }
+        $res .= '/>';
+        return $res;
+    }
+
+    public static function copy($source, $dest)
+    {
+        if (!file_exists($source)) {
+            return;
+        }
+        $dir = dirname($dest);
+        if (!file_exists($dir)) {
+            mkdir($dir, 0777, true);
+        }
+        copy($source, $dest);
+        touch($dest, filemtime($source));
+    }
+
+    public static function pages2html($book_id, $page)
+    {
+        global $core;
+        if ($page % 2 == 1) {
+            $page--;
+        }
+
+        $convert = [$page, $page + 1];
+
+        $dao = new wsDAOBook($core->con);
+        $pages = $dao->getPagesOfBook($book_id);
+
+        $svg = '';
+        $left = 0;
+        $css = '';
+        foreach ($convert as $item) {
+
+            if (isset($pages[$item])) {
+                $p = $pages[$item];
+                $dir = wsDocument::getDir($p['document_id']);
+                $f = $dir . '/html/fo' . $p['document_page'] . '-150.svg';
+
+                if (!isset($width)) {
+                    $s = CubeIT_Image::getimagesize($f);
+                    $width = $s[0];
+                    $height = $s[1];
+                }
+
+                $svg .= '<div class="page" id="p' . $item . '">';
+                $svg .= self::_svg(file_get_contents($f), $item);
+                $svg .= '</div>';
+                $css .= '#p' . $item . '{left:' . $left . 'px;}';
+                $left += ($width - 1);
+            }
+        }
+
+        $res = '<html>';
+        $res .= '<head>';
+        $res .= '<meta name="width" value="' . $left . '">';
+        $res .= '<meta name="height" value="' . $height . '">';
+        $res .= '<!-- tweenmax --><script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/2.0.2/TweenMax.min.js"></script><!-- -->';
+        $res .= '<!-- jquery --><script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script><!-- -->';
+        $res .= '<script src="main.js"></script>';
+        $res .= '<style type="text/css">*{padding:0;margin:0}svg{display: block;}.page{position: absolute;top: 0;}.page svg{width:' . $width . 'px;height:' . $height . 'px;}' . $css . '</style></head>';
+        $res .= '<link href="main.css" type="text/css" rel="stylesheet" />';
+        $res .= '<body>';
+        $res .= $svg;
+        $res .= '</body>';
+        $res .= '</html>';
+
+        $n = 'fb_' . $book_id . '_' . $page . '.zip';
+        $zipname = '/tmp/' . $n;
+        $zip = new ZipArchive();
+        $zip->open($zipname, ZipArchive::CREATE);
+        $zip->addFromString('index.html', $res);
+        $zip->close();
+        return ['name' => $n, 'path' => $zipname];
+    }
+
+    protected static function _svg($c, $p)
+    {
+        self::$_i = 0;
+        self::$_e = 0;
+        self::$_u = 0;
+
+        $c = str_replace('id="', 'id="p' . $p . '-', $c);
+        $c = str_replace('url(#', 'url(#p' . $p . '-', $c);
+        $c = str_replace('xlink:href="#', 'xlink:href="#p' . $p . '-', $c);
+        $c = preg_replace_callback('/\<image([^\>]*)\>/m', function ($matches) use ($p) {
+            self::$_i++;
+            return '<g id="p' . $p . '-imageholder-' . self::$_i . '"><image id="p' . $p . '-image-' . self::$_i . '" ' . $matches[1] . '></g>';
+        }, $c);
+        $c = str_replace('</image>', '</image></g></g>', $c);
+
+        $c = preg_replace_callback('/<path /', function () use ($p) {
+            self::$_e++;
+            return '<path id="p' . $p . '-e-' . self::$_e . '" ';
+        }, $c);
+        $c = preg_replace_callback('/<use /', function () use ($p) {
+            self::$_u++;
+            return '<use id="p' . $p . '-u-' . self::$_u . '" ';
+        }, $c);
+
+        return $c;
+    }
+
+
+    public function chiffres()
+    {
+
+    }
+
 
 
 }
diff --git a/quiz/imsmanifest.xml b/quiz/imsmanifest.xml
new file mode 100644 (file)
index 0000000..1b6416d
--- /dev/null
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>\r
+<manifest xmlns="http://www.imsproject.org/xsd/imscp_rootv1p1p2"\r
+          xmlns:imsmd="http://www.imsglobal.org/xsd/imsmd_rootv1p2p1"\r
+          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:adlcp="http://www.adlnet.org/xsd/adlcp_rootv1p2"\r
+          identifier="MANIFEST-90878C16-EB60-D648-94ED-9651972B5F42"\r
+          xsi:schemaLocation="http://www.imsproject.org/xsd/imscp_rootv1p1p2 imscp_rootv1p1p2.xsd http://www.imsglobal.org/xsd/imsmd_rootv1p2p1 imsmd_rootv1p2p1.xsd http://www.adlnet.org/xsd/adlcp_rootv1p2 adlcp_rootv1p2.xsd">\r
+    <metadata>\r
+        <schema>ADL SCORM</schema>\r
+        <schemaversion>1.2</schemaversion>\r
+    </metadata>\r
+    <organizations default="hf">\r
+        <organization identifier="hf" structure="hierarchical">\r
+            <title>$title</title>\r
+            <item identifier="hfq" identifierref="hfq" isvisible="true">\r
+                <title>$title</title>\r
+            </item>\r
+        </organization>\r
+    </organizations>\r
+    <resources>\r
+        <resource type="webcontent" adlcp:scormtype="sco" identifier="hfq" href="index.html">\r
+            <file href="index.html"/>\r
+        </resource>\r
+    </resources>\r
+</manifest>
\ No newline at end of file
diff --git a/quiz/template.zip b/quiz/template.zip
new file mode 100644 (file)
index 0000000..38495f3
Binary files /dev/null and b/quiz/template.zip differ