From: Vincent Vanwaelscappel Date: Mon, 19 Dec 2022 15:24:59 +0000 (+0100) Subject: wip #5661 @2 X-Git-Url: http://git.cubedesigners.com/?a=commitdiff_plain;h=2d5bd969f9f88fdec4a9a451952673d9e4eab508;p=fluidbook-toolbox.git wip #5661 @2 --- diff --git a/app/Console/Commands/FluidbookFarmPing.php b/app/Console/Commands/FluidbookFarmPing.php index bdbf351fe..d7ef3c62b 100644 --- a/app/Console/Commands/FluidbookFarmPing.php +++ b/app/Console/Commands/FluidbookFarmPing.php @@ -3,7 +3,7 @@ namespace App\Console\Commands; -use App\Util\FluidbookFarm; +use App\Fluidbook\Farm; use Cubist\Backpack\Console\Commands\CubistCommand; @@ -14,6 +14,6 @@ class FluidbookFarmPing extends CubistCommand public function handle() { - FluidbookFarm::ping(true, $this->option('force', false)); + Farm::ping(true, $this->option('force', false)); } } diff --git a/app/Fluidbook/Farm.php b/app/Fluidbook/Farm.php new file mode 100644 index 000000000..b14290dd1 --- /dev/null +++ b/app/Fluidbook/Farm.php @@ -0,0 +1,148 @@ + 'alphaville', 'host' => 'fluidbook-processfarm', 'port' => 9000, 'weight' => 24], + ['name' => 'brazil', 'host' => 'brazil.cubedesigners.com', 'weight' => 6], + ['name' => 'clockwork', 'host' => 'clockwork.cubedesigners.com', 'weight' => 2], + ['name' => 'dracula', 'host' => 'dracula.cubedesigners.com', 'weight' => 3], + ['name' => 'elephantman', 'host' => 'elephantman.cubedesigners.com', 'weight' => 1], + ['name' => 'fastandfurious', 'host' => 'fastandfurious.cubedesigners.com', 'weight' => 1], + ['name' => 'godzilla', 'host' => 'godzilla.cubedesigners.com', 'weight' => 3], + ['name' => 'her', 'host' => 'her2.cubedesigners.com', 'weight' => 4], + ['name' => 'isleofdogs', 'host' => 'paris.cubedesigners.com', 'port' => 9458, 'weight' => 2], + ['name' => 'jumanji', 'host' => 'paris.cubedesigners.com', 'port' => 9459, 'weight' => 2], + ]; + + protected static function _pingCache() + { + return Files::mkdir(storage_path('fluidbookfarm')) . '/pings'; + } + + protected static function _serversCache() + { + return Files::mkdir(storage_path('fluidbookfarm')) . '/servers'; + } + + public static function getServers() + { + return self::$_farmServers; + } + + public static function pickOneServer() + { + $hat = []; + $pingCache = self::_pingCache(); + if (!file_exists($pingCache)) { + self::ping(false); + } + $pings = json_decode(file_get_contents(self::_pingCache())); + + foreach (self::$_farmServers as $k => $farmServer) { + if (!isset($pings[$k]) || !$pings[$k]) { + continue; + } + for ($i = 0; $i < $farmServer['weight']; $i++) { + $hat[] = $k; + } + } + shuffle($hat); + $i = array_pop($hat); + return self::$_farmServers[$i]; + } + + public static function getFCGIConnexion(array $farm, $timeout = 240): NetworkSocket + { + $timeout *= 1000; + return new NetworkSocket($farm['host'], $farm['port'] ?? 9457, $timeout, $timeout); + } + + public static function sendRequest($farmer, $url, $params = [], $timeout = 240) + { + set_time_limit(0); + $client = new Client(); + $response = $client->sendRequest(self::getFCGIConnexion($farmer, $timeout), new PostRequest($url, http_build_query($params))); + return trim($response->getBody()); + } + + public static function getFile($page, $format, $resolution, $withText, $withGraphics, $version, $resolutionRatio, $mobileFirstRatio, $path, $force = false) + { + $start = microtime(true); + $farmer = self::pickOneServer(); + + $params = ['page' => $page, 'format' => $format, 'resolution' => $resolution, 'withText' => $withText, 'withGraphics' => $withGraphics, 'version' => $version, 'force' => $force, 'out' => $path, 'resolutionRatio' => $resolutionRatio, 'mobileRatio' => $mobileFirstRatio]; + + $output = self::sendRequest($farmer, 'process.php', $params); + if (preg_match('|/data1/extranet/www/[^\s]+|', $output, $matches)) { + $o = $matches[0]; + } else { + $o = $output; + } + + if (file_exists($o)) { + $res = $o; + } else { + echo $o; + $res = false; + } + + $time = round(microtime(true) - $start, 4); + $log = '[' . $farmer['name'] . ']' . "\t" . date('Y-m-d H:i:s') . "\t" . $time . "\t$page|$format|$resolution|$withText|$withGraphics|$version\t$res\t" . $output . "\n"; + + error_log($log); + + return $res; + } + + public static function ping($echo = true, $force = false) + { + $cache = self::_pingCache(); + $servers = self::getServers(); + $pings = []; + if (file_exists($cache)) { + $cached = json_decode(file_get_contents($cache)); + if (is_countable($cached) && count($cached) === count($servers)) { + $pings = $cached; + } + } + + foreach ($servers as $id => $farmer) { + if ($echo) { + echo $farmer['name'] . ' (' . $id . ') || '; + } + if (isset($pings[$id]) && !$pings[$id]) { + // If ping failed recently, we wait a bit before trying again. + if (!$force && rand(0, 9) != 5) { + if ($echo) { + echo 'Skipped, will try again soon' . "\n"; + } + continue; + } + } + try { + $res = self::sendRequest($farmer, 'ping.php', [], 5); + $ok = $res == '1'; + } catch (\Exception $e) { + $res = $e->getMessage(); + $ok = false; + } + + if ($echo) { + echo ($ok ? 'OK' : 'KO') . ' : ' . trim($res) . "\n"; + } + + $pings[$id] = $ok; + } + file_put_contents($cache, json_encode($pings)); + file_put_contents(self::_serversCache(), json_encode($servers)); + } +} diff --git a/app/Fluidbook/Links.php b/app/Fluidbook/Links.php new file mode 100644 index 000000000..77d41167a --- /dev/null +++ b/app/Fluidbook/Links.php @@ -0,0 +1,476 @@ + __('Identifiant unique'), + 'page' => __('Page de la publication'), 'left' => __('x'), 'top' => __('y'), 'width' => __('Largeur'), 'height' => __('Hauteur'), 'rot' => __('Rotation'), + 'type' => __('Type'), 'to' => __('Destination'), 'target' => __('Cible'), + 'infobulle' => __('Infobulle'), 'numerotation' => __('Numérotation'), + 'display_area' => __('Activer la surbrillance'), + 'video_loop' => __('Video : boucle'), 'video_auto_start' => __('Video : démarrage automatique'), 'video_controls' => __('Vidéo : afficher les contrôles'), 'video_sound_on' => __('Vidéo : activer le son'), + 'inline' => __('Vidéo : afficher dans la page'), 'video_width' => __('Vidéo : Largeur du popup'), 'video_height' => __('Vidéo : Hauteur du popup'), + 'interactive' => __('Interactivité'), 'video_service' => __('Webvideo : service'), + 'extra' => __('Paramètre supplémentaire'), + 'alternative' => __('Alternative'), + 'read_mode' => __('Mode de lecture'), + 'image' => __('Image'), 'image_rollover' => __('Animation au survol'), + 'animation' => __('Animation'), + 'group' => __('Groupe'), + 'zindex' => __('Profondeur'), + ); + + $comments = array(); + + $xls = new Spreadsheet(); + $s = $xls->setActiveSheetIndex(0); + $s->setTitle('Links'); + + // Labels + $i = 1; + foreach ($cols as $id => $label) { + $s->setCellValueByColumnAndRow($i, 1, $id); + $s->getColumnDimensionByColumn($i)->setAutoSize(true); + $s->getStyleByColumnAndRow($i, 1)->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER); + $i++; + } + + // Links + self::_correctImageSpecialLinks($links); + $j = 2; + foreach ($links as $l) { + $i = 1; + foreach ($cols as $id => $label) { + if (($id == 'document_id' || $id == 'document_page')) { + if (!is_null($pages)) { + $infos = $pages[$l['page']]; + $value = $infos[$id]; + } else { + $value = ''; + } + } else { + + if (isset($l[$id])) { + if (is_bool($l[$id])) { + $l[$id] = $l[$id] ? '1' : '0'; + } + if ($id === 'numerotation') { + if ($l[$id] === 'false') { + $l[$id] = 'physical'; + } + } + if ($id === 'to') { + $s->getCellByColumnAndRow($i, $j)->setDataType(DataType::TYPE_STRING)->getStyle()->getNumberFormat()->setFormatCode(NumberFormat::FORMAT_TEXT); + } + $value = $l[$id]; + } else { + $value = ''; + } + } + + $s->setCellValueExplicitByColumnAndRow($i, $j, $value, DataType::TYPE_STRING); + $i++; + } + $j++; + } + // Rulers + $s = $xls->createSheet(); + $s->setTitle('Rulers'); + + $rcols = array('page', 'type', 'pos'); + $i = 1; + // Labels + foreach ($rcols as $id) { + $s->setCellValueByColumnAndRow($i, 1, $id); + $s->getColumnDimensionByColumn($i)->setAutoSize(true); + $i++; + } + + // Contents + $j = 2; + foreach ($rulers as $r) { + $i = 1; + foreach ($rcols as $id) { + if (!is_null($pages) && ($id == 'document_id' || $id == 'document_page')) { + $infos = $pages[$r['page']]; + $value = $infos[$id]; + } else { + $value = $r[$id]; + } + $s->setCellValueByColumnAndRow($i, $j, $value); + $s->getStyleByColumnAndRow($i, $j)->getAlignment()->setHorizontal(Alignment::HORIZONTAL_LEFT); + $i++; + } + $j++; + } + + $xls->setActiveSheetIndex(0); + return $xls; + } + + + public static function getLinksAndRulers($book_id, &$links, &$rulers, $time = 'latest') + { + if (null === $time) { + $time = 'latest'; + } + $dir = self::getLinksDir($book_id); + + $file = $dir . '/' . $time . '.links3.gz'; + if ($time === 'latest' && !file_exists($file)) { + $versions = self::getLinksVersions($book_id); + foreach ($versions as $version => $m) { + copy(Files::firstThatExists($dir . '/' . $version . '.links3.gz', $dir . '/' . $version . '.links.gz'), $dir . '/latest.links3.gz'); + copy(Files::firstThatExists($dir . '/' . $version . '.meta3.gz', $dir . '/' . $version . '.meta.gz'), $dir . '/latest.meta3.gz'); + break; + } + } + if (!file_exists($file)) { + $links = []; + $rulers = []; + return; + } + + $r = json_decode(gzdecode(file_get_contents($file)), true); + $links = self::_UID($r['links']); + $rulers = self::_UID($r['rulers']); + if (can('fluidbook-publication:links:edit-animations')) { + $links = Link::decryptLinks($links); + }else{ + $links = Link::encryptLinks($links); + } + + self::_correctImageSpecialLinks($links); + } + + protected static function _UID($items) + { + $res = []; + foreach ($items as $item) { + if (!isset($item['uid'])) { + $item['uid'] = self::uid(); + } + $res[$item['uid']] = $item; + } + return $res; + } + + protected static function uid() + { + return Str::lower(Str::random(12)); + } + + protected static function _correctImageSpecialLinks(&$links) + { + foreach ($links as $k => $link) { + if (preg_match('/^link_(.*)$/', $link['page'], $matches) && strlen($matches[1]) !== 32) { + $uid = $matches[1]; + foreach ($links as $l) { + if ($l['uid'] === $uid && $l['alternative']) { + $links[$k]['page'] = 'link_' . md5($l['alternative']); + break; + } + } + } else if (preg_match('/^([0-9a-f]{32})$/', $link['page'], $matches)) { + $links[$k]['page'] = 'link_' . $matches[1]; + } + } + } + + public static function getLinksFromExcel($xls, &$links, &$rulers) + { + $s = $xls->setActiveSheetIndexByName('Links'); + $i = 0; + $links = array(); + foreach ($s->getRowIterator() as $row) { + $cellIterator = $row->getCellIterator(); + $cellIterator->setIterateOnlyExistingCells(false); + if ($i == 0) { + $cols = array(); + foreach ($cellIterator as $cell) { + $cols[] = $cell->getValue(); + } + } else { + $link = array(); + $j = 0; + foreach ($cellIterator as $cell) { + $link[$cols[$j]] = $cell->getValue(); + $j++; + } + if ($link['display_area'] == '' || !$link['display_area']) { + $link['display_area'] = '0'; + } + if (trim($link['infobulle']) == '') { + $link['infobulle'] = ''; + } + $links[] = $link; + } + + $i++; + } + + $i = 0; + $rulers = array(); + $s = $xls->setActiveSheetIndexByName('Rulers'); + foreach ($s->getRowIterator() as $row) { + $cellIterator = $row->getCellIterator(); + $cellIterator->setIterateOnlyExistingCells(false); + if ($i == 0) { + $cols = array(); + foreach ($cellIterator as $cell) { + $cols[] = $cell->getValue(); + } + } else { + $link = array(); + $j = 0; + foreach ($cellIterator as $cell) { + $ruler[$cols[$j]] = $cell->getValue(); + $j++; + } + + $rulers[] = $ruler; + } + $i++; + } + + self::_correctImageSpecialLinks($links); + } + + public static function getLinksFromAutobookmarkText($txt, &$links, &$rulers) + { + $links = array(); + $rulers = array(); + + $lines = explode("\n", $txt); + + $protocols = array('mailto' => 3, 'custom' => 7, 'cart' => 12, 'pagelabel' => 26); + + foreach ($lines as $line) { + $line = trim($line); + if ($line == '') { + continue; + } + if (strpos('#', $line) === 0) { + continue; + } + $target = $numerotation = ''; + list($page, $left, $top, $width, $height, $type, $to) = explode(';', $line); + if ($type <= 2) { + $target = '_blank'; + } elseif ($type == 5) { + $numerotation = 'physical'; + } + + $links[] = array( + 'page' => $page, + 'left' => $left, 'top' => $top, 'width' => $width, 'height' => $height, 'rot' => '', + 'type' => $type, 'to' => $to, 'target' => $target, + 'infobulle' => '', 'numerotation' => $numerotation, 'display_area' => '1'); + } + + self::_correctImageSpecialLinks($links); + } + + public static function saveLinksInFile($book_id, $user_id, $comments, $links, $rulers = [], $specialLinks = [], $specialRulers = []) + { + $lr = self::mergeLinksAndRulers($links, $rulers, $specialLinks, $specialRulers); + $meta = ['links' => count($lr['links']), 'rulers' => count($lr['rulers']), 'comments' => $comments, 'user' => $user_id]; + $base = self::getLinksDir($book_id) . '/' . time(); + $latestLinks = self::getLinksDir($book_id) . '/latest.links3.gz'; + $latestMeta = self::getLinksDir($book_id) . '/latest.meta3.gz'; + file_put_contents($base . '.meta3.gz', gzencode(json_encode($meta))); + file_put_contents($base . '.links3.gz', gzencode(json_encode($lr))); + copy($base . '.links3.gz', $latestLinks); + copy($base . '.meta3.gz', $latestMeta); + } + + + public static function getLinksDir($book_id) + { + return Files::mkdir('/data/extranet/www/fluidbook/books/links/' . $book_id); + } + + public static function getLinksVersions($book_id) + { + $dir = self::getLinksDir($book_id); + $dr = opendir($dir); + $updates = []; + while ($f = readdir($dr)) { + if ($f === '.' || $f === '..') { + continue; + } + $e = explode('.', $f, 2); + if (($e[1] !== 'meta.gz' && $e[1] !== 'meta3.gz') || $e[0] === 'latest') { + continue; + } + + $updates[$e[0]] = self::getMeta($book_id, $e[0]); + } + krsort($updates); + + + $res = []; + foreach ($updates as $timestamp => $u) { + try { + $u['name'] = User::find($u['user'])->name; + } catch (\Exception $e) { + $u['name'] = '-'; + } + $u['date'] = date('Y-m-d H:i:s', $timestamp); + $u['timestamp'] = $timestamp; + $res[] = $u; + } + + return $res; + } + + public static function getMeta($book_id, $update = 'latest') + { + return json_decode(gzdecode(file_get_contents(Files::firstThatExists(self::getLinksDir($book_id) . '/' . $update . '.meta3.gz', self::getLinksDir($book_id) . '/' . $update . '.meta.gz'))), true); + } + + public static function mergeLinksAndRulers($links, $rulers, $specialLinks, $specialRulers) + { + $finalLinks = []; + $l = array_merge(self::_getAsArray($links), self::_getAsArray($specialLinks)); + + $k = 0; + foreach ($l as $item) { + $item['id'] = $k + 1; + if (!isset($item['to'])) { + $item['to'] = ''; + } + $finalLinks[] = $item; + $k++; + } + + self::_correctImageSpecialLinks($finalLinks); + + return ['links' => Link::encryptLinks($finalLinks), 'rulers' => array_merge(self::_getAsArray($rulers), self::_getAsArray($specialRulers))]; + } + + protected static function _getAsArray($v) + { + if (is_array($v)) { + return $v; + } + return json_decode($v, true); + } + + public static function addLinksFromPDF($book_id) + { + global $core; + + $daoBook = new wsDAOBook($core->con); + $pages = $daoBook->getPagesOfBook($book_id); + + $booleans = array('video_loop', 'video_auto_start', 'video_controls', 'video_sound_on'); + $numbers = ['left', 'top', 'width', 'height']; + + $links = []; + + foreach ($pages as $page => $info) { + $csv = wsDocument::getDir($info['document_id']) . '/p' . $info['document_page'] . '.csv'; + if (!file_exists($csv) && file_exists($csv . '.gz')) { + $csv = 'compress.zlib://' . $csv . '.gz'; + } elseif (!file_exists($csv)) { + continue; + } + + $newformat = (filemtime($csv) > 1363685416); + + $fp = fopen($csv, 'rb'); + + while (true) { + $line = fgetcsv($fp, 512, ';', '"'); + // End of file + if (!$line) { + break; + } + + // Commentaire || ligne vide + if (substr($line[0], 0, 1) == '#' || is_null($line[0])) { + continue; + } + + $link = []; + if ($newformat) { + $cols = array('page' => '', 'left' => '', 'top' => '', 'width' => '', 'height' => '', 'type' => '', 'to' => '', 'target' => '_blank', 'video_loop' => true, 'video_auto_start' => true, 'video_controls' => true, 'video_sound_on' => true, 'infobulle' => '', 'numerotation' => 'physical', "inline" => true); + } else { + $cols = array('page' => '', 'type' => '', 'to' => '', 'left' => '', 'top' => '', 'width' => '', 'height' => '', 'target' => '_blank', 'video_loop' => true, 'video_auto_start' => true, 'video_controls' => true, 'video_sound_on' => true, 'infobulle' => '', 'numerotation' => 'physical'); + } + + + $k = 0; + foreach ($cols as $col => $default) { + if (isset($line[$k])) { + if (in_array($k, $numbers)) { + $link[$col] = (float)str_replace(',', '.', $line[$k]); + } else if (in_array($k, $booleans)) { + $link[$col] = ($line[$k] == '1'); + } else { + $link[$col] = utf8_encode($line[$k]); + } + } else { + $link[$col] = $default; + } + $k++; + } + + if ($link['type'] == 18) { + $link['infobulle'] = $link['to']; + $link['to'] = ''; + } + + $link['display_area'] = '1'; + $link['page'] = $page; + $links[] = $link; + } + + } + + self::saveLinksInFile($book_id, $core->user->utilisateur_id, 'Links imported from PDF', $links, []); + } + + public static function getLinksAndRulersFromExcelFile($path, &$links, &$rulers) + { + $reader = new \PhpOffice\PhpSpreadsheet\Reader\Xlsx(); + $xls = $reader->load($path); + Links::getLinksFromExcel($xls, $links, $rulers); + } + + public static function generateUID() + { + $characters = '0123456789abcdefghijklmnopqrstuvwxyz'; + $randstring = ''; + for ($i = 0; $i < 12; $i++) { + $randstring = $characters[rand(0, 35)]; + } + return $randstring; + } +} diff --git a/app/Fluidbook/Packager/Base.php b/app/Fluidbook/Packager/Base.php new file mode 100644 index 000000000..7cb739764 --- /dev/null +++ b/app/Fluidbook/Packager/Base.php @@ -0,0 +1,295 @@ +cleanOnDestruct = $packager->cleanOnDestruct && $cleanOnDestruct; + + return $packager->makePackage($zip); + } + + public function __construct($book_id, $vdir = null, $whole = true, $options = []) + { + + $this->_clean = (null === $vdir); + + $this->book_id = $book_id; + + $this->vdir = $vdir; + $this->dir = ROOT . '/fluidbook/packager/' . $book_id . '/'; + $this->whole = $whole; + + if (!file_exists($this->dir)) { + mkdir($this->dir, 0777, true); + } + + $this->daoBook = new wsDAOBook($core->con); + $this->book = $this->daoBook->selectById($book_id); + $forceCompile = false; + if (count($options)) { + $options['forceCompileOnDownload'] = true; + } + foreach ($options as $k => $v) { + $this->book->parametres->$k = $v; + } + + $this->pages = $this->daoBook->getPagesOfBook($book_id, false); + + $daoTheme = new wsDAOTheme($core->con); + $this->theme = $daoTheme->getThemeOfBook($book_id, true); + $this->themeRoot = WS_THEMES . '/' . $this->theme->theme_id . '/'; + + $this->workingDir = $this->book->getAssetDir(); + + if ($this->_compileOnConstruct) { + $this->compile($forceCompile); + } + } + + + protected function compile($forceCompile = false) + { + $this->daoBook->compile($this->book_id, '2', false, $this->book->parametres->forceCompileOnDownload || $forceCompile, false, $this->book); + } + + protected function preparePackage() + { + $this->initTempDir(); + } + + public function makePackage($zip) + { + $this->preparePackage(); + } + + protected function replaceContents($str, $toReplace) + { + $res = $str; + foreach ($toReplace as $k => $v) { + if (is_null($v)) { + return; + } + $res = str_replace('$' . $k, $v, $res); + } + return $res; + } + + protected function copyFluidbookFiles() + { + // Copie du FB vers un répertoire temporaire + $cp = new CommandLine('cp'); + $cp->setArg('R'); + $cp->setArg('p'); + $cp->setArg(null, WS_BOOKS . '/final/' . $this->book->book_id . '/*'); + $cp->setArg(null, $this->vdir); + $cp->execute(); + } + + protected function copyOtherFiles($files) + { + foreach ($files as $source => $dest) { + if (is_int($source)) { + $source = $dest; + } + + $s = WS_COMPILE_ASSETS . '/' . $source; + if (is_file($s) && !file_exists($this->vdir . $dest)) { + $this->copy($s, $this->vdir . $dest); + } else if (is_dir($s)) { + $cp = new CommandLine('cp'); + $cp->setArg('R'); + $cp->setArg('p'); + $cp->setArg(null, $s); + $cp->setArg(null, $this->vdir); + $cp->execute(); + + $mv = new CommandLine('mv'); + $mv->setArg($this->vdir . '/' . $source); + $mv->setArg($this->vdir . '/' . $dest); + $mv->execute(); + } + } + } + + protected function getBaseFile() + { + return $this->version . '-' . date('Ymdhis') . '-' . $this->escapeTitle(); + } + + protected function escapeTitle() + { + $res = cubeText::str2URL($this->book->parametres->title); + if ($res == '') { + $res = 'fluidbook'; + } + return $res; + } + + protected function getRelativeBase() + { + return '/packager/download/' . $this->getBaseFile(); + } + + protected function getURLBase($ext = '') + { + $res = '/fluidbook' . $this->getRelativeBase(); + if ($ext != '') { + $res .= '.' . $ext; + } + return $res; + } + + protected function getPathBase($ext = '') + { + $res = WS_FILES . $this->getRelativeBase(); + if ($ext != '') { + $res .= '.' . $ext; + } + + return $res; + } + + protected function zip($zipfile = null) + { + if (!$this->whole) { + return; + } + $url = $this->getURLBase('zip'); + $final = $this->getPathBase('zip'); + $rename = false; + if (is_null($zipfile)) { + $zipfile = $final; + } else { + $rename = true; + } + + $dir = $this->getFinalPackageDir(); + if (file_exists($dir)) { + $zip = new CommandLine('zip'); + $zip->cd($dir); + $zip->setArg(null, $zipfile); + $zip->setArg('symlinks'); + $zip->setArg('0'); + $zip->setArg('r'); + $zip->setArg('u'); + $zip->setArg(null, '.'); + $zip->setManualArg('-x "*/\.*"'); + $zip->execute(); + $zip->debug(); + } + + if (!file_exists(WS_FILES . '/packager/download')) { + mkdir(WS_FILES . '/packager/download', 0777, true); + } + + if ($rename) { + rename($zipfile, $final); + } + return $url; + } + + public function getFinalPackageDir() + { + $dir = $this->vdir; + $dir .= '/m/'; + return $dir; + } + + protected function initTempDir() + { + if (is_null($this->vdir)) { + $this->vdir = $this->dir . $this->version . '/'; + } + $this->cleanVdir(); + if (!file_exists($this->vdir . '/data')) { + mkdir($this->vdir . '/data', 0777, true); + } + } + + protected function cleanVdir() + { + if (!$this->_clean) { + return; + } + if (file_exists($this->vdir)) { + // Suppression du répertoire si il existe + $rm = new CommandLine('rm'); + $rm->setArg('r'); + $rm->setArg('f'); + $rm->setArg(null, $this->vdir); + $rm->execute(); + } + } + + protected function postPackage() + { + + } + + public function __destruct() + { + if ($this->whole && $this->cleanOnDestruct) { + $this->cleanVdir(); + } + } + + public function copy($source, $dest) + { + if (!file_exists($source)) { + return; + } + copy($source, $dest); + touch($dest, filemtime($source)); + } +} diff --git a/app/Fluidbook/Packager/ChromeOS.php b/app/Fluidbook/Packager/ChromeOS.php new file mode 100644 index 000000000..2f7068cf7 --- /dev/null +++ b/app/Fluidbook/Packager/ChromeOS.php @@ -0,0 +1,50 @@ +version = 'chromeos'; + $this->cleanOnDestruct = true; + } + + protected function preparePackage() + { + $res = parent::preparePackage(); + $manifest = ['name' => $this->book->parametres->offlineTitle == '' ? $this->book->parametres->title : $this->book->parametres->offlineTitle, + 'version' => '1.0.' . time(), + 'manifest_version' => 3, + //'default_locale' => $this->book->lang, + //'icons' => [], + 'permissions' => [ + 'webview', + ] + ]; +// $sizes=[128,64,32,16]; +// $pngFile = WS_THEMES . '/' . $this->theme->theme_id . '/' . $this->theme->parametres->favicon; +// foreach ($sizes as $size) { +// +// } + + file_put_contents($this->vdir . '/m/manifest.json', json_encode($manifest)); + return $res; + } + + public function makePackage($zip) + { + $this->preparePackage(); + + $chrome = new CommandLine('crx3'); + $chrome->setArg('o', WS_PACKAGER . '/download/' . $this->getBaseFile() . '.crx'); + $chrome->setArg(null, $this->vdir . '/m/'); + $chrome->execute(); + $chrome->debug(); + + return $this->getURLBase('crx'); + } +} diff --git a/app/Fluidbook/Packager/MacOS.php b/app/Fluidbook/Packager/MacOS.php new file mode 100644 index 000000000..42fb150d0 --- /dev/null +++ b/app/Fluidbook/Packager/MacOS.php @@ -0,0 +1,128 @@ +version = 'mac_exe_html'; + } + + public function makePackage($zip) + { + $this->preparePackage(); + $toDelete = ['chromedriver', 'credits.html', 'minidump_stackwalk', 'nwjc', 'payload', 'v8_context_snapshot.bin', 'natives_blob.bin', 'libffmpeg.dylib']; + foreach ($toDelete as $item) { + $p = $this->getFinalPackageDir() . '/' . $item; + `rm -rf "$p"`; + } + if ($zip) { + $res = $this->zip(null); + } else { + $res = $this->getFinalPackageDir(); + } + $this->postPackage(); + return $res; + } + + public function getAppPath() + { + return $this->getFinalPackageDir() . '/' . $this->exeName . '.app'; + } + + public function getFinalPackageDir() + { + $res = parent::getFinalPackageDir(); + return $res; + } + + protected function preparePackage() + { + $this->initTempDir(); + $this->copyFluidbookFiles(); + $this->makeJSON(); + + $this->buildPath = WS_PACKAGER . '/nwbuild/' . $this->version . '/' . $this->book_id; + if (!file_exists($this->buildPath)) { + mkdir($this->buildPath, 0777, true); + } + + $cl = new CommandLine('nwbuild'); + $cl->setPath(CONVERTER_PATH); + $cl->setArg('p', $this->nwplatform); + $cl->setArg('o', $this->buildPath); + $cl->setArg('v', $this->nwversion); + $cl->setArg('winIco', $this->vdir . '/icon.ico'); + $cl->setArg('macIcns', $this->vdir . '/icon.icns'); + $cl->setArg(null, $this->vdir); + $cl->execute(); + $cl->debug(); + + $this->replaceFFMpeg(); + if(!file_exists($this->getAppPath())){ + die('Error while building mac app : '.$cl->commande.' // '.$cl->output); + } + + $this->signApp(); + } + + function replaceFFMpeg() + { + copy(WS_COMPILE_ASSETS . '/_exehtml/_ffmpeg/libffmpeg.dylib', $this->getAppPath() . '/Contents/Frameworks/nwjs Framework.framework/Versions/Current/libffmpeg.dylib'); + } + + protected function signApp() + { + self::_signApp($this->getAppPath()); + } + + public static function _signApp($appPath, $back = true) + { + $local_root = '/Users/vincent/Sign/'; + $dist_root = '/mnt/sshfs/macparis' . $local_root; + + $f = 'tmp_' . md5(rand(0, 1000000)) . ".app"; + $path = $dist_root . $f; + + // Copy app to mac + $cp = new CommandLine('cp'); + $cp->setArg('r'); + $cp->setArg(null, $appPath); + $cp->setArg(null, $path); + $cp->execute(); + $cp->debug(); + + // Sign app + $cl = new CommandLine($local_root . 'sign'); + $cl->setSSH(wsExporter::VINCENT, 'vincent', 'atacama', 22022); + $cl->setArg(null, $local_root . $f); + $cl->execute(); + $cl->debug(); + $res = $cl->output; + + if ($back) { + // Copy back signed + $cp = new CommandLine('rsync'); + $cp->setArg('r'); + $cp->setArg('l'); + $cp->setArg('p'); + $cp->setArg('D'); + $cp->setArg('v'); + $cp->setArg(null, $path . '/'); + $cp->setArg(null, $appPath . '/'); + $cp->setArg('delete'); + $cp->execute(); + $cp->debug(); + } + + // `rm -rf $path`; + return $res; + } +} diff --git a/app/Fluidbook/Packager/OfflineHTML.php b/app/Fluidbook/Packager/OfflineHTML.php new file mode 100644 index 000000000..22ff130cf --- /dev/null +++ b/app/Fluidbook/Packager/OfflineHTML.php @@ -0,0 +1,14 @@ +vdir . '/index.swf'); + unlink($this->vdir . '/player.swf'); + copy(WS_COMPILE_ASSETS . '/offline.swf', $this->vdir . '/index.swf'); + } +} diff --git a/app/Fluidbook/Packager/Online.php b/app/Fluidbook/Packager/Online.php new file mode 100644 index 000000000..b81c37439 --- /dev/null +++ b/app/Fluidbook/Packager/Online.php @@ -0,0 +1,441 @@ +version = 'html'; + } + + protected function preparePackage() + { + parent::preparePackage(); + + if ($this->_disableScorm) { + $this->book->parametres->scorm_enable = false; + } + + $this->_ext = $this->book->parametres->htmlExtension; + $this->book->parametres->actualHtmlExtension = $this->_ext; + + $this->copyFluidbookFiles(); + $this->mergeJavascript(); + +// $others = array('fluidbook.js', 'getflash.gif', 'index.html', 'style.css'); +// $others = array_merge($others, $this->getSWFFiles()); +// +// $this->copyOtherFiles($others); + + if (!file_exists($this->vdir . '/pages')) { + mkdir($this->vdir . '/pages/', 0777, true); + } + + $ga = ''; + if ($this->book->parametres->googleAnalyticsCustom) { + $ga = $this->book->parametres->googleAnalyticsCustom; + } elseif ($this->book->parametres->googleAnalytics != '') { + $variables = array('Language' => array('value' => 'getLang()', 'valueAsJS' => true, 'scope' => 2)); + $ga = cubePage::googleAnalytics(explode(',', $this->book->parametres->googleAnalytics), true, $variables); + } + + $statsfooter = ''; + if ($this->book->parametres->statsCustom != '') { + $statsfooter = $this->book->parametres->statsCustom; + } + + $facebook = ''; + if ($this->book->parametres->facebook) { + if ($this->book->parametres->facebook_title != '') { + $facebook .= ''; + } else { + $facebook .= ''; + } + if ($this->book->parametres->facebook_description != '') { + $facebook .= ''; + } + $t = 'https://workshop.fluidbook.com/services/facebook_thumbnail?cid=' . $this->book->cid . '&j=' . time(); + $dim = getimagesize($t); + $facebook .= ''; + $facebook .= ''; + $facebook .= ''; + } + + $favicon = ''; + + if ($this->theme->parametres->favicon != '') { + $favicon = ''; + } + + $redirectPDF = 'redirectPDF();'; + if ($this->book->parametres->mobileVersion == 'pdf') { + $redirectMobile = $redirectPDF; + } else { + $redirectMobile = 'redirectMobile();'; + $this->prepareHTML5(); + } + + $seoVersion = true; + if (isset($this->book->parametres->seoVersion)) { + $seoVersion = $this->book->parametres->seoVersion; + } + + $seoRobot = true; + if (isset($this->book->parametres->seoRobots)) { + $seoRobot = $this->book->parametres->seoRobots; + } + + $robots = ''; + if (!$seoRobot) { + $robots = ''; + } + + $keywords = ''; + if ($this->book->parametres->seoKeywords) { + $keywords = ''; + } + + $alwaysHTML5 = true; + $html5priority = true; + + + // Stuffs to replace in html + $toReplace = array('lang' => strtolower($this->book->lang), + 'ga' => $ga, + 'statsfooter' => $statsfooter, + 'facebook' => $facebook, + 'bgcolor' => $this->theme->parametres->loadingBackColor, + 'junk' => TIME, + 'robots' => $robots, + 'favicon' => $favicon, + 'alwaysHTML5' => $alwaysHTML5, + 'keywords' => $keywords, + ); + + $this->origHTML = $this->book->parametres->htmlPrepend; + $h = $this->vdir . '/index.html'; + if (file_exists($h)) { + $this->origHTML .= file_get_contents($h); + unlink($h); + } + $this->origHTML = $this->replaceHTML($toReplace); + + $nav1 = $this->makeHTMLNav(true); + $nav = $this->makeHTMLNav(false); + $footer = $this->makeHTMLFooter(); + + + foreach ($this->pages as $page => $infos) { + $pathToIndex = 'index.swf'; + $pathToGetflash = 'getflash.gif'; + $redirectScript = ''; + if ($page == 1) { + $dest = 'index.' . $this->_ext; + $title = $this->book->parametres->title; + $sp = ''; + } else { + $label = $this->_getLabelOfPage($page); + $title = $label . ' - ' . $this->book->parametres->title; + $dest = 'pages/' . $page . '-' . mb_strtolower(cubeText::str2URL($label)) . '.' . $this->_ext; + $pathToIndex = '../index.swf'; + $pathToGetflash = '../getflash.gif'; + $sp = '../'; + $redirectScript = ''; + } + $alt = ''; + +// if ($seoVersion && CubeIT_Util_Gzip::file_exists($htmlfile)) { +// $html = CubeIT_Util_Gzip::file_get_contents($htmlfile); +// $alt .= "\n" . $html . "\n"; +// +// if ($page == 1) { +// $alt .= $nav1; +// } else { +// $alt .= $nav; +// } +// } + + $alt .= $footer; + + $base = ''; + if ($this->book->parametres->baseUrl) { + $base = ''; + } + + if ($page == 1 && $this->book->parametres->seoDescription) { + $description = $this->book->parametres->seoDescription; + } else { + $textfile = wsDocument::getDir($infos['document_id']) . 'ph' . $infos['document_page'] . '.txt'; + if (file_exists($textfile)) { + $description = mb_substr(file_get_contents($textfile), 0, 150); + } else { + $description = ''; + } + } + + $data = str_replace('$alt', $alt, $this->origHTML); + $data = str_replace('$base', $base, $data); + $data = str_replace('$pathToIndex', $pathToIndex, $data); + $data = str_replace('$title', $this->escape($title), $data); + $data = str_replace('$pathToGetflash', $pathToGetflash, $data); + $data = str_replace('$redirectScript', $redirectScript, $data); + $data = str_replace('$sp', $sp, $data); + $data = str_replace('$index_ext', $this->_ext, $data); + $data = str_replace('$description', '', $data); + + file_put_contents($this->vdir . $dest, $data); + + if (!$seoVersion) { + break; + } + } + } + + protected function getFlashvars() + { + return array(); + } + + public function prepareHTML5() + { + if (!$this->whole) { + return; + } + $dest = $this->vdir . 'm'; + + $mfid = $this->book->parametres->mobilefirstFluidbookId; + if ($mfid != '' && (int)$mfid > 0) { + $mfbook = $this->daoBook->selectById($mfid); + $this->_compileHTML5($this->book_id, $this->book, $dest . '/d', true); + $vars = wsDAOBook::$lastHTML5Compiler->getIndexVars(); + $this->_compileHTML5($mfid, $mfbook, $dest . '/mf', true); + + $hybrid = file_get_contents(WS_COMPILE_ASSETS . '/hybrid/index.html'); + $replace = [ + 'titre' => $vars[''], + 'breakpoint' => $this->book->parametres->mobilefirstBreakpoint . 'px', + 'bgcolor' => $this->theme->parametres->loadingBackColor, + 'description' => $vars[''], + 'twittercard' => $vars[''], + 'opengraph' => $vars[''], + 'credits' => $vars[''], + ]; + foreach ($replace as $var => $value) { + $hybrid = str_replace('$' . $var, $value, $hybrid); + } + file_put_contents($dest . '/index.html', $hybrid); + } else { + $this->_compileHTML5($this->book_id, $this->book, $dest); + } + + } + + protected function _compileHTML5($bookId, $book, $dest, $hybrid = false) + { + + $this->daoBook->compile($bookId, 'html5', false, $book->parametres->forceCompileOnDownload, false, $book, true, false, $hybrid); + + if (!file_exists($dest)) { + mkdir($dest, 0777, true); + } + + $cp = new CommandLine('cp'); + $cp->setPath(CONVERTER_PATH); + $cp->setArg('r'); + $cp->setArg('p'); + $cp->setArg(null, WS_BOOKS . '/html5/' . $bookId . '/*'); + $cp->setArg(null, $dest); + $cp->execute(); + + $filesToAdd = array(); + foreach ($filesToAdd as $f) { + $this->copy(WS_COMPILE_ASSETS . '/_html5/' . $f, $dest . '/' . $f); + } + + $filesToDelete = array('indext.html', 'indexu.html'); + + $htmlFiles = array('index'); + + foreach ($htmlFiles as $name) { + $html = $book->parametres->htmlPrepend . file_get_contents($dest . '/' . $name . '.html'); + file_put_contents($dest . '/' . $name . '.' . $this->_ext, $html); + if ($this->_ext != 'html') { + $filesToDelete[] = $name . '.html'; + } + } + + $rm = new CommandLine('rm'); + $rm->setPath(CONVERTER_PATH); + foreach ($filesToDelete as $f) { + $rm->setArg(null, $dest . '/' . $f); + } + $rm->execute(); + + if ($this->_ext !== 'html') { + $e = $this->_ext; + `find $dest -type f -name "*.html" -exec rename 's/\.html$/.$e/' '{}' \;`; + + } + } + + public function makePackage($zip) + { + parent::makePackage($zip); + if ($zip) { + return $this->zip(); + } + return $this->getFinalPackageDir(); + } + + protected function makeHTMLNav($root) + { + $res = ''; + return $res; + } + + protected function _getLabelOfPage($page) + { + if (!isset($this->_chapters)) { + $this->_chapters = $this->book->chapters; + } + + + if (isset($this->_labels[$page])) { + return $this->_labels[$page]; + } + + + if ($page == 1) { + $this->_labels[1] = $this->book->parametres->title; + + return $this->_labels[1]; + } + + + $virtual = $this->_getVirtualPage($page); + + $candidates = array(); + foreach ($this->_chapters as $c) { + if ($c->page == $virtual) { + $candidates[] = $c; + } + } + if (!count($candidates)) { + $this->_labels[$page] = $this->_getLabelOfPage($page - 1); + return $this->_labels[$page]; + } + + usort($candidates, array($this, '_sortCandidates')); + $c = array_shift($candidates); + + $this->_labels[$page] = $c->label; + return $this->_labels[$page]; + } + + protected function _sortCandidates($a, $b) + { + if ($a->level > $b->level) { + return 1; + } else if ($a->level < $b->level) { + return -1; + } else { + return 0; + } + } + + protected function _getVirtualPage($page) + { + $num = explode(',', $this->book->numerotation); + if (isset($num[$page - 1])) { + return $num[$page - 1]; + } + return 1; + } + + protected function escape($txt, $replaceNewLines = false) + { + $res = htmlentities($txt, ENT_COMPAT, 'UTF-8'); + if ($replaceNewLines) { + $res = str_replace("\n", ' ', $res); + $res = str_replace("\r", '', $res); + } + return $res; + } + + protected function makeHTMLFooter() + { + $res = ''; + return $res; + } + + protected function replaceHTML($toReplace) + { + return $this->replaceContents($this->origHTML, $toReplace); + } + + protected function mergeJavascript() + { + $dest = WS_COMPILE_ASSETS . '/fluidbook.js'; + $orig = WS_COMPILE_ASSETS . '/_js/'; + $files = array('log4js.js' => false, 'esapi.js' => false, 'resources/i18n/ESAPI_Standard_en_US.properties.js' => false, 'resources/Base.esapi.properties.js' => false, 'swfobject.js' => false, 'swfaddress.js' => true, 'fluidbook.js' => true); + + $refresh = false; + if (file_exists($dest)) { + $mtime = filemtime($dest); + foreach ($files as $file => $min) { + if (filemtime($orig . $file) > $mtime) { + $refresh = true; + break; + } + } + } else { + $refresh = true; + } + if (!$refresh) { + return; + } + + $minjs = "\n\n"; + foreach ($files as $file => $min) { + $c = file_get_contents($orig . $file); + if ($min) { + $c = JSMin::minify($c); + } + + $minjs .= $c . "\n\n"; + } + file_put_contents($dest, $minjs); + } + + +} diff --git a/app/Fluidbook/Packager/Precompiled.php b/app/Fluidbook/Packager/Precompiled.php new file mode 100644 index 000000000..45b87e926 --- /dev/null +++ b/app/Fluidbook/Packager/Precompiled.php @@ -0,0 +1,48 @@ +version = 'precompiled'; + $this->book->parametres->embedAllLibraries = true; + } + + + public function prepareHTML5() + { + $res = parent::prepareHTML5(); + $dest = $this->vdir . 'm'; + + // Copy styles + $source = wsHTML5::getSourcesPath($this->book->parametres->mobileLVersion); + $styles = $source . '/style'; + $destLess = $dest . '/_less'; + $cmd = "cp -R $styles $destLess"; + `$cmd`; + + // Copy theme assets + $theme = WS_THEMES . '/' . $this->book->theme . '/'; + $destTheme = $dest . '/_theme'; + $cmd = "cp -R -L $theme $destTheme"; + `$cmd`; + + // Cleanup + $clean = ['data/thumbnails', 'data/background', 'data/contents', 'data/*.pdf', 'data/style', 'style']; + foreach ($clean as $item) { + $path = $dest . '/' . $item; + if (strstr($item, '.')) { + `rm -f $path`; + } else { + `rm -rf $path`; + } + } + + return $res; + } + + +} diff --git a/app/Fluidbook/Packager/Scorm.php b/app/Fluidbook/Packager/Scorm.php new file mode 100644 index 000000000..dcba067fb --- /dev/null +++ b/app/Fluidbook/Packager/Scorm.php @@ -0,0 +1,20 @@ +version = 'scorm'; + $this->_disableScorm = false; + } + + protected function preparePackage() + { + $res = parent::preparePackage(); + $manifests = $this->vdir . '/m/data/links/*/imsmanifest.xml'; + $cmd = "rm $manifests"; + `$cmd`; + return $res; + } +} diff --git a/app/Fluidbook/Packager/Sharepoint.php b/app/Fluidbook/Packager/Sharepoint.php new file mode 100644 index 000000000..341849ac0 --- /dev/null +++ b/app/Fluidbook/Packager/Sharepoint.php @@ -0,0 +1,15 @@ +version = 'sharepoint'; + $this->book->parametres->seoVersion = false; + $this->book->parametres->maxResolution = 150; + $this->book->parametres->htmlExtension = 'aspx'; + } +} diff --git a/app/Fluidbook/Packager/USBKey.php b/app/Fluidbook/Packager/USBKey.php new file mode 100644 index 000000000..e3a65c38b --- /dev/null +++ b/app/Fluidbook/Packager/USBKey.php @@ -0,0 +1,39 @@ +version = 'win_cd_html'; + } + + protected function preparePackage() + { + parent::preparePackage(); + $this->replaceAutorun(); + + // Package mac app + $win = ROOT . Base::package($this->book_id, 'win_inss_html', false, false); + + $dest = $this->getFinalPackageDir() . "/" . $this->exeName . '.exe'; + $cp = "cp $win $dest"; + `$cp`; + } + + public function replaceAutorun() + { + $inf = file_get_contents(WS_COMPILE_ASSETS . '/autorun-html.inf'); + $toReplace = array('title' => $this->book->parametres->title, 'exe' => $this->exeName . '.exe', 'nwplatform' => $this->nwplatform); + $inf = $this->replaceContents($inf, $toReplace); + file_put_contents($this->getFinalPackageDir() . '/autorun.inf', utf8_decode($inf)); + } + + + protected function postPackage() + { + + } + +} diff --git a/app/Fluidbook/Packager/WindowsEXE.php b/app/Fluidbook/Packager/WindowsEXE.php new file mode 100644 index 000000000..937497dd0 --- /dev/null +++ b/app/Fluidbook/Packager/WindowsEXE.php @@ -0,0 +1,13 @@ +version = 'win_inss_html'; + } +} diff --git a/app/Fluidbook/Packager/WindowsInstaller.php b/app/Fluidbook/Packager/WindowsInstaller.php new file mode 100644 index 000000000..e3c61f3e4 --- /dev/null +++ b/app/Fluidbook/Packager/WindowsInstaller.php @@ -0,0 +1,115 @@ +version = 'win_ins_html'; + } + + protected function preparePackage() + { + parent::preparePackage(); + + $this->makeNSI(); + } + + protected function makeNSI() + { + global $core; + + $winvdir = $this->getFinalPackageDir(); + + $daoLang = new wsDAOLang($core->con); + $lang = $daoLang->selectById($this->book->lang); + + if ($lang->nsis == 'Arabic') { + $lang->nsis = 'English'; + } + + if (!file_exists(WS_FILES . '/packager/download')) { + mkdir(WS_FILES . '/packager/download', 0777, true); + } + + $fname = $this->exeName; + $title = $this->appName; + + $nsi = file_get_contents(WS_COMPILE_ASSETS . '/' . $this->nsifile . '.nsi'); + $nsi = str_replace('$name', $title, $nsi); + $nsi = str_replace('$htmldir', WS_COMPILE_ASSETS, $nsi); + $nsi = str_replace('$fname', $fname, $nsi); + $nsi = str_replace('$fdir', $winvdir, $nsi); + $nsi = str_replace('$titre', $title, $nsi); + $nsi = str_replace('$lang', $lang->nsis, $nsi); + $nsi = str_replace('$nwplatform', $this->nwplatform, $nsi); + $nsi = str_replace('$nsisdir', '/usr/local/share/nsis', $nsi); + $nsi = str_replace('$output', $this->getPathBase('exe'), $nsi); + $favicon = $this->vdir . 'data/favicon.ico'; + if ($this->theme->parametres->favicon == '') { + $this->copy(WS_COMPILE_ASSETS . '/fluidbook.ico', $favicon); + } else if (!file_exists($favicon)) { + $pngFile = WS_THEMES . '/' . $this->theme->theme_id . '/' . $this->theme->parametres->favicon; + $icoFile = WS_THEMES . '/' . $this->theme->theme_id . '/favicon.ico'; + if (!file_exists($icoFile) || filemtime($icoFile) < filemtime($pngFile) || filemtime(__FILE__) > filemtime($icoFile)) { + $tmp = CubeIT_Files::tempnam() . '.png'; + $convert = "convert $pngFile -resize 64x64^ -gravity center $tmp"; + `$convert`; + + $icotool = new CommandLine('icotool'); + $icotool->setArg('c'); + $icotool->setArg('o', $icoFile); + $icotool->setArg(null, $tmp); + $icotool->execute(); + + unlink($tmp); + } + $this->copy($icoFile, $favicon); + if (!file_exists($favicon)) { + $this->copy(WS_COMPILE_ASSETS . '/fluidbook.ico', $favicon); + } + } + $nsi = str_replace('$favicon', $favicon, $nsi); + + $this->nsi = $nsi; + } + + public function makePackage($zip) + { + $this->preparePackage(); + + $tmp = cubeFiles::tempnam() . '.nsi'; + file_put_contents($tmp, $this->nsi); + $makensis = new CommandLine('makensis'); + $makensis->setArg(null, '-V4'); + $makensis->setArg(null, $tmp); + $makensis->execute(); + $makensis->debug(); + if (!file_exists($this->getPathBase('exe'))) { + die('Error while building the installer : ' . $this->getPathBase('exe') . ' // ' . $makensis->commande . ' // ' . $makensis->output); + } + + $this->signInstaller(); + + if (!file_exists($this->getPathBase('exe'))) { + die('Error during the signing process : ' . $this->getPathBase('exe')); + } + + return $this->getURLBase('exe'); + } + + public function signInstaller() + { + $this->_sign($this->getPathBase('exe')); + } + + public function __destruct() + { + + } + +} diff --git a/app/Fluidbook/Packager/WindowsZIP.php b/app/Fluidbook/Packager/WindowsZIP.php new file mode 100644 index 000000000..7526a8ca9 --- /dev/null +++ b/app/Fluidbook/Packager/WindowsZIP.php @@ -0,0 +1,218 @@ +version = 'win_exe_html'; + $this->appName = ''; + $this->appversion = '1.0.' . time(); + $this->_clean = false; + + if ($this->book->parametres->offlineTitle == "") { + $this->exeName = $this->book->book_id . '-' . trim(cubeText::str2URL(mb_substr($this->book->parametres->title, 0, $this->exenameMaxlength - 6)), '-'); + $this->appName = $this->book->parametres->title; + } else { + $this->exeName = trim(cubeText::str2URL(mb_substr($this->book->parametres->offlineTitle, 0, $this->exenameMaxlength)), '-'); + $this->appName = $this->book->parametres->offlineTitle; + } + + if ($this->exeName === '') { + $this->exeName = $book_id . '-fluidbook'; + } + } + + protected function preparePackage() + { + + parent::preparePackage(); + + $this->copyFluidbookFiles(); + $this->makeJSON(); + + $this->buildPath = WS_PACKAGER . '/nwbuild/' . $this->version . '/' . $this->book_id; + + `umask 0000;rm -rf $this->buildPath;mkdir -p 0777 $this->buildPath;chmod -R 777 $this->vdir;mkdir -p 0777 /application/tmp;chmod -R 777 /application/tmp`; + + $cl = new CommandLine('/usr/local/web2exe/web2exe-linux'); + $cl->setSudo(true); + $cl->setEnv('TMPDIR', '/application/tmp'); + $cl->setLongArgumentSeparator(' '); + $cl->setArg('export-to', $this->nwplatform); + $cl->setArg('uncompressed-folder'); + $cl->setArg('title', $this->appName); + $cl->setArg('output-dir', $this->buildPath); + $cl->setArg('nw-version', $this->nwversion); + //$cl->setArg('sdk-build'); + $cl->setArg('main', 'index.html'); + $cl->setArg('name', $this->exeName); + $cl->setArg('mac-icon', $this->vdir . 'icon.icns'); + $cl->setArg('exe-icon', $this->vdir . 'icon.ico'); + $cl->setArg('icon', $this->vdir . 'icon.png'); + $cl->setArg('width', 1024); + $cl->setArg('height', 768); + $cl->setArg('app-name', $this->exeName); + $cl->setArg('version', $this->appversion); + $cl->setArg('id', 'com.fluidbook.' . $this->book_id); + $cl->setArg('verbose'); + $cl->setArg(null, $this->vdir); + $cl->execute(); + $cl->debug(); + + `sudo chown -R extranet:www-data $this->buildPath`; + + if (!file_exists($this->buildPath)) { + die('Error while making exe : ' . $cl->commande . ' // ' . $cl->output); + } + + $this->replaceFFMpeg(); + + $this->signExe(); + } + + function signExe() + { + $exe = $this->buildPath . '/' . $this->exeName . '/' . $this->nwplatform . '/' . $this->exeName . '.exe'; + $this->_sign($exe); + } + + function _sign($exe) + { + $rand = 'sign-' . rand(1000000, 9999999) . '.exe'; + copy($exe, '/mnt/sshfs/codesign/' . $rand); + $cli = new CommandLine('C:/Program Files (x86)/Windows Kits/10/bin/10.0.18362.0/x64/signtool.exe'); + $cli->setManualArg("sign /f C:/Users/vince/Documents/Cubedesigners.cer /csp \"eToken Base Cryptographic Provider\" /k \"[SafeNet Token JC 0{{TYWjZacq%hAH98}}]=54C3F1B91759268A\" /tr http://timestamp.sectigo.com /td sha256 /fd sha256 /a C:/Sign/$rand"); + $cli->execute(new SSH2('paris.cubedesigners.com', 'vince', 'Y@mUC9mY2DOYWXkN', '22422')); + $cli->debug(); + sleep(2); + copy('/mnt/sshfs/codesign/' . $rand, $exe); + unlink('/mnt/sshfs/codesign/' . $rand); + } + + function replaceFFMpeg() + { + copy(WS_COMPILE_ASSETS . '/_exehtml/_ffmpeg/' . $this->nwplatform . '-ffmpeg.dll', $this->getFinalPackageDir() . '/ffmpeg.dll'); + } + + function makeJSON() + { + $data = ['app_name' => $this->appName, 'main' => 'index.html', 'name' => $this->exeName, 'version' => '1.0.' . time(), + 'webkit' => [], + 'window' => ['height' => 768, 'width' => 1024, 'title' => $this->appName, 'id' => 'main', 'icon' => 'icon.png', 'mac_icon' => 'icon.icns'], + 'dependencies' => ['child_process' => "^1.0.2", + 'fs' => '0.0.1-security', + 'path' => '^0.12.7'], + ]; + + if ($this->book->parametres->offlineWindowsProfilePath != '') { + $datadir = str_replace('%title%', $this->exeName, $this->book->parametres->offlineWindowsProfilePath); + $data['chromium-args'] = '--crash-dumps-dir=\'' . $datadir . '\' --user-data-dir=\'' . $datadir . '\''; + } + + + $pngIcon = $this->vdir . '/icon.png'; + $winIcon = $this->vdir . '/icon.ico'; + + $png = WS_COMPILE_ASSETS . '/fluidbook.png'; + $ico = WS_COMPILE_ASSETS . '/fluidbook.ico'; + + if ($this->theme->parametres->favicon != '') { + if (file_exists($this->vdir . '/data/favicon.png')) { + $png = $this->vdir . '/data/favicon.png'; + } + if (file_exists($this->vdir . '/data/favicon.ico')) { + $ico = $this->vdir . '/data/favicon.ico'; + } + } + + $icns = $this->vdir . '/icon.icns'; + $this->copy($png, $pngIcon); + $this->copy($ico, $winIcon); + commonTools::pngToIcns($png, $icns); + + + file_put_contents($this->vdir . '/package.json', json_encode($data)); + } + + public function makePackage($zip) + { + parent::makePackage($zip); + $res = $this->zip(); + $this->postPackage(); + return $res; + } + + public function getFinalPackageDir() + { + return $this->buildPath . '/' . $this->exeName . '/' . $this->nwplatform; + } + + protected function compile($forceCompile = false) + { + // For exe version, force to export only the html5 version + // No need to export pages with texts for this version, we are certain that svg is supported if enabled + if ($this->book->parametres->mobileVersion == 'html5-desktop') { + $this->book->parametres->mobileVersion = 'html5'; + } + $this->book->parametres->seoVersion = false; + + $this->daoBook->compile($this->book_id, 'html5', false, $this->book->parametres->forceCompileOnDownload, false, $this->book); + } + + protected function copyFluidbookFiles() + { + // Copie du FB vers un répertoire temporaire + $cp = new CommandLine('cp'); + $cp->setArg('R'); + $cp->setArg('p'); + $cp->setArg(null, WS_BOOKS . '/html5/' . $this->book->book_id . '/*'); + $cp->setArg(null, $this->vdir); + $cp->execute(); + + $this->copyExtras(); + $this->copyNodeModules(); + } + + protected function copyExtras() + { + + if ($this->book->parametres->form == 'bourbon') { + + $dest = $this->vdir . '/exe'; + if (!file_exists($dest)) { + mkdir($dest, 0777, true); + } + $this->copy(WS_FILES . '/bourbon/sendemail.exe', $dest . '/sendemail.exe'); + } + } + + protected function copyNodeModules() + { + $dest = $this->vdir . '/node_modules'; + if (!file_exists($dest)) { + mkdir($dest, 0777, true); + } + $cp = new CommandLine('cp'); + $cp->setArg('R'); + $cp->setArg('p'); + $cp->setArg(null, WS_COMPILE_ASSETS . '/_exehtml/_node_modules_' . $this->node_platform . '/*'); + $cp->setArg(null, $dest); + $cp->execute(); + } +} diff --git a/app/Http/Controllers/Admin/Operations/FluidbookPublication/EditOperation.php b/app/Http/Controllers/Admin/Operations/FluidbookPublication/EditOperation.php index 72a5311d2..cf6798be9 100644 --- a/app/Http/Controllers/Admin/Operations/FluidbookPublication/EditOperation.php +++ b/app/Http/Controllers/Admin/Operations/FluidbookPublication/EditOperation.php @@ -2,9 +2,8 @@ namespace App\Http\Controllers\Admin\Operations\FluidbookPublication; -use App\Models\FluidbookDocument; +use App\Fluidbook\Links; use App\Models\FluidbookPublication; -use App\Util\FluidbookLinks; use Cubist\Backpack\Http\Controllers\Base\XSendFileController; use Cubist\Util\Files\Files; use Illuminate\Http\UploadedFile; @@ -48,7 +47,7 @@ trait EditOperation abort(401); } - FluidbookLinks::saveLinksInFile($fluidbook_id, + Links::saveLinksInFile($fluidbook_id, backpack_user()->id, request('message'), json_decode(request('links', '[]'), true), @@ -56,7 +55,7 @@ trait EditOperation ); $fb = FluidbookPublication::find($fluidbook_id); - return response()->json(['assets' => $fb->getLinksAssetsDimensions(), 'versions' => FluidbookLinks::getLinksVersions($fluidbook_id)]); + return response()->json(['assets' => $fb->getLinksAssetsDimensions(), 'versions' => Links::getLinksVersions($fluidbook_id)]); } protected function moveLinks($fluidbook_id) @@ -77,7 +76,7 @@ trait EditOperation $width = $fb->getPageWidth(); $isOnePage = $fb->isOnePage(); - FluidbookLinks::getLinksAndRulers($fluidbook_id, $links, $rulers); + Links::getLinksAndRulers($fluidbook_id, $links, $rulers); $rlinks = array(); foreach ($links as $k => $link) { @@ -125,7 +124,7 @@ trait EditOperation $rrulers[$k] = $ruler; } - FluidbookLinks::saveLinksInFile($fluidbook_id, backpack_user()->id, __('Décalage de :nb pages à partir de la page :page', ['nb' => $offset, 'page' => $from]), $rlinks, $rrulers); + Links::saveLinksInFile($fluidbook_id, backpack_user()->id, __('Décalage de :nb pages à partir de la page :page', ['nb' => $offset, 'page' => $from]), $rlinks, $rrulers); return response()->json(['success' => 'ok']); } @@ -160,7 +159,7 @@ trait EditOperation if (!FluidbookPublication::hasPermission($fluidbook_id)) { abort(401); } - $links = FluidbookLinks::getLinksVersions($fluidbook_id); + $links = Links::getLinksVersions($fluidbook_id); return response()->json($links); } @@ -169,8 +168,8 @@ trait EditOperation if (!FluidbookPublication::hasPermission($fluidbook_id)) { abort(401); } - FluidbookLinks::getLinksAndRulers($fluidbook_id, $links, $rulers, $version); - $xlsx = FluidbookLinks::linksToExcel($links, $rulers); + Links::getLinksAndRulers($fluidbook_id, $links, $rulers, $version); + $xlsx = Links::linksToExcel($links, $rulers); $tmpfile = Files::tempnam() . '.xlsx'; $writer = new Xlsx($xlsx); $writer->save($tmpfile); @@ -183,8 +182,8 @@ trait EditOperation abort(401); } - FluidbookLinks::getLinksAndRulers($fluidbook_id, $links, $rulers, $version); - FluidbookLinks::saveLinksInFile($fluidbook_id, backpack_user()->id, __('Restaurer la sauvegarde des liens :date', ['date' => date('Y-m-d H:i:s', $version)]), $links, $rulers, [], []); + Links::getLinksAndRulers($fluidbook_id, $links, $rulers, $version); + Links::saveLinksInFile($fluidbook_id, backpack_user()->id, __('Restaurer la sauvegarde des liens :date', ['date' => date('Y-m-d H:i:s', $version)]), $links, $rulers, [], []); return response()->json(['success' => 'ok']); } @@ -196,8 +195,8 @@ trait EditOperation /** @var UploadedFile $uploadedFile */ $uploadedFile = request()->file('file'); - FluidbookLinks::getLinksAndRulersFromExcelFile($uploadedFile->getPathname(), $links, $rulers); - FluidbookLinks::saveLinksInFile($fluidbook_id, backpack_user()->id, __("Remplacer les liens à partir du fichier :file", ['file' => $uploadedFile->getClientOriginalName()]), $links, $rulers, [], []); + Links::getLinksAndRulersFromExcelFile($uploadedFile->getPathname(), $links, $rulers); + Links::saveLinksInFile($fluidbook_id, backpack_user()->id, __("Remplacer les liens à partir du fichier :file", ['file' => $uploadedFile->getClientOriginalName()]), $links, $rulers, [], []); return response()->json(['success' => 'ok']); } @@ -210,8 +209,8 @@ trait EditOperation /** @var UploadedFile $uploadedFile */ $uploadedFile = request()->file('file'); - FluidbookLinks::getLinksAndRulers($fluidbook_id, $merged_links, $merged_rulers); - FluidbookLinks::getLinksAndRulersFromExcelFile($uploadedFile->getPathname(), $links, $rulers); + Links::getLinksAndRulers($fluidbook_id, $merged_links, $merged_rulers); + Links::getLinksAndRulersFromExcelFile($uploadedFile->getPathname(), $links, $rulers); $existing_uids = []; foreach ($merged_links as $merged_link) { $existing_uids[$merged_link['uid']] = true; @@ -219,7 +218,7 @@ trait EditOperation foreach ($links as $link) { if (isset($existing_uids[$link['uid']])) { - $link['uid'] = FluidbookLinks::generateUID(); + $link['uid'] = Links::generateUID(); $existing_uids[$link['uid']] = true; } $merged_links[] = $link; @@ -227,7 +226,7 @@ trait EditOperation $merged_rulers = array_merge($merged_rulers, $rulers); - FluidbookLinks::saveLinksInFile($fluidbook_id, backpack_user()->id, __("Ajouter les liens à partir du fichier :file", ['file' => $uploadedFile->getClientOriginalName()]) . ' ', $merged_links, $merged_rulers, [], []); + Links::saveLinksInFile($fluidbook_id, backpack_user()->id, __("Ajouter les liens à partir du fichier :file", ['file' => $uploadedFile->getClientOriginalName()]) . ' ', $merged_links, $merged_rulers, [], []); return response()->json(['success' => 'ok']); } diff --git a/app/Jobs/FluidbookCompiler.php b/app/Jobs/FluidbookCompiler.php index 49172d88f..f2f97568f 100644 --- a/app/Jobs/FluidbookCompiler.php +++ b/app/Jobs/FluidbookCompiler.php @@ -2,17 +2,18 @@ namespace App\Jobs; +use App\Fluidbook\Links; +use App\Fluidbook\Link\Link; use App\Fluidbook\PDF; -use App\Http\Controllers\Admin\Operations\FluidbookPublication\Services\SocialImageOperation; -use App\Http\Controllers\Admin\Operations\Tools\Favicon; -use App\Models\Signature; use App\Fluidbook\SearchIndex; use App\Fluidbook\SEO\Document; +use App\Http\Controllers\Admin\Operations\FluidbookPublication\Services\SocialImageOperation; +use App\Http\Controllers\Admin\Operations\Tools\Favicon; use App\Models\FluidbookPublication; use App\Models\FluidbookTheme; use App\Models\FluidbookTranslate; +use App\Models\Signature; use App\Models\Traits\FluidbookPlayerBranches; -use App\Util\FluidbookLinks; use Cubist\Backpack\Magic\Fields\Checkbox; use Cubist\Excel\ExcelToArray; use Cubist\Locale\Country; @@ -33,11 +34,9 @@ use Cubist\Util\WebVideo; use DOMDocument; use DOMElement; use DOMXPath; -use Faker\Provider\File; use Fluidbook\Tools\Compiler\CompilerInterface; use Fluidbook\Tools\Links\AnchorLink; use Fluidbook\Tools\Links\ContentLink; -use App\Fluidbook\Link\Link; use Fluidbook\Tools\SVG\SVGTools; use Illuminate\Console\Command; use SimpleXMLElement; @@ -662,7 +661,7 @@ class FluidbookCompiler extends Base implements CompilerInterface $file = $cdir . $this->fluidbookSettings->basketReferences; $this->config->basketReferences = ExcelToArray::excelToArrayKeyVars($file); - FluidbookLinks::getLinksAndRulers($this->book_id, $links, $rulers); + Links::getLinksAndRulers($this->book_id, $links, $rulers); foreach ($links as $link) { if ($link['type'] == '12') { @@ -693,7 +692,7 @@ class FluidbookCompiler extends Base implements CompilerInterface $file = $cdir . $this->fluidbookSettings->basketReferences; $this->config->basketReferences = ExcelToArray::excelToArrayKeyVars($file); - FluidbookLinks::getLinksAndRulers($this->book_id, $links, $rulers); + Links::getLinksAndRulers($this->book_id, $links, $rulers); foreach ($this->config->basketReferences as $ref => $data) { $source = $cdir . '/' . $data['Image']; @@ -775,7 +774,7 @@ class FluidbookCompiler extends Base implements CompilerInterface $this->vdir->copy($cdir . $f, 'data/commerce/' . $f); } - FluidbookLinks::getLinksAndRulers($this->book_id, $links, $rulers); + Links::getLinksAndRulers($this->book_id, $links, $rulers); } public function writeGrandPavoisCart() @@ -795,7 +794,7 @@ class FluidbookCompiler extends Base implements CompilerInterface $file = $cdir . $this->fluidbookSettings->basketReferences; $this->config->basketReferences = ExcelToArray::excelToArrayKeyVars($file); - FluidbookLinks::getLinksAndRulers($this->book_id, $links, $rulers); + Links::getLinksAndRulers($this->book_id, $links, $rulers); } @@ -816,7 +815,7 @@ class FluidbookCompiler extends Base implements CompilerInterface $this->config->eanReferences = ExcelToArray::excelToArrayIndexKeyVars($eanFile); } - FluidbookLinks::getLinksAndRulers($this->book_id, $links, $rulers); + Links::getLinksAndRulers($this->book_id, $links, $rulers); foreach ($links as $link) { if ($link['type'] == '12' && isset($this->config->basketReferences[$link['to']])) { $this->config->basketReferences[$link['to']]['zoom_image'] = 'data/links/zoom_' . $link['uid'] . '.jpg'; @@ -2187,7 +2186,7 @@ height="0" width="0" style="display:none;visibility:hidden"> $this->vdir->copyDirectory($d['dir'], $d['fdir']); } - FluidbookLinks::getLinksAndRulers($this->book_id, $links, $rulers); + Links::getLinksAndRulers($this->book_id, $links, $rulers); if ($this->fluidbookSettings->basketManager === 'Puma') { foreach ($links as $k => $init) { @@ -2283,7 +2282,7 @@ height="0" width="0" style="display:none;visibility:hidden"> $anchorExists[$linkData['to']] = $linkData; } if ($linkData['type'] == 35 || $linkData['type'] == 15 || $linkData['type'] == 39) { - $linkData = FluidbookLinks::decryptLink($linkData); + $linkData = Links::decryptLink($linkData); $animations = ContentLink::parseAnimations($linkData['image_rollover']); foreach ($animations as $animation) { if (isset($animation['backgroundcolor']) && $animation['backgroundcolor'] !== 'transparent') { diff --git a/app/Models/FluidbookDocument.php b/app/Models/FluidbookDocument.php index 3ae438568..30a4566b6 100644 --- a/app/Models/FluidbookDocument.php +++ b/app/Models/FluidbookDocument.php @@ -2,10 +2,10 @@ namespace App\Models; +use App\Fluidbook\Farm; use App\Jobs\FluidbookDocumentFileProcess; use App\Jobs\FluidbookDocumentUpload; use App\Models\Base\ToolboxModel; -use App\Util\FluidbookFarm; use Cubist\Backpack\Magic\Fields\Integer; use Cubist\Backpack\Magic\Fields\Text; use Cubist\Backpack\Magic\Fields\Textarea; @@ -329,7 +329,7 @@ class FluidbookDocument extends ToolboxModel public function _getFile($page, $format = 'jpg', $resolution = 150, $withText = true, $withGraphics = true, $version = 'html') { if (!$this->hasFile($page, $format, $resolution, $withText, $withGraphics, $version)) { - return FluidbookFarm::getFile($page, $format, $resolution, $withText, $withGraphics, $version, $this->getResolutionRatio(), $this->getMobileFirstRatio(), $this->path()); + return Farm::getFile($page, $format, $resolution, $withText, $withGraphics, $version, $this->getResolutionRatio(), $this->getMobileFirstRatio(), $this->path()); } $path = $this->_getPath($page, $format, $resolution, $withText, $withGraphics, $version); diff --git a/app/Models/FluidbookPublication.php b/app/Models/FluidbookPublication.php index 05d57a9ba..41c57325d 100644 --- a/app/Models/FluidbookPublication.php +++ b/app/Models/FluidbookPublication.php @@ -8,17 +8,17 @@ use App\Fields\FluidbookChapters; use App\Fields\FluidbookComposition; use App\Fields\FluidbookLocale; use App\Fields\User; +use App\Fluidbook\Links; use App\Http\Controllers\Admin\Operations\FluidbookPublication\CompositionOperation; use App\Http\Controllers\Admin\Operations\FluidbookPublication\DeletefbOperation; use App\Http\Controllers\Admin\Operations\FluidbookPublication\DownloadOperation; +use App\Http\Controllers\Admin\Operations\FluidbookPublication\EditOperation; use App\Http\Controllers\Admin\Operations\FluidbookPublication\PreviewOperation; use App\Http\Controllers\Admin\Operations\FluidbookPublication\StatsOperation; -use App\Http\Controllers\Admin\Operations\FluidbookPublication\EditOperation; use App\Jobs\FluidbookImagesPreprocess; use App\Models\Base\ToolboxSettingsModel; use App\Models\Traits\PublicationSettings; use App\Models\Traits\SCORMVersionTrait; -use App\Util\FluidbookLinks; use Backpack\CRUD\app\Http\Controllers\Operations\CloneOperation; use Backpack\CRUD\app\Library\Widget; use Cubist\Backpack\Magic\Fields\FormBigSection; @@ -373,7 +373,7 @@ class FluidbookPublication extends ToolboxSettingsModel public function getLinksAndRulers(&$links, &$rulers) { - FluidbookLinks::getLinksAndRulers($this->id, $links, $rulers); + Links::getLinksAndRulers($this->id, $links, $rulers); } public function getLinksAssetsDimensions() diff --git a/app/Util/FluidbookFarm.php b/app/Util/FluidbookFarm.php deleted file mode 100644 index ebfd5d4b4..000000000 --- a/app/Util/FluidbookFarm.php +++ /dev/null @@ -1,147 +0,0 @@ - 'alphaville', 'host' => 'fluidbook-processfarm', 'port' => 9000, 'weight' => 24], - ['name' => 'brazil', 'host' => 'brazil.cubedesigners.com', 'weight' => 6], - ['name' => 'clockwork', 'host' => 'clockwork.cubedesigners.com', 'weight' => 2], - ['name' => 'dracula', 'host' => 'dracula.cubedesigners.com', 'weight' => 3], - ['name' => 'elephantman', 'host' => 'elephantman.cubedesigners.com', 'weight' => 1], - ['name' => 'fastandfurious', 'host' => 'fastandfurious.cubedesigners.com', 'weight' => 1], - ['name' => 'godzilla', 'host' => 'godzilla.cubedesigners.com', 'weight' => 3], - ['name' => 'her', 'host' => 'her2.cubedesigners.com', 'weight' => 4], - ['name' => 'isleofdogs', 'host' => 'paris.cubedesigners.com', 'port' => 9458, 'weight' => 2], - ['name' => 'jumanji', 'host' => 'paris.cubedesigners.com', 'port' => 9459, 'weight' => 2], - ]; - - protected static function _pingCache() - { - return Files::mkdir(storage_path('fluidbookfarm')) . '/pings'; - } - - protected static function _serversCache() - { - return Files::mkdir(storage_path('fluidbookfarm')) . '/servers'; - } - - public static function getServers() - { - return self::$_farmServers; - } - - public static function pickOneServer() - { - $hat = []; - $pingCache = self::_pingCache(); - if (!file_exists($pingCache)) { - self::ping(false); - } - $pings = json_decode(file_get_contents(self::_pingCache())); - - foreach (self::$_farmServers as $k => $farmServer) { - if (!isset($pings[$k]) || !$pings[$k]) { - continue; - } - for ($i = 0; $i < $farmServer['weight']; $i++) { - $hat[] = $k; - } - } - shuffle($hat); - $i = array_pop($hat); - return self::$_farmServers[$i]; - } - - public static function getFCGIConnexion(array $farm, $timeout = 240): NetworkSocket - { - $timeout *= 1000; - return new NetworkSocket($farm['host'], $farm['port'] ?? 9457, $timeout, $timeout); - } - - public static function sendRequest($farmer, $url, $params = [], $timeout = 240) - { - set_time_limit(0); - $client = new Client(); - $response = $client->sendRequest(self::getFCGIConnexion($farmer, $timeout), new PostRequest($url, http_build_query($params))); - return trim($response->getBody()); - } - - public static function getFile($page, $format, $resolution, $withText, $withGraphics, $version, $resolutionRatio, $mobileFirstRatio, $path, $force = false) - { - $start = microtime(true); - $farmer = self::pickOneServer(); - - $params = ['page' => $page, 'format' => $format, 'resolution' => $resolution, 'withText' => $withText, 'withGraphics' => $withGraphics, 'version' => $version, 'force' => $force, 'out' => $path, 'resolutionRatio' => $resolutionRatio, 'mobileRatio' => $mobileFirstRatio]; - - $output = self::sendRequest($farmer, 'process.php', $params); - if (preg_match('|/data1/extranet/www/[^\s]+|', $output, $matches)) { - $o = $matches[0]; - } else { - $o = $output; - } - - if (file_exists($o)) { - $res = $o; - } else { - echo $o; - $res = false; - } - - $time = round(microtime(true) - $start, 4); - $log = '[' . $farmer['name'] . ']' . "\t" . date('Y-m-d H:i:s') . "\t" . $time . "\t$page|$format|$resolution|$withText|$withGraphics|$version\t$res\t" . $output . "\n"; - - error_log($log); - - return $res; - } - - public static function ping($echo = true, $force = false) - { - $cache = self::_pingCache(); - $servers = self::getServers(); - $pings = []; - if (file_exists($cache)) { - $cached = json_decode(file_get_contents($cache)); - if (is_countable($cached) && count($cached) === count($servers)) { - $pings = $cached; - } - } - - foreach ($servers as $id => $farmer) { - if ($echo) { - echo $farmer['name'] . ' (' . $id . ') || '; - } - if (isset($pings[$id]) && !$pings[$id]) { - // If ping failed recently, we wait a bit before trying again. - if (!$force && rand(0, 9) != 5) { - if ($echo) { - echo 'Skipped, will try again soon' . "\n"; - } - continue; - } - } - try { - $res = self::sendRequest($farmer, 'ping.php', [], 5); - $ok = $res == '1'; - } catch (\Exception $e) { - $res = $e->getMessage(); - $ok = false; - } - - if ($echo) { - echo ($ok ? 'OK' : 'KO') . ' : ' . trim($res) . "\n"; - } - - $pings[$id] = $ok; - } - file_put_contents($cache, json_encode($pings)); - file_put_contents(self::_serversCache(), json_encode($servers)); - } -} diff --git a/app/Util/FluidbookLinks.php b/app/Util/FluidbookLinks.php deleted file mode 100644 index cf6cc2830..000000000 --- a/app/Util/FluidbookLinks.php +++ /dev/null @@ -1,478 +0,0 @@ - __('Identifiant unique'), - 'page' => __('Page de la publication'), 'left' => __('x'), 'top' => __('y'), 'width' => __('Largeur'), 'height' => __('Hauteur'), 'rot' => __('Rotation'), - 'type' => __('Type'), 'to' => __('Destination'), 'target' => __('Cible'), - 'infobulle' => __('Infobulle'), 'numerotation' => __('Numérotation'), - 'display_area' => __('Activer la surbrillance'), - 'video_loop' => __('Video : boucle'), 'video_auto_start' => __('Video : démarrage automatique'), 'video_controls' => __('Vidéo : afficher les contrôles'), 'video_sound_on' => __('Vidéo : activer le son'), - 'inline' => __('Vidéo : afficher dans la page'), 'video_width' => __('Vidéo : Largeur du popup'), 'video_height' => __('Vidéo : Hauteur du popup'), - 'interactive' => __('Interactivité'), 'video_service' => __('Webvideo : service'), - 'extra' => __('Paramètre supplémentaire'), - 'alternative' => __('Alternative'), - 'read_mode' => __('Mode de lecture'), - 'image' => __('Image'), 'image_rollover' => __('Animation au survol'), - 'animation' => __('Animation'), - 'group' => __('Groupe'), - 'zindex' => __('Profondeur'), - ); - - $comments = array(); - - $xls = new Spreadsheet(); - $s = $xls->setActiveSheetIndex(0); - $s->setTitle('Links'); - - // Labels - $i = 1; - foreach ($cols as $id => $label) { - $s->setCellValueByColumnAndRow($i, 1, $id); - $s->getColumnDimensionByColumn($i)->setAutoSize(true); - $s->getStyleByColumnAndRow($i, 1)->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER); - $i++; - } - - // Links - self::_correctImageSpecialLinks($links); - $j = 2; - foreach ($links as $l) { - $i = 1; - foreach ($cols as $id => $label) { - if (($id == 'document_id' || $id == 'document_page')) { - if (!is_null($pages)) { - $infos = $pages[$l['page']]; - $value = $infos[$id]; - } else { - $value = ''; - } - } else { - - if (isset($l[$id])) { - if (is_bool($l[$id])) { - $l[$id] = $l[$id] ? '1' : '0'; - } - if ($id === 'numerotation') { - if ($l[$id] === 'false') { - $l[$id] = 'physical'; - } - } - if ($id === 'to') { - $s->getCellByColumnAndRow($i, $j)->setDataType(DataType::TYPE_STRING)->getStyle()->getNumberFormat()->setFormatCode(NumberFormat::FORMAT_TEXT); - } - $value = $l[$id]; - } else { - $value = ''; - } - } - - $s->setCellValueExplicitByColumnAndRow($i, $j, $value, DataType::TYPE_STRING); - $i++; - } - $j++; - } - // Rulers - $s = $xls->createSheet(); - $s->setTitle('Rulers'); - - $rcols = array('page', 'type', 'pos'); - $i = 1; - // Labels - foreach ($rcols as $id) { - $s->setCellValueByColumnAndRow($i, 1, $id); - $s->getColumnDimensionByColumn($i)->setAutoSize(true); - $i++; - } - - // Contents - $j = 2; - foreach ($rulers as $r) { - $i = 1; - foreach ($rcols as $id) { - if (!is_null($pages) && ($id == 'document_id' || $id == 'document_page')) { - $infos = $pages[$r['page']]; - $value = $infos[$id]; - } else { - $value = $r[$id]; - } - $s->setCellValueByColumnAndRow($i, $j, $value); - $s->getStyleByColumnAndRow($i, $j)->getAlignment()->setHorizontal(Alignment::HORIZONTAL_LEFT); - $i++; - } - $j++; - } - - $xls->setActiveSheetIndex(0); - return $xls; - } - - - public static function getLinksAndRulers($book_id, &$links, &$rulers, $time = 'latest') - { - if (null === $time) { - $time = 'latest'; - } - $dir = self::getLinksDir($book_id); - - $file = $dir . '/' . $time . '.links3.gz'; - if ($time === 'latest' && !file_exists($file)) { - $versions = self::getLinksVersions($book_id); - foreach ($versions as $version => $m) { - copy(Files::firstThatExists($dir . '/' . $version . '.links3.gz', $dir . '/' . $version . '.links.gz'), $dir . '/latest.links3.gz'); - copy(Files::firstThatExists($dir . '/' . $version . '.meta3.gz', $dir . '/' . $version . '.meta.gz'), $dir . '/latest.meta3.gz'); - break; - } - } - if (!file_exists($file)) { - $links = []; - $rulers = []; - return; - } - - $r = json_decode(gzdecode(file_get_contents($file)), true); - $links = self::_UID($r['links']); - $rulers = self::_UID($r['rulers']); - if (can('fluidbook-publication:links:edit-animations')) { - $links = Link::decryptLinks($links); - }else{ - $links = Link::encryptLinks($links); - } - - self::_correctImageSpecialLinks($links); - } - - protected static function _UID($items) - { - $res = []; - foreach ($items as $item) { - if (!isset($item['uid'])) { - $item['uid'] = self::uid(); - } - $res[$item['uid']] = $item; - } - return $res; - } - - protected static function uid() - { - return Str::lower(Str::random(12)); - } - - protected static function _correctImageSpecialLinks(&$links) - { - foreach ($links as $k => $link) { - if (preg_match('/^link_(.*)$/', $link['page'], $matches) && strlen($matches[1]) !== 32) { - $uid = $matches[1]; - foreach ($links as $l) { - if ($l['uid'] === $uid && $l['alternative']) { - $links[$k]['page'] = 'link_' . md5($l['alternative']); - break; - } - } - } else if (preg_match('/^([0-9a-f]{32})$/', $link['page'], $matches)) { - $links[$k]['page'] = 'link_' . $matches[1]; - } - } - } - - public static function getLinksFromExcel($xls, &$links, &$rulers) - { - $s = $xls->setActiveSheetIndexByName('Links'); - $i = 0; - $links = array(); - foreach ($s->getRowIterator() as $row) { - $cellIterator = $row->getCellIterator(); - $cellIterator->setIterateOnlyExistingCells(false); - if ($i == 0) { - $cols = array(); - foreach ($cellIterator as $cell) { - $cols[] = $cell->getValue(); - } - } else { - $link = array(); - $j = 0; - foreach ($cellIterator as $cell) { - $link[$cols[$j]] = $cell->getValue(); - $j++; - } - if ($link['display_area'] == '' || !$link['display_area']) { - $link['display_area'] = '0'; - } - if (trim($link['infobulle']) == '') { - $link['infobulle'] = ''; - } - $links[] = $link; - } - - $i++; - } - - $i = 0; - $rulers = array(); - $s = $xls->setActiveSheetIndexByName('Rulers'); - foreach ($s->getRowIterator() as $row) { - $cellIterator = $row->getCellIterator(); - $cellIterator->setIterateOnlyExistingCells(false); - if ($i == 0) { - $cols = array(); - foreach ($cellIterator as $cell) { - $cols[] = $cell->getValue(); - } - } else { - $link = array(); - $j = 0; - foreach ($cellIterator as $cell) { - $ruler[$cols[$j]] = $cell->getValue(); - $j++; - } - - $rulers[] = $ruler; - } - $i++; - } - - self::_correctImageSpecialLinks($links); - } - - public static function getLinksFromAutobookmarkText($txt, &$links, &$rulers) - { - $links = array(); - $rulers = array(); - - $lines = explode("\n", $txt); - - $protocols = array('mailto' => 3, 'custom' => 7, 'cart' => 12, 'pagelabel' => 26); - - foreach ($lines as $line) { - $line = trim($line); - if ($line == '') { - continue; - } - if (strpos('#', $line) === 0) { - continue; - } - $target = $numerotation = ''; - list($page, $left, $top, $width, $height, $type, $to) = explode(';', $line); - if ($type <= 2) { - $target = '_blank'; - } elseif ($type == 5) { - $numerotation = 'physical'; - } - - $links[] = array( - 'page' => $page, - 'left' => $left, 'top' => $top, 'width' => $width, 'height' => $height, 'rot' => '', - 'type' => $type, 'to' => $to, 'target' => $target, - 'infobulle' => '', 'numerotation' => $numerotation, 'display_area' => '1'); - } - - self::_correctImageSpecialLinks($links); - } - - public static function saveLinksInFile($book_id, $user_id, $comments, $links, $rulers = [], $specialLinks = [], $specialRulers = []) - { - $lr = self::mergeLinksAndRulers($links, $rulers, $specialLinks, $specialRulers); - $meta = ['links' => count($lr['links']), 'rulers' => count($lr['rulers']), 'comments' => $comments, 'user' => $user_id]; - $base = self::getLinksDir($book_id) . '/' . time(); - $latestLinks = self::getLinksDir($book_id) . '/latest.links3.gz'; - $latestMeta = self::getLinksDir($book_id) . '/latest.meta3.gz'; - file_put_contents($base . '.meta3.gz', gzencode(json_encode($meta))); - file_put_contents($base . '.links3.gz', gzencode(json_encode($lr))); - copy($base . '.links3.gz', $latestLinks); - copy($base . '.meta3.gz', $latestMeta); - } - - - public static function getLinksDir($book_id) - { - return Files::mkdir('/data/extranet/www/fluidbook/books/links/' . $book_id); - } - - public static function getLinksVersions($book_id) - { - $dir = self::getLinksDir($book_id); - $dr = opendir($dir); - $updates = []; - while ($f = readdir($dr)) { - if ($f === '.' || $f === '..') { - continue; - } - $e = explode('.', $f, 2); - if (($e[1] !== 'meta.gz' && $e[1] !== 'meta3.gz') || $e[0] === 'latest') { - continue; - } - - $updates[$e[0]] = self::getMeta($book_id, $e[0]); - } - krsort($updates); - - - $res = []; - foreach ($updates as $timestamp => $u) { - try { - $u['name'] = User::find($u['user'])->name; - } catch (\Exception $e) { - $u['name'] = '-'; - } - $u['date'] = date('Y-m-d H:i:s', $timestamp); - $u['timestamp'] = $timestamp; - $res[] = $u; - } - - return $res; - } - - public static function getMeta($book_id, $update = 'latest') - { - return json_decode(gzdecode(file_get_contents(Files::firstThatExists(self::getLinksDir($book_id) . '/' . $update . '.meta3.gz', self::getLinksDir($book_id) . '/' . $update . '.meta.gz'))), true); - } - - public static function mergeLinksAndRulers($links, $rulers, $specialLinks, $specialRulers) - { - $finalLinks = []; - $l = array_merge(self::_getAsArray($links), self::_getAsArray($specialLinks)); - - $k = 0; - foreach ($l as $item) { - $item['id'] = $k + 1; - if (!isset($item['to'])) { - $item['to'] = ''; - } - $finalLinks[] = $item; - $k++; - } - - self::_correctImageSpecialLinks($finalLinks); - - return ['links' => Link::encryptLinks($finalLinks), 'rulers' => array_merge(self::_getAsArray($rulers), self::_getAsArray($specialRulers))]; - } - - protected static function _getAsArray($v) - { - if (is_array($v)) { - return $v; - } - return json_decode($v, true); - } - - public static function addLinksFromPDF($book_id) - { - global $core; - - $daoBook = new wsDAOBook($core->con); - $pages = $daoBook->getPagesOfBook($book_id); - - $booleans = array('video_loop', 'video_auto_start', 'video_controls', 'video_sound_on'); - $numbers = ['left', 'top', 'width', 'height']; - - $links = []; - - foreach ($pages as $page => $info) { - $csv = wsDocument::getDir($info['document_id']) . '/p' . $info['document_page'] . '.csv'; - if (!file_exists($csv) && file_exists($csv . '.gz')) { - $csv = 'compress.zlib://' . $csv . '.gz'; - } elseif (!file_exists($csv)) { - continue; - } - - $newformat = (filemtime($csv) > 1363685416); - - $fp = fopen($csv, 'rb'); - - while (true) { - $line = fgetcsv($fp, 512, ';', '"'); - // End of file - if (!$line) { - break; - } - - // Commentaire || ligne vide - if (substr($line[0], 0, 1) == '#' || is_null($line[0])) { - continue; - } - - $link = []; - if ($newformat) { - $cols = array('page' => '', 'left' => '', 'top' => '', 'width' => '', 'height' => '', 'type' => '', 'to' => '', 'target' => '_blank', 'video_loop' => true, 'video_auto_start' => true, 'video_controls' => true, 'video_sound_on' => true, 'infobulle' => '', 'numerotation' => 'physical', "inline" => true); - } else { - $cols = array('page' => '', 'type' => '', 'to' => '', 'left' => '', 'top' => '', 'width' => '', 'height' => '', 'target' => '_blank', 'video_loop' => true, 'video_auto_start' => true, 'video_controls' => true, 'video_sound_on' => true, 'infobulle' => '', 'numerotation' => 'physical'); - } - - - $k = 0; - foreach ($cols as $col => $default) { - if (isset($line[$k])) { - if (in_array($k, $numbers)) { - $link[$col] = (float)str_replace(',', '.', $line[$k]); - } else if (in_array($k, $booleans)) { - $link[$col] = ($line[$k] == '1'); - } else { - $link[$col] = utf8_encode($line[$k]); - } - } else { - $link[$col] = $default; - } - $k++; - } - - if ($link['type'] == 18) { - $link['infobulle'] = $link['to']; - $link['to'] = ''; - } - - $link['display_area'] = '1'; - $link['page'] = $page; - $links[] = $link; - } - - } - - self::saveLinksInFile($book_id, $core->user->utilisateur_id, 'Links imported from PDF', $links, []); - } - - public static function getLinksAndRulersFromExcelFile($path, &$links, &$rulers) - { - $reader = new \PhpOffice\PhpSpreadsheet\Reader\Xlsx(); - $xls = $reader->load($path); - FluidbookLinks::getLinksFromExcel($xls, $links, $rulers); - } - - public static function generateUID() - { - $characters = '0123456789abcdefghijklmnopqrstuvwxyz'; - $randstring = ''; - for ($i = 0; $i < 12; $i++) { - $randstring = $characters[rand(0, 35)]; - } - return $randstring; - } -}