From: vincent@cubedesigners.com Date: Thu, 16 Jan 2020 18:02:20 +0000 (+0000) Subject: wip #3335 @1.25 X-Git-Url: http://git.cubedesigners.com/?a=commitdiff_plain;h=99683805a5374cebb4cd38af3d77bde35adf7979;p=cubeextranet.git wip #3335 @1.25 --- diff --git a/inc/commons/class.common.tools.php b/inc/commons/class.common.tools.php index 5fff8167d..4d35851d0 100644 --- a/inc/commons/class.common.tools.php +++ b/inc/commons/class.common.tools.php @@ -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 .= '
'; + $res .= ''; + $res .= ''; + $res .= ''; + $res .= ''; + $res .= ''; + $res .= '
' . __('Update your quiz') . '
Please upload your quiz (ZIP file expected)
' . $core->typo->BoutonOK(__('Proceed to update')) . '
'; + $res .= '
'; + $res .= ''; + $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 = "dom->xmlVersion}\" encoding=\"{$this->dom->encoding}\"?><$this->join>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(' ', ' ', htmlspecialchars($rlt)); + $rlt = str_replace(array("\r\n", "\n", "\r"), '
', $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 diff --git a/inc/ws/Util/class.ws.tools.php b/inc/ws/Util/class.ws.tools.php index 9855f1e00..e206e25c6 100644 --- a/inc/ws/Util/class.ws.tools.php +++ b/inc/ws/Util/class.ws.tools.php @@ -3,343 +3,344 @@ 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('|\]*)\>|', 'wsTools::optimizeRaster', $svg); - $osvg = preg_replace('/^]*>/', '$0', $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 = ' $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 .= '
'; - $svg .= self::_svg(file_get_contents($f), $item); - $svg .= '
'; - $css .= '#p' . $item . '{left:' . $left . 'px;}'; - $left += ($width - 1); - } - } - - $res = ''; - $res .= ''; - $res .= ''; - $res .= ''; - $res .= ''; - $res .= ''; - $res .= ''; - $res .= ''; - $res .= ''; - $res .= ''; - $res .= $svg; - $res .= ''; - $res .= ''; - - $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('/\]*)\>/m', function ($matches) use ($p) { - self::$_i++; - return ''; - }, $c); - $c = str_replace('', '', $c); - - $c = preg_replace_callback('/ 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('|\]*)\>|', 'wsTools::optimizeRaster', $svg); + $osvg = preg_replace('/^]*>/', '$0', $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 = ' $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 .= '
'; + $svg .= self::_svg(file_get_contents($f), $item); + $svg .= '
'; + $css .= '#p' . $item . '{left:' . $left . 'px;}'; + $left += ($width - 1); + } + } + + $res = ''; + $res .= ''; + $res .= ''; + $res .= ''; + $res .= ''; + $res .= ''; + $res .= ''; + $res .= ''; + $res .= ''; + $res .= ''; + $res .= $svg; + $res .= ''; + $res .= ''; + + $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('/\]*)\>/m', function ($matches) use ($p) { + self::$_i++; + return ''; + }, $c); + $c = str_replace('', '', $c); + + $c = preg_replace_callback('/ + + + ADL SCORM + 1.2 + + + + $title + + $title + + + + + + + + + \ No newline at end of file diff --git a/quiz/template.zip b/quiz/template.zip new file mode 100644 index 000000000..38495f3b1 Binary files /dev/null and b/quiz/template.zip differ