From: Vincent Vanwaelscappel Date: Fri, 21 Apr 2023 10:30:27 +0000 (+0200) Subject: fix #5399 @0.5 X-Git-Url: http://git.cubedesigners.com/?a=commitdiff_plain;h=b4769c14ed83e03325ab724f32c14f98df120fa5;p=fluidbook-toolbox.git fix #5399 @0.5 --- diff --git a/app/Console/Commands/FluidbookCompile.php b/app/Console/Commands/FluidbookCompile.php index 18d161227..6339d4914 100644 --- a/app/Console/Commands/FluidbookCompile.php +++ b/app/Console/Commands/FluidbookCompile.php @@ -2,7 +2,7 @@ namespace App\Console\Commands; -use App\Jobs\FluidbookCompiler; +use App\Fluidbook\Compiler; use App\Models\FluidbookPublication; use Cubist\Backpack\Console\Commands\CubistCommand; use Cubist\Util\PHP; @@ -19,7 +19,7 @@ class FluidbookCompile extends CubistCommand { PHP::neverStop(true); $fluidbook = FluidbookPublication::find($this->argument('id')); - $compiler = new FluidbookCompiler($fluidbook, command: $this); + $compiler = new Compiler($fluidbook, command: $this); $compiler->handle(); } } diff --git a/app/Console/Commands/FluidbookDocumentProcess.php b/app/Console/Commands/FluidbookDocumentProcess.php index 0de115376..009457e7a 100644 --- a/app/Console/Commands/FluidbookDocumentProcess.php +++ b/app/Console/Commands/FluidbookDocumentProcess.php @@ -2,10 +2,8 @@ namespace App\Console\Commands; -use App\Jobs\FluidbookCompiler; use App\Jobs\FluidbookDocumentUpload; use App\Models\FluidbookDocument; -use App\Models\FluidbookPublication; use App\Models\User; use Cubist\Backpack\Console\Commands\CubistCommand; use Cubist\Util\PHP; diff --git a/app/Console/Commands/FluidbookPreprocessImages.php b/app/Console/Commands/FluidbookPreprocessImages.php index 45287da36..6edb950da 100644 --- a/app/Console/Commands/FluidbookPreprocessImages.php +++ b/app/Console/Commands/FluidbookPreprocessImages.php @@ -2,9 +2,7 @@ namespace App\Console\Commands; -use App\Jobs\FluidbookCompiler; use App\Jobs\FluidbookImagesPreprocess; -use App\Models\FluidbookPublication; use Cubist\Backpack\Console\Commands\CubistCommand; use Cubist\Util\PHP; diff --git a/app/Fluidbook/Compiler.php b/app/Fluidbook/Compiler.php new file mode 100644 index 000000000..f20137c75 --- /dev/null +++ b/app/Fluidbook/Compiler.php @@ -0,0 +1,4099 @@ + + ['js/libs/cube/util.js', + 'js/libs/cube/fb.js',], + 'modernizr' => + ['js/libs/modernizr/modernizr.min.js', + 'js/libs/modernizr/tests.js',], + 'modifier' => ['js/libs/threejs/modifier.min.js'], + 'threejs' => + ['js/libs/threejs/legacy/three.min.js', + 'js/libs/threejs/legacy/Projector.js', + 'js/libs/threejs/legacy/CanvasRenderer.js', + ], +// 'threejs-latest' => +// ['js/libs/threejs/latest/three.min.js', +// ], + 'jquery' => + [ + 'js/libs/jquery/jquery.min.js', + ], + 'jquery-extras' => ['js/libs/jquery/jquery.transform.js', + 'js/libs/jquery/jquery.form.min.js', + 'js/libs/jquery/jquery.mousewheel.min.js', + 'js/libs/jquery/jquery.hashchange.min.js', + 'js/libs/jquery/jquery.scrollto.min.js', + ], + 'aria' => ['js/libs/aria/radio.js',], + 'bluebird' => ['js/libs/bluebird.min.js'], + 'screenfull' => ['js/libs/screenfull.min.js'], + 'storage' => ['js/libs/storage.js',], + 'hotkeys' => ['js/libs/hotkeys.min.js',], + 'forge' => ['js/libs/forge/forge-sha256.min.js',], + 'perfectscrollbar' => ['js/libs/perfect-scrollbar/perfect-scrollbar.js', + 'js/libs/perfect-scrollbar/perfect-scrollbar.jquery.js'], + 'confirm' => ['js/libs/jquery/jquery.confirm.min.js'], + 'mmenu' => + ['js/libs/mmenu/jquery.mmenu.all.js'], + 'gsap' => + ['js/libs/gsap/gsap.min.js', + 'js/libs/gsap/ScrollToPlugin.min.js', + 'js/libs/gsap/InertiaPlugin.min.js', + 'js/libs/gsap/Draggable.min.js', + ], + 'hammer' => ['js/libs/hammer.min.js',], + 'interactjs' => ['js/libs/interact.min.js'], + 'gal' => + ['js/libs/gal/gal.js', + 'js/libs/gal/gal.filesystem.js',], + 'raphael' => + ['js/libs/raphael/raphael.min.js', + 'js/libs/gsap/plugins/RaphaelPlugin.min.js'], + 'countup' => + ['js/libs/countup/countup.min.js'], + 'clipboard' => ['js/libs/clipboard.min.js'], + 'fluidbook' => + ['js/libs/fluidbook/fluidbook.utils.js', + 'js/libs/fluidbook/fluidbook.networkcontrol.js', + 'js/libs/fluidbook/fluidbook.splash.js', + 'js/libs/fluidbook/fluidbook.links.js', + 'js/libs/fluidbook/fluidbook.support.js', + 'js/libs/fluidbook/fluidbook.video.js', + 'js/libs/fluidbook/fluidbook.viewport.js', + 'js/libs/fluidbook/fluidbook.desktop.js', + 'js/libs/fluidbook/fluidbook.service.js', + 'js/libs/fluidbook/fluidbook.share.js', + 'js/libs/fluidbook/fluidbook.l10n.js', + 'js/libs/fluidbook/fluidbook.slider.js', + 'js/libs/fluidbook/fluidbook.pagetransitions.js', + 'js/libs/fluidbook/fluidbook.nav.js', + 'js/libs/fluidbook/fluidbook.interface.js', + 'js/libs/fluidbook/fluidbook.input.js', + 'js/libs/fluidbook/fluidbook.touch.js', + 'js/libs/fluidbook/fluidbook.loader.js', + 'js/libs/fluidbook/fluidbook.search.js', + 'js/libs/fluidbook/fluidbook.help.js', + 'js/libs/fluidbook/fluidbook.resize.js', + 'js/libs/fluidbook/fluidbook.stats.js', + 'js/libs/fluidbook/fluidbook.cache.js', + 'js/libs/fluidbook/fluidbook.tooltip.js', + 'js/libs/fluidbook/fluidbook.bookmarks.js', + 'js/libs/fluidbook/fluidbook.background.js', + 'js/libs/fluidbook/fluidbook.pad.js', + 'js/libs/fluidbook/fluidbook.audiodescription.js', + 'js/libs/fluidbook/fluidbook.audioplayer.js', + 'js/libs/fluidbook/fluidbook.accessibility.js', + 'js/libs/fluidbook/fluidbook.privacy.js', + 'js/libs/fluidbook/fluidbook.zoom.js', + 'js/libs/fluidbook/fluidbook.menu.js', + 'js/libs/fluidbook/fluidbook.sound.js', + 'js/libs/fluidbook/fluidbook.contentlock.js', + 'js/libs/fluidbook/fluidbook.scorm.js', + 'js/libs/fluidbook/fluidbook.3dflip.js', + 'js/libs/fluidbook/menu/fluidbook.chapters.js', + 'js/libs/fluidbook/menu/fluidbook.index.js', + 'js/libs/fluidbook/fluidbook.landingpage.js', + 'js/libs/fluidbook/fluidbook.print.js', + 'js/libs/fluidbook/fluidbook.secure.js', + 'js/libs/fluidbook/fluidbook.tabs.js', + 'js/libs/fluidbook/fluidbook.articles.js', + 'js/libs/fluidbook/fluidbook.widget.js', + 'js/libs/fluidbook/fluidbook.keyboard.js', + 'js/libs/fluidbook/fluidbook.posad.js', + 'js/libs/fluidbook/fluidbook.notes.js', + 'js/libs/fluidbook/fluidbook.gamify.js', + 'js/libs/fluidbook/fluidbook.js', + 'js/main.js'], + 'mobilefirst' => [ + 'js/libs/fluidbook/fluidbook.mobilefirst.js', + 'js/libs/fluidbook/mobilefirst/fluidbook.mobilefirst.slider.js', + ], + ]; + + protected $specialJsFiles = array(); + + + public $jsFiles = []; + + // Collection of LESS files to be compiled + // Filename with no extension, relative to the /style directory in the player build folder + public $lessFiles = ['fluidbook']; + + public $specialCSS = array(); + public $phonegapStandardPlugins = array('ios' => array('ExternalFileUtil'), + 'android' => array('webintent')); + public $pluginCSS = array(); + public $pluginJs = array(); + public $htmlmultimedia = array(); + public $cssX = array(); + public $cssY = array(); + public $cssWidths = array(); + public $pdf2htmlRatio; + public $scale; + public $multiply; + public $div = array(); + public $numerotation; + public $fontDocs = array(); + public $dir; + public $z = 3; + protected $_lottieIDByHash = []; + + public $pages; + public $theme; + public $devversion; + public $book_id; + public $themeRoot; + public $needToRecompileContents = true; + public $needToRecompileSettings = true; + public $width; + public $height; + public $cssWidth; + public $cssHeight; + public $cssOneWidth; + public $cssOneHeight; + public $cssScale; + public $linkScale; + public $optimalWidth = 567; + public $optimalHeight = 709; + public $additionalConfig = array(); + public $fontScale = 1; + public $cache = array(); + public $backgroundsPrefix = array(); + public $svg = true; + public $assets = ''; + public $phonegap = false; + public $phonegapVersion; + public $standalone = false; + public $hiddenContents = array(); + public $appcache; + public $home; + public $widget = true; + public $multiApp = false; + public $pageLabels = array(); + public $stylesheets = array(); + public $logfp = null; + public $logtime = null; + public $beginBody = array(); + public $seoArticles = []; + public $securityPolicyWhitelist = ['*.google-analytics.com', '*.youtube.com', '*.ytimg.com', '*.googletagmanager.com']; + public $writeLinksData = false; + public $content_lock = []; + public $cssfont = []; + public $lessVariables = ["import-cart-styles" => 'none']; + protected $_indexVars = null; + public $accessibleTexts = []; + protected $_svgSymbols = []; + protected $_addedPDFJS = false; + protected $audioDescriptionTextsList = []; + protected $hybrid = false; + + protected $_docDimensions = []; + + public $_signature; + public $seo = null; + + /** + * @var \Cubist\Backpack\Magic\EntityData + */ + public $fluidbookSettings; + + /** + * @var \Cubist\Backpack\Magic\EntityData + */ + public $themeSettings; + + /** + * @var Command + */ + protected $_command = null; + + /** + * @param FluidbookPublication $book + * @param bool $scormVariant + * @param $phonegap + * @param $phonegapVersion + * @param $dir + * @param $standalone + * @param $appcache + * @param $home + * @param $theme FluidbookTheme|null + * @param $hybrid + * @param Command|null $command + * @throws \Exception + */ + + function __construct(FluidbookPublication $book, $scormVariant = false, $phonegap = false, $phonegapVersion = 'latest', $dir = null, $standalone = false, $appcache = false, $home = false, FluidbookTheme $theme = null, $hybrid = false, Command $command = null) + { + ExcelToArray::setCache(protected_path('fluidbookpublication/cache/exceltoarray')); + + parent::__construct(); + + $this->setFluidbook($book); + $this->setCommand($command); + + $this->phonegapVersion = self::getPhonegapVersion($phonegapVersion); + $this->appcache = $appcache; + $this->multiApp = $this->home = $home; + $this->devversion = $this->getFluidbook()->mobileLVersion; + $this->scormVariant = $scormVariant; + + $this->hybrid = $hybrid; + + $this->assets = self::getSourcesPath($this->devversion); + $this->compiledAssets = self::getCompiledSourcesPath($this->devversion); + + $this->phonegap = $phonegap; + $this->standalone = $standalone || $this->phonegap; + $this->appcache = $appcache; + $this->widget = !$this->phonegap; + + $this->fluidbookSettings = $this->getFluidbook()->getSettings(); + + PHP::memoryAllocate('12G'); + + $this->book_id = $this->getFluidbook()->id; + $this->log('Start compilation'); + + $this->dir = $this->getFluidbook()->getFinalPath($theme, $scormVariant); + $this->vdir = new VirtualDirectory($this->dir); + + $this->wdir = $this->getFluidbook()->getAssetDir(); + + $this->widget = false; + + $this->pages = $this->getFluidbook()->composition; + $this->maxRes = min(self::MAX_RES, $this->fluidbookSettings->maxResolution); + + $this->theme = $theme ?? $this->getFluidbook()->getTheme(); + + $this->themeSettings = $this->theme->getPageData(); + $this->log('Got data from database'); + + $this->width = round($this->getFluidbook()->getPageWidth(), 8); + $this->height = round($this->getFluidbook()->getPageHeight(), 8); + + $this->imageFormat = $this->fluidbookSettings->imageFormat; + + $p1 = $this->getFluidbook()->getFile(1, $this->imageFormat, 150); + $imagesize = Image::getimagesize($p1); + $this->pdf2htmlRatio = round(($imagesize[0] * 0.48) / $this->width, 12); + + $this->linkScale = $this->cssScale = $this->z * min($this->optimalWidth / $this->width, $this->optimalHeight / $this->height); + + $this->cssOneScale = $this->z * min(($this->optimalWidth * 2) / $this->width, $this->optimalHeight / $this->height); + + $this->cssWidth = $this->width * $this->cssScale; + $this->cssHeight = $this->height * $this->cssScale; + + $this->cssOneWidth = $this->width * $this->cssOneScale; + $this->cssOneHeight = $this->height * $this->cssOneScale; + + $this->scale = 1; + + if ($this->isMobileFirst()) { + $this->cssScale = $this->cssOneScale = 480 / $this->width; + $this->linkScale = $this->cssScale; + $this->cssOneWidth = $this->cssWidth = $this->width * $this->cssScale; + $this->cssOneHeight = $this->cssHeight = $this->height * $this->cssScale; + $this->initMobileFirst(); + } + + $this->svgfiles = array_unique([$this->assets . '/images/symbols/interface.svg', + storage_path('icons/15.svg')]); + if ($this->themeSettings->iconSet > 15) { + $this->svgfiles[] = storage_path('icons/' . $this->themeSettings->iconSet . '.svg'); + } + if ($symbols = $this->themeAsset('symbols')) { + $this->svgfiles[] = $symbols->getPathname(); + } + + if ($this->isMobileFirst()) { + $this->multiply = $this->pdf2htmlRatio * $this->scale * $this->cssOneScale; + } else { + $this->multiply = $this->pdf2htmlRatio * $this->scale * $this->cssScale; + } + + $this->initConfig(); + $this->log('Defined dimensions'); + } + + public function getFinalPath() + { + return $this->dir; + } + + + public function getSetting($key, $default = null) + { + if ($this->fluidbookSettings->has($key)) { + return $this->fluidbookSettings->get($key, $default); + } + if ($this->themeSettings->has($key)) { + return $this->themeSettings->get($key, $default); + } + return $default; + } + + + public function setSetting($key, $value) + { + if ($this->themeSettings->has($key)) { + $this->themeSettings->set($key, $value); + return; + } + $this->fluidbookSettings->set($key, $value); + } + + + /** + * @param $key + * @param $default + * @return null|SplFileInfo + */ + public function themeAsset($key) + { + $collection = $this->theme->{$key}; + + if ($collection) { + $path = $this->theme->getFirstMediaPath($collection); + if (!$path || !file_exists($path)) { + $res = false; + } else { + $res = new SplFileInfo($path); + } + } else { + $res = false; + } + if (null !== $this->config) { + if ($res instanceof SplFileInfo) { + $this->config->set($key, $res->getFilename()); + } else { + $this->config->set($key, ''); + } + } + return $res; + } + + /** + * @return FluidbookPublication + */ + public function getFluidbook(): FluidbookPublication + { + return $this->_fluidbook; + } + + /** + * @param FluidbookPublication $fluidbook + */ + public function setFluidbook(FluidbookPublication $fluidbook): void + { + $this->_fluidbook = $fluidbook; + } + + /** + * @return Command|null + */ + public function getCommand(): ?Command + { + return $this->_command; + } + + /** + * @param Command|null $command + */ + public function setCommand(?Command $command): void + { + $this->_command = $command; + } + + public function isMobileFirst() + { + return $this->fluidbookSettings->mobileNavigationType === 'mobilefirst'; + } + + public function initMobileFirst() + { + $this->themeSettings->usePageEdges = false; + } + + public function initConfig() + { + if (!$this->scormVariant) { + $this->fluidbookSettings->scorm_enable = false; + } + + $this->config = new Data(array_merge($this->fluidbookSettings->getRawData()['settings'], $this->themeSettings->getRawData())); + $this->config->pages = count($this->getFluidbook()->composition); + $this->config->bookmarkDisablePages = ArrayUtil::parseRange($this->config->bookmarkDisablePages); + $this->config->rasterizePages = ArrayUtil::parseRange($this->config->rasterizePages); + $this->config->vectorPages = array_diff(ArrayUtil::parseRange($this->config->vectorPages), $this->config->rasterizePages); + $this->numerotation = $this->config->numerotation = explode(',', $this->getFluidbook()->page_numbers); + + $hideOnPages = ArrayUtil::parseRange($this->config->tabsHideOnPages); + $this->config->tabsDisabledOnPages = ArrayUtil::parseRange($this->config->tabsDisabledOnPages); + + if ($this->config->tabsHideOnCover) { + $hideOnPages[] = 0; + $hideOnPages[] = 1; + } + if ($this->config->tabsHideOnLastPage) { + $hideOnPages[] = count($this->pages); + } + $this->config->tabsHideOnPages = $hideOnPages; + $this->config->triggersLinks = []; + $this->config->hasContentLock = false; + } + + public function populateConfig() + { + $this->config->id = $this->getFluidbook()->book_id; + $this->config->cid = $this->getFluidbook()->cid; + $this->config->cacheDate = time(); + $this->config->width = round($this->cssWidth, 2); + $this->config->height = round($this->cssHeight, 2); + $this->config->optimalWidth = round($this->optimalWidth, 2); + $this->config->optimalHeight = round($this->optimalHeight, 2); + $this->config->chapters = $this->getFluidbook()->chapters; + $this->config->videoFormats = $this->getVideosFormats(false); + $this->config->htmlmultimedia = $this->htmlmultimedia; + $this->config->phonegap = $this->phonegap; + $this->config->retinaResolution = min($this->fluidbookSettings->maxResolution, $this->maxRes); + $this->config->standardResolution = min($this->fluidbookSettings->maxResolution, 150); + $this->config->pageLabels = $this->pageLabels; + $this->config->pageZoomFactor = $this->z; + $this->config->multiply = round($this->multiply, 6); + $this->config->cssScale = round($this->cssScale, 6); + $this->config->pdfZoomFactor = round($this->pdf2htmlRatio, 6); + if ($this->home) { + $this->config->home = 'http://home'; + } + $this->config->multiApp = $this->multiApp; + foreach ($this->additionalConfig as $k => $v) { + $this->config->$k = $v; + } + if ($this->phonegap && ($this->fluidbookSettings->offlineLink == '' || $this->fluidbookSettings->offlineLink == 'http://')) { + $this->config->share = false; + } + if ($this->config->maxPages > 0) { + $this->addContentLock($this->config->maxPages); + } + + // We need to be able to reference both navOrder and navOrderH so convert both to arrays + // We also make sure there are no empty items in the arrays (see: http://php.net/manual/en/function.array-filter.php#111091) + $this->config->navOrder = array_filter(array_map('trim', explode(',', $this->config->navOrder)), 'strlen'); + $this->config->navOrderH = array_filter(array_map('trim', explode(',', $this->config->navOrderH)), 'strlen'); + + $this->config->standalone = $this->standalone; + if ($this->config->phonegap) { + $this->config->manifest = $this->writeManifest(); + } + + if ($this->config->form == 'bulle') { + $this->addJsLib('bulle', 'js/libs/fluidbook/forms/fluidbook.form.bulle.js'); + } else if ($this->config->form == 'bourbon') { + $this->addJsLib('parsley', 'js/libs/parsley.min.js'); + $this->addJsLib('bourbon', 'js/libs/fluidbook/forms/fluidbook.form.bourbon.js'); + } else if ($this->config->form == 'avery') { + $this->addJsLib('parsley', 'js/libs/parsley.min.js'); + $this->addJsLib('avery', 'js/libs/fluidbook/forms/fluidbook.form.avery.js'); + $this->addLess('form/avery'); + $this->writeCountries(); + } + $this->config->seoArticles = $this->seoArticles; + } + + public function writeGrandVisionCart() + { + $this->lessVariables['import-cart-styles'] = 'grandvision'; + + $this->addJsLib('cookie', 'js/libs/jquery/jquery.cookie.js'); + $this->addJsLib('grandvision', 'js/libs/fluidbook/cart/fluidbook.cart.grandvision.js'); + $this->addJsLib('html2pdf', 'js/libs/html2pdf/html2pdf.min.js'); + $this->addJsLib('multiselect', 'js/libs/jquery/jquery.multi-select.js'); + $this->addJsLib('jqueryui', 'js/libs/jquery/jquery-ui.min.js'); + $this->addJsLib('exceljs', 'js/libs/exceljs.min.js'); + $this->svgfiles[] = $this->assets . '/images/symbols/grandvision.svg'; + + $cdir = $this->wdir . '/commerce/'; + $file = $cdir . $this->fluidbookSettings->basketReferences; + $refs = ExcelToArray::excelToArrayKeyVars($file, 'Excel2007', true); + $this->config->basketReferences = []; + foreach ($refs as $ean => $ref) { + $this->config->basketReferences[$ean] = $ref; + $this->config->basketReferences[$ean]['angle_url'] = base64_encode(file_get_contents($this->wdir . '/commerce/opt/' . $ean . '-angle.jpg')); + } + + $odir = $cdir . '/opt/'; + if (!file_exists($odir)) { + mkdir($odir, 0777, true); + } + + $it = Files::getDirectoryIterator($cdir); + $exts = ['png', 'jpg', 'tif', 'mp4']; + foreach ($it as $file) { + + /** @var $file SplFileInfo */ + if ($file->isDir()) { + continue; + } + $ext = $file->getExtension(); + if (!in_array($ext, $exts)) { + continue; + } + + $e = Text::multiExplode('_-.', mb_strtolower($file->getFilename())); + $ean = $this->findEAN($e); + if (!$ean) { + continue; + } + if (!isset($this->config->basketReferences[$ean])) { + continue; + } + $f = $file->getPathname(); + + if ($ext === 'mp4') { + $n = $ean . '-360.mp4'; + $this->config->basketReferences[$ean]['360'] = true; + $opt = $odir . '/' . $n; + if (!file_exists($opt) || !filesize($opt) || filemtime($opt) < filemtime($f)) { + // Optimize original video + `ffmpeg -i $f -filter:v scale=360:-2 -vcodec libx264 -an $opt`; + touch($opt, filemtime($f)); + } + } else { + if (in_array('front', $e)) { + $type = 'front'; + } else if (in_array('angle', $e)) { + $type = 'angle'; + } else { + continue; + } + + $n = $ean . '-' . $type . '.jpg'; + $this->config->basketReferences[$ean][$type] = true; + $opt = $odir . '/' . $n; + if (!file_exists($opt) || !filesize($opt) || filemtime($opt) < filemtime($f)) { + // Optimize original image + $convert = new Resizer(); + $convert->loadImage($f); + $convert->resize(1080, null, 'ratio', false, 'C', 'M', 'white'); + $convert->output('jpg', $opt, 75); + touch($opt, filemtime($f)); + } + } + $this->vdir->copy($opt, 'data/commerce/' . $n); + } + } + + public function findEAN($array) + { + foreach ($array as $item) { + if (strlen($item) === 13 && preg_match('/^\d{13}$/', $item)) { + return $item; + } + } + return false; + } + + public function writeFlexipanCart() + { + $this->lessVariables['import-cart-styles'] = 'flexipan'; + + $this->addJsLib('cookie', 'js/libs/jquery/jquery.cookie.js'); + $this->addJsLib('flexipan', 'js/libs/fluidbook/cart/fluidbook.cart.flexipan.js'); + $this->addJsLib('html2pdf', 'js/libs/html2pdf/html2pdf.min.js'); + + $cdir = $this->wdir . '/commerce/'; + + + $file = $cdir . $this->fluidbookSettings->basketReferences; + $this->config->basketReferences = ExcelToArray::excelToArrayKeyVars($file); + + $this->getLinksAndRulers($links, $rulers); + + foreach ($links as $link) { + if ($link['type'] == '12') { + + } + } + + $this->config->product_zoom_references = []; + foreach ($this->config->basketReferences as $ref => $data) { + $this->config->product_zoom_references[$ref] = [$ref]; + } + } + + public function getLinksAndRulers(&$links, &$rulers) + { + Links::getLinksAndRulers($this->book_id, $links, $rulers, 'latest', true); + } + + public function writeMIFCart() + { + $this->lessVariables['import-cart-styles'] = 'mif'; + + $this->addJsLib('cookie', 'js/libs/jquery/jquery.cookie.js'); + $this->addJsLib('mif', 'js/libs/fluidbook/cart/fluidbook.cart.mif.js'); + $this->addJsLib('html2pdf', 'js/libs/html2pdf/html2pdf.min.js'); + + $cdir = $this->wdir . '/commerce/'; + $odir = $cdir . '/opt/'; + if (!file_exists($odir)) { + mkdir($odir, 0777, true); + } + + $file = $cdir . $this->fluidbookSettings->basketReferences; + $this->config->basketReferences = ExcelToArray::excelToArrayKeyVars($file); + + $this->getLinksAndRulers($links, $rulers); + + foreach ($this->config->basketReferences as $ref => $data) { + $source = $cdir . '/' . $data['Image']; + if (!file_exists($source)) { + continue; + } + $d = Text::str2URL($ref) . '.jpg'; + $dest = $odir . '/' . $d; + if (!file_exists($dest) || !filesize($dest) || filemtime($dest) < filemtime($source)) { + $convert = new Resizer(); + $convert->loadImage($source); + $convert->resize(500, 500, 'ratio', false, 'C', 'M', 'ffffff'); + $convert->output('jpg', $dest, 80); + } + $vdest = 'data/commerce/opt/' . $d; + $this->vdir->copy($dest, $vdest); + $this->config->basketReferences[$ref]['Image'] = $vdest; + } + + foreach ($links as $link) { + if ($link['type'] == '12') { + + } + } + +// $this->config->product_zoom_references = []; +// foreach ($this->config->basketReferences as $ref => $data) { +// $r = [$data['Lien']]; +// $this->config->product_zoom_references[$ref] = $r; +// } + } + + public function writeJoueClub2021Cart() + { + $this->lessVariables['import-cart-styles'] = 'joueclub2021'; + $extra = Link::parseExtras($this->fluidbookSettings->cartExtraSettings, true); + + /** + * buttonColor=#d7b646 + * buttonTextColor=#ffffff + * headerBackgroundColor=#0e1a3c + * headerTextColor=#ffffff + */ + $this->lessVariables['cart-button-color'] = $extra['buttoncolor'] ?? '#e30613'; + $this->lessVariables['cart-button-text-color'] = $extra['buttontextcolor'] ?? '#ffffff'; + $this->lessVariables['cart-button-radius'] = $extra['buttonradius'] ?? '50%'; + $this->lessVariables['cart-header-background-color'] = $extra['headerbackgroundcolor'] ?? '#26348b'; + $this->lessVariables['cart-header-text-color'] = $extra['headertextcolor'] ?? '#ffffff'; + $this->lessVariables['cart-close-color'] = $extra['closecolor'] ?? '#ffffff'; + $this->lessVariables['cart-close-background-color'] = $extra['closebackgroundcolor'] ?? '#e30613'; + $this->lessVariables['cart-close-radius'] = $extra['closeradius'] ?? '50%'; + $this->lessVariables['cart-actions-radius'] = $extra['actionsradius'] ?? '8px'; + $this->lessVariables['cart-actions-background-color'] = $extra['actionsbackgroundcolor'] ?? '#26348b'; + $this->lessVariables['cart-actions-text-color'] = $extra['actionstextcolor'] ?? '#ffffff'; + $this->lessVariables['cart-scrollbar-color'] = $extra['scrollbarcolor'] ?? '#26348b'; + + $this->addJsLib('cookie', 'js/libs/jquery/jquery.cookie.js'); + $this->addJsLib('joueclub2021', 'js/libs/fluidbook/cart/fluidbook.cart.joueclub2021.js'); + $this->addJsLib('html2pdf', 'js/libs/html2pdf/html2pdf.min.js'); + + $cdir = $this->wdir . '/commerce/'; + + $file = $cdir . $this->fluidbookSettings->basketReferences; + $this->config->basketReferences = ExcelToArray::excelToArrayKeyVars($file); + + foreach ($this->config->basketReferences as $ref => $data) { + $dest = $cdir . $ref . '.jpg'; + if (!file_exists($dest)) { + copy($data['img'], $dest); + } + $this->vdir->copy($dest, 'data/commerce/' . $ref . '.jpg'); + } + $addFiles = [$this->config->cartHeaderImage, $this->config->cartHeaderMobileImage]; + foreach ($addFiles as $f) { + if (!$f) { + return; + } + + $this->vdir->copy($cdir . $f, 'data/commerce/' . $f); + } + + $this->getLinksAndRulers($links, $rulers); + } + + public function writeGrandPavoisCart() + { + $this->lessVariables['import-cart-styles'] = 'grandpavois'; + + $this->addJsLib('cookie', 'js/libs/jquery/jquery.cookie.js'); + $this->addJsLib('grandpavois', 'js/libs/fluidbook/cart/fluidbook.cart.grandpavois.js'); + $this->addJsLib('html2pdf', 'js/libs/html2pdf/html2pdf.min.js'); + + $cdir = $this->wdir . '/commerce/'; + $odir = $cdir . '/opt/'; + if (!file_exists($odir)) { + mkdir($odir, 0777, true); + } + + $file = $cdir . $this->fluidbookSettings->basketReferences; + $this->config->basketReferences = ExcelToArray::excelToArrayKeyVars($file); + + $this->getLinksAndRulers($links, $rulers); + } + + + public function writePumaCart() + { + $this->lessVariables['import-cart-styles'] = 'puma'; + + $this->addJsLib('parsley', 'js/libs/parsley.min.js'); + $this->addJsLib('cookie', 'js/libs/jquery/jquery.cookie.js'); + $this->addJsLib('puma', 'js/libs/fluidbook/cart/fluidbook.cart.puma.js'); + $this->addJsLib('html2pdf', 'js/libs/html2pdf/html2pdf.min.js'); + $this->addJsLib('exceljs', 'js/libs/exceljs.min.js'); + $this->addVideoJs(); + + $this->config->basketReferences = ExcelToArray::excelToArrayKeyVars($this->wdir . 'commerce/' . $this->fluidbookSettings->basketReferences); + $eanFile = $this->wdir . 'commerce/ean.xlsx'; + if (file_exists($eanFile)) { + $this->config->eanReferences = ExcelToArray::excelToArrayIndexKeyVars($eanFile); + } + + $this->getLinksAndRulers($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'; + $this->config->basketReferences[$link['to']]['zoom_url'] = base64_encode(file_get_contents($this->dir . '/data/links/zoom_' . $link['uid'] . '.jpg')); + $this->config->basketReferences[$link['to']]['zoom_image_ratio'] = $link['width'] / $link['height']; + } + } + + $this->config->product_zoom_references = []; + $files = ['360°', 'Image supplémentaire', 'Fiche technique']; + foreach ($this->config->basketReferences as $ref => $data) { + $r = []; + foreach ($files as $file) { + if (!isset($data[$file])) { + $data[$file] = ''; + } + $fname = trim($data[$file]); + if ($fname !== '') { + $fname = str_replace(' ', '-', $fname); + $wfile = $this->wdir . 'commerce/' . $fname; + if (file_exists($wfile)) { + $fname = 'data/commerce/' . $fname; + $this->vdir->copy($wfile, $fname); + } else { + $fname = ''; + } + } + $r[] = $fname; + } + $this->config->product_zoom_references[$ref] = $r; + } + } + + + public function writeThirietCart() + { + $this->config->cartLinkAppearance = 'overlay'; + $this->svgfiles[] = $this->assets . '/images/symbols/cart-overlay.svg'; + $this->addJsLib('thiriet', 'js/libs/fluidbook/cart/fluidbook.cart.thiriet.js'); + } + + public function writeCFOCCart() + { + + $this->lessVariables['import-cart-styles'] = 'cfoc'; + + $this->addJsLib('cfoc', 'js/libs/fluidbook/cart/fluidbook.cart.cfoc.js'); + + if (!empty($this->config->basketReferences) && is_string($this->config->basketReferences)) { + if (file_exists($this->config->basketReferences) || Url::isDistant($this->config->basketReferences)) { + $referencesFile = $this->config->basketReferences; + } else { + $referencesFile = $this->wdir . 'commerce/' . $this->config->basketReferences; + } + } + + $references = []; + + if (file_exists($referencesFile) || Url::isDistant($referencesFile)) { + $rows = ExcelToArray::excelToArrayFirstSheet($referencesFile); + + + // Expected headings are: EXCLU, LIGNE, EAN, REF, DESIGNATION, COULEUR, QTE MINI, PRIX TTC + $column_headings = array_shift($rows); // We assume the first row will be the headings, so we slice it off + $column_headings = array_map(function ($heading) { // Clean the headings a bit + return trim(strtoupper($heading)); + }, $column_headings); + + foreach ($rows as $row) { + + // First, trim values in case there are any stray spaces + $row = array_map('trim', $row); + + // Next, set the headings as keys to make it easier to refer to the row values by name + $row = array_combine($column_headings, $row); + + // For the 'EXCLU' field, this should be converted to a boolean + $row['EXCLU'] = ('exclu boutique' === $row['EXCLU']); + + // Make sure the PRIX TTC field doesn't have any stray commas or spaces in the numbers + // because this will cause unexpected problems for the calculations + $row['PRIX TTC'] = str_replace([',', ' '], '', $row['PRIX TTC']); + + // The EAN and REF are required, so if they don't exist, we can assume it's not a valid row + if (empty($row['EAN']) || empty($row['REF'])) { + continue; + } + + $references[$row['REF']][$row['EAN']] = $row; + } + + } + + $this->config->basketReferences = $references; + + // It's possible to use the cartExtraSettings field in the parameters to define publication specific settings, + // such as the subject line used in the e-mail that is sent from the cart validation + $extra = Link::parseExtras($this->fluidbookSettings->cartExtraSettings, true); + + $this->config->cartEmailSubject = $extra['email_subject'] ?? 'Récapitulatif de votre commande CFOC'; + } + + /** + * @throws \Exception + */ + public function writeBastideCart() + { + + $this->lessVariables['import-cart-styles'] = 'bastide'; + + $this->addJsLib('bastide', 'js/libs/fluidbook/cart/fluidbook.cart.bastide.js'); + + + if (!empty($this->config->basketReferences)) { + if (file_exists($this->config->basketReferences) || Url::isDistant($this->config->basketReferences)) { + $referencesFile = $this->config->basketReferences; + } else { + $referencesFile = $this->wdir . 'commerce/' . $this->config->basketReferences; + } + } + + $references = []; + + if (file_exists($referencesFile) || Url::isDistant($referencesFile)) { + $rows = ExcelToArray::excelToArrayFirstSheet($referencesFile); + + // Expected headings are: n° page, Chapitre, Article Code, Article, Conditionnement + $column_headings = array_shift($rows); // We assume the first row will be the headings, so we slice it off + $column_headings = array_map(function ($heading) { // Normalise the headings, removing extra spaces and line breaks + return trim(strtoupper(preg_replace('/\s+/', ' ', $heading))); + }, $column_headings); + + foreach ($rows as $row) { + + // First, trim values in case there are any stray spaces + $row = array_map('trim', $row); + + // Next, set the headings as keys to make it easier to refer to the row values by name + $row = array_combine($column_headings, $row); + + // The ARTICLE CODE is required, so if it doesn't exist, we can assume it's not a valid row + if (empty($row['ARTICLE CODE'])) { + continue; + } + + $references[$row['ARTICLE CODE']] = $row; + } + + } + + $this->config->basketReferences = $references; + + // Allow individual Fluidbooks to override the columns shown in the cart + $extra = Link::parseExtras($this->config->cartExtraSettings, true); + + if (!empty($extra['cart_columns'])) { + // In the "Paramètres panier" field (cartExtraSettings), the cart columns can be defined in this format: + // cart_columns=XLS COL NAME|Display name,XLS COL 2|Display name 2 + // This setting needs to be trimmed and converted into an associative array with column_name => display_name + // Split by commas, then by pipes | + $columns = array_map(function ($heading) { + return explode('|', $heading); + }, explode(',', $extra['cart_columns'])); + $processed_columns = []; + foreach ($columns as $column) { + $processed_columns[strtoupper(trim($column[0]))] = trim($column[1] ?? ''); + } + + // Ensure that special QUANTITY and DELETE columns are present (see getColumns() in fluidbook.cart.bastide.js) + $processed_columns['QUANTITY'] = $processed_columns['QUANTITY'] ?? 'Quantité'; + $processed_columns['DELETE'] = ''; + + $this->config->cartColumns = $processed_columns; + } + } + + public function writeCartConfig() + { + if ($this->fluidbookSettings->cartLinkAppearance == 'overlay') { + $this->svgfiles[] = $this->assets . '/images/symbols/cart-overlay.svg'; + } + + if ($this->config->basket) { + $this->addJsLib('cart', 'js/libs/fluidbook/fluidbook.cart.js'); + switch ($this->config->basketManager) { + case 'Thiriet': + $this->writeThirietCart(); + return; + case 'Flexipan': + $this->writeFlexipanCart(); + return; + case 'Puma': + $this->writePumaCart(); + return; + case 'MIF': + $this->writeMIFCart(); + return; + case 'GrandVision': + $this->writeGrandVisionCart(); + return; + case 'GrandPavois': + $this->writeGrandPavoisCart(); + return; + case 'JoueclubWishlist2021': + $this->writeJoueClub2021Cart(); + return; + case 'Remarkable': + $this->addJsLib('parsley', 'js/libs/parsley.min.js'); + $this->addJsLib('cookie', 'js/libs/jquery/jquery.cookie.js'); + $this->addJsLib('remarkable', 'js/libs/fluidbook/cart/fluidbook.cart.remarkable.js'); + break; + case 'Mopec': + $this->addJsLib('parsley', 'js/libs/parsley.min.js'); + $this->addJsLib('cookie', 'js/libs/jquery/jquery.cookie.js'); + $this->addJsLib('mopec', 'js/libs/fluidbook/cart/fluidbook.cart.mopec.js'); + break; + case 'CFOC': + $this->writeCFOCCart(); + break; + case 'Bastide': + $this->writeBastideCart(); + break; + default: + break; + } + } + if (!$this->config->product_zoom_references && $this->config->basketReferences && $this->config->basketManager == "ZoomProductLink") { + $this->config->product_zoom_references = $this->config->basketReferences; + $this->config->basketReferences = ''; + } + + if ($this->config->product_zoom_references) { + if (file_exists($this->config->product_zoom_references) || Url::isDistant($this->config->product_zoom_references)) { + $referencesFile = $this->config->product_zoom_references; + } else { + $referencesFile = $this->wdir . '/commerce/' . $this->config->product_zoom_references; + } + if (file_exists($referencesFile) || Url::isDistant($referencesFile)) { + $this->config->product_zoom_references = ExcelToArray::excelToArrayKeyValMulti($referencesFile, 'Excel2007', true); + } + } + + if ($this->config->basketReferences && is_string($this->config->basketReferences)) { + if (file_exists($this->config->basketReferences) || Url::isDistant($this->config->basketReferences)) { + $referencesFile = $this->config->basketReferences; + } else { + $referencesFile = $this->wdir . '/commerce/' . $this->config->basketReferences; + } + + if (file_exists($referencesFile) || Url::isDistant($referencesFile)) { + $ext = Files::getExtension($referencesFile); + if ($ext == 'xlsx') { + if ($this->config->basketManager == "ZoomProductLink") { + $function = 'excelToArrayKeyVal'; + } else { + $function = 'excelToArray'; + } + $this->config->basketReferences = ExcelToArray::$function($referencesFile); + if ($this->fluidbookSettings->customLinkClass == 'AtlanticDownloadLink') { + $this->config->basketReferences = self::atlanticReferences($this->config->basketReferences, 'local/', array($this, 'log'), array($this->vdir, "copy")); + } + } + $this->log("Done cart references"); + } + } + } + + public static function atlanticReferences($references, $dir, $log = null, $copy = 'copy') + { + foreach ($references as $i => $sheet) { + foreach ($sheet as $j => $line) { + foreach ($line as $k => $v) { + if (preg_match('|^http:\/\/atlantic-international-book-com\.com\/files\/(.*)$|', $v, $matches)) { + $local = $dir . '/' . $matches[1]; + $url = str_replace(' ', '%20', $v); + $cache = protected_path('fluidbookpublication/atlantic') . md5($url); + if (!file_exists($cache) || filemtime($url) > filemtime($cache)) { + copy($url, $cache); + $copylog = ' (copy) '; + } else { + $copylog = ' '; + } + call_user_func($copy, $cache, $dir . '/' . $matches[1]); + $references[$i][$j][$k] = 'local/' . $matches[1]; + if (null !== $log) { + call_user_func($log, 'Done' . $copylog . $matches[1]); + } + } + } + } + } + + return $references; + } + + public function log($step) + { + $currenttime = microtime(true); + if (null === $this->logfp) { + $this->logfp = fopen(Files::mkdir(storage_path('logs/htmlconversions')) . $this->book_id . '.log', 'w+'); + } + if (null === $this->logtime) { + $this->logtime = $currenttime; + } + $time = $currenttime - $this->logtime; + $log = $step . ' | ' . round($time, 3) . 's' . "\n"; + fwrite($this->logfp, $log); + fflush($this->logfp); + $this->logtime = $currenttime; + + if (null !== $this->getCommand()) { + $this->getCommand()->info(trim($log)); + } + } + + public function addFacebookSDK() + { + $lang = str_replace('-', '_', $this->getFluidbook()->lang); + $e = explode('_', $lang); + if (count($e) > 1) { + $e[1] = mb_strtoupper($lang); + } + $lang = implode('_', $e); + + $langsMap = ['fr' => 'fr_FR', 'en' => 'en_US']; + + if (isset($langsMap[$lang])) { + $lang = $langsMap[$lang]; + } + + $this->beginBody[] = "
+"; + $this->securityPolicyWhitelist[] = '*.facebook.net'; + $this->securityPolicyWhitelist[] = 'data:'; + } + + public function addPageLabel($page, $label) + { + $this->pageLabels[$label] = $page; + } + + public function getResolutions() + { + return self::getBookResolutions($this->getFluidbook()); + } + + + public static function getBookResolutions($book) + { + $maxRes = min(self::MAX_RES, $book->settings['maxResolution']); + $res = []; + if ($maxRes == self::MAX_RES) { + $res = [150, self::MAX_RES]; + } else if ($maxRes <= 150) { + $res = [$maxRes]; + } + return $res; + } + + public function getCssScale() + { + return $this->cssScale; + } + + public function getLinkScale() + { + return $this->linkScale; + } + + public function virtualToPhysical($virtual): string|int + { + if (isset($this->pageLabels[$virtual])) { + return $virtual; + } + if (!in_array($virtual, $this->numerotation)) { + return $virtual; + } + $p = array_search($virtual, $this->numerotation); + return $p + 1; + } + + public function handle() + { + $this->log('Preprocess images'); + FluidbookImagesPreprocess::dispatchSync($this->book_id); + $this->log('Start compile process'); + + // Raw copy of some directories + $directories = array('style/fonts/OpenSans', 'images', 'sound'); + foreach ($directories as $directory) { + $from = $this->assets . '/' . $directory; + $this->vdir->copyDirectory($from, $directory); + } + + if ($this->fluidbookSettings->scorm_enable || $this->fluidbookSettings->secureClientSidePassword) { + $this->fluidbookSettings->seoVersion = false; + } + if ($this->fluidbookSettings->embedAllLibraries) { + $this->addVideoJs(); + $this->addSlideshowLibrary(false); + $this->addSlideshowLibrary(true); + } + + $this->log('Copied assets'); + $this->writeSecure(); + $this->loadPlugins(); + $this->log('Plugins loaded'); + $this->writeImages(); + $this->log('Images written'); + $this->writeCartConfig(); + $this->writeXMLArticles(); + $this->log('XML Articles written'); + $this->writeSlider(); + $this->log('Slider written'); + $linksCSS = $this->writeLinks(); + $this->log('Links written'); + $this->writeArticles(); + $this->log('Articles written'); + $this->writeStats(); + $this->log('Stats written'); + $this->writeLangs(); + $this->log('Langs written'); + $this->writeSEO(); + $this->log('SEO written'); + $this->writeWidget(); + $this->log('Widget written'); + $this->writeSounds(); + $this->log('Sound written'); + $this->writeTexts(); + $this->log('Texts written'); + $this->writeAccessibility(); + $this->log('Accessibility written'); + $this->writeExtras(); + $this->log('Extras written'); + $this->populateConfig(); + $this->log('Config populated'); + $this->writeCSS($linksCSS); + $this->log('CSS written'); + $this->writeIndex(); + $this->log('Index written'); + if ($this->fluidbookSettings->scorm_enable) { + $this->writeScorm(); + $this->log('SCORM written'); + } + $this->writeJs(); + $this->log('Js written'); + $this->vdir->sync(true, $this); + $this->log('Files Synced'); +// $f=rtrim(str_replace('/html5/', '/compiletime/', $this->dir)); +// touch($f); + } + + protected function writeSlider() + { + if ($this->fluidbookSettings->sliderImage) { + $dim = Image::getimagesize($this->wdir . '/' . $this->fluidbookSettings->sliderImage); + $this->config->sliderImageDimensions = $dim; + $this->copyLinkFile($this->fluidbookSettings->sliderImage, 'data/interface'); + } + } + + protected function writeStats() + { + if ($this->fluidbookSettings->stats) { + $this->config->statsMatomo = $this->book_id; + $this->config->statsMatomoServer = 3; + if ($this->book_id >= 21210) { + $this->config->statsMatomoServer = 4 + ($this->book_id % 2); + } + } else { + $this->config->statsMatomo = false; + } + + if ($this->fluidbookSettings->tagcommander_id) { + $id = $this->fluidbookSettings->tagcommander_id; + if (!$this->fluidbookSettings->tagcommander_prod) { + $id .= '/uat'; + } + + $default = ['page_name' => '']; + $this->config->tagcommander_default_vars = array_merge($default, $this->parseVariables($this->fluidbookSettings->tagcommander_default_vars)); + $this->config->tagcommander_default_vars['env_work'] = $this->fluidbookSettings->tagcommander_prod ? 'prod' : 'pre-prod'; + + $scriptNames = explode(',', $this->config->tagcommander_scriptname); + $this->fluidbookSettings->googleAnalyticsCustom .= ''; + for ($i = 1; $i < count($scriptNames); $i++) { + $this->fluidbookSettings->statsCustom .= ''; + } + + if ($this->fluidbookSettings->tagcommander_plan) { + $planPath = $this->_wdirOrAbsolute($this->fluidbookSettings->tagcommander_plan); + + $plan = ExcelToArray::excelToArrayKeyVars($planPath); + $fixedplan = []; + foreach ($plan as $k => $v) { + $e = explode('#', $k); + if (count($e) === 2) { + $k = $e[1]; + } + + $fixedplan[$this->_labelToPage($k)] = $v; + } + $this->config->tagcommander_plan = $fixedplan; + } + } + if (isset($this->fluidbookSettings->googleTagManager) && $this->fluidbookSettings->googleTagManager) { + $this->fluidbookSettings->googleAnalyticsCustom .= " + + +"; + $this->fluidbookSettings->statsCustom = ' + +'; + + + } + } + + + protected function _wdirOrAbsolute($path) + { + $e = explode('#', $path); + if (file_exists($e[0])) { + return $path; + } + return $this->wdir . $path; + + } + + protected function _labelToPage($k) + { + $k = trim($k, '#/'); + $k = str_replace('page/page', 'page', $k); + + if (preg_match('/^page\/(\d+)$/', $k, $matches)) { + return $k; + } + + if (preg_match('/^page\/(.+)$/', $k, $matches)) { + $matches[1] = Text::removeAccents($matches[1]); + $matches[1] = mb_strtolower($matches[1]); + if (isset($this->pageLabels[$matches[1]])) { + return 'page/' . $this->pageLabels[$matches[1]]; + } + } + return $k; + } + + protected function writeSecure() + { + if ($this->fluidbookSettings->secureClientSidePassword) { + $credentials = Text::explodeNewLines($this->fluidbookSettings->secureClientSidePasswordCredentials); + $credentials[] = 'fluidbook:LatacaM4##*'; + $users = []; + foreach ($credentials as $credential) { + $salt = bin2hex(random_bytes(5)); + $e = explode(':', $credential); + if (count($e) <= 1) { + continue; + } + $usersalt = bin2hex(random_bytes(5)); + $user = hash("sha256", $usersalt . '+' . $e[0]); + $users[$user] = ['salt' => $salt, 'usersalt' => $usersalt, 'hash' => hash("sha256", $salt . '-' . $e[1])]; + } + + $secure = file_get_contents($this->wdir . '/' . $this->fluidbookSettings->secureClientSidePassword); + $secure = str_replace('$CREDENTIALS', 'var CREDENTIALS=' . json_encode($users) . ';', $secure); + $secure = str_replace('$TITLE', $this->fluidbookSettings->title, $secure); + $secure = str_replace('$CODE', '$(function () { + $(\'form\').on(\'submit\', function () { + var u = $("#username").val(); + var p = $("#password").val(); + var error = true; + $.each(CREDENTIALS, function (user, data) { + if (forge_sha256(data.usersalt + \'+\' + u) === user && forge_sha256(data.salt + \'-\' + p) === data.hash) { + error = false; + window.sessionStorage.setItem(\'secureUsername\', u); + window.sessionStorage.setItem(\'securePassword\', p); + window.location = \'index.html\'; + } + }); + if (error) { + $("#message").text(\'Wrong username or password\'); + } + return false; + }); + });', $secure); + $this->vdir->file_put_contents('secure.html', $secure); + $this->config->secureClientSidePasswordCredentials = $users; + } + + if ($this->fluidbookSettings->recaptcha) { + $this->beginBody[] = ''; + } + } + + protected function loadPlugins() + { + $e = explode("\n", $this->fluidbookSettings->mobilePlugins); + $main = array_pop($this->jsFiles); + + $plugins = array(); + + foreach ($e as $plugin) { + $plugin = trim($plugin); + if ($plugin == '') { + continue; + } + + $d = 'plugins/' . str_replace('.', '/', $plugin); + $dir = $this->assets . '/' . $d; + if (!file_exists($dir)) { + continue; + } + + $plugins[] = $plugin; + + if (file_exists($dir . '/plugin.js')) { + $f = $d . '/plugin.js'; + $this->pluginJs[] = $f; + $this->vdir->copy($dir . '/plugin.js', $f); + } + if (file_exists($dir . '/plugin.css')) { + $f = $d . '/plugin.css'; + $this->pluginCSS[] = $f; + $this->vdir->copy($dir . '/plugin.css', $f); + } + } + + $this->config->plugins = $plugins; + + array_push($this->jsFiles, $main); + } + + public function getVideosFormats($poster = true) + { + $res = []; + $res[] = 'mp4'; + + if ($poster) { + $res[] = 'jpg'; + } + return $res; + } + + /** + * Helper function to add a unique script entry to the JS stack. + * Normally this is a relative path but it can be an external URL. + * External URLs are added to the pluginJs collection instead of jsFiles. + * Duplicate paths are ignored. + * @param $path + */ + public function addJs($path, $collection = null) + { + + if (null === $collection) { + // If JS is external, it will be included via the pluginJs collection + // Otherwise, it will be compiled into the main JS file + $collection = (preg_match('#^https?://#i', $path) === 1) ? 'pluginJs' : 'jsFiles'; + } + + if (!in_array($path, $this->$collection)) { + $this->{$collection}[] = $path; + } + } + + /** + * Helper function to add a unique stylesheet entry to the LESS stack for compilation + * Duplicate paths are ignored. + * @param $path string The path of the file relative to the /style folder, without any extension + */ + public function addLess($path) + { + if (!in_array($path, $this->lessFiles)) { + $this->lessFiles[] = $path; + } + } + + /** + * @throws \Exception + */ + protected function writeSounds() + { + if ($this->fluidbookSettings->soundTheme == '') { + return; + } + $dir = resource_path('fluidbookpublication/sounds/' . $this->fluidbookSettings->soundTheme); + $this->setSetting('simpleSoundTheme', file_exists($dir . '/flip.mp3')); + $this->vdir->copyDirectory($dir, 'data/sounds'); + } + + protected function writeAccessibility() + { + if ($this->fluidbookSettings->audiodescriptionTexts) { + + $file = $this->wdir . '/' . $this->fluidbookSettings->audiodescriptionTexts; + if (file_exists($file)) { + new PHPExcel(); + $reader = new PHPExcel_Reader_Excel2007(); + $phpexcel = $reader->load($file); + + $sheet = $phpexcel->getActiveSheet(); + $maxRow = $sheet->getHighestRow(0); + + for ($i = 0; $i <= $maxRow; $i++) { + $page = trim($sheet->getCellByColumnAndRow(0, $i)->getValue()); + $text = trim($sheet->getCellByColumnAndRow(1, $i)->getValue()); + $voice = trim($sheet->getCellByColumnAndRow(2, $i)->getValue()); + if ($page == '' || $text == '') { + continue; + } + $data = ['text' => $text]; + if ($voice) { + $data['voice'] = $voice; + } + $this->audioDescriptionTextsList[$page] = $data; + } + } + } + + foreach ($this->audioDescriptionTextsList as $page => $data) { + $replace = [ + '`' => "'", + '“' => '"', + '”' => '"', + '’' => "'", + '—' => " - ", + '‘' => "'", + "…" => "...", + ]; + + $text = trim($data['text']); + $text = str_replace(array_keys($replace), array_values($replace), $text); + $text = Text::cleanUTF8($text, ''); + + $voiceInfos = $data['voice'] ?? $this->fluidbookSettings->audiodescriptionVoice; + + if ($voiceInfos) { + $e = explode(':', $voiceInfos); + + if (count($e) === 1) { + $engine = 'azuretts'; + $voice = $voiceInfos; + } else { + $engine = $e[0]; + $voice = $e[1]; + } + + $hash = hash('sha256', $engine . ':' . $voice . '_^_' . $text); + $fname = $hash . '.mp3'; + $dir = Files::mkdir(protected_path('audiodescription')); + + $file = $dir . $fname; + + if (!file_exists($file) || filesize($file) === 0) { + if ($engine == 'azuretts') { + $e = explode('/', $voice); + $this->_azureTTS($text, $e[0], $e[1], $e[2], $file); + } + } + + $this->config->audiodescription[$page] = $fname; + $this->vdir->copy($file, 'data/audiodescription/' . $fname); + } + $this->accessibleTexts[$page] = $text; + } + + if (count($this->accessibleTexts) > 0) { + $this->config->accessibleTexts = $this->accessibleTexts; + } + } + + + protected function _azureTTS($text, $locale, $gender, $voiceName, $output) + { + try { + $api = new \Cubist\Azure\TTS\Api('28fdfcdcc7f141b29cd9db4afc5779c5'); + $api->textToSpeech($text, $locale, $gender, $voiceName, $output); + } catch (Exception $e) { + dd($e); + } + } + + protected function _writeIndex($page) + { + if (!isset($this->seo->pages[$page])) { + return; + } + /** @var Page $seo */ + $seo = $this->seo->pages[$page]; + if (!$this->fluidbookSettings->seoVersion) { + $seo->robots = false; + } + $html = $seo->getHTML(); + + if ($this->fluidbookSettings->seoVersion) { + $seo->writePage($html, $this->vdir); + } + if ($page == 1) { + $seo->writePage($html, $this->vdir, 'index.html'); + } + } + + /** + * @throws \Exception + */ + public function getIndexVars() + { + if (null === $this->_indexVars) { + $titre = $this->fluidbookSettings->title; + + if (null === $this->_signature) { + $this->_signature = Signature::find($this->fluidbookSettings->signature); + $credits = $this->_signature->credits; + } else { + $credits = ''; + } + $hiddenContents = implode("\n", $this->hiddenContents); + $bgcolor = $this->themeSettings->loadingBackColor; + + // Feuilles de style + $sheets = array_merge($this->stylesheets, $this->specialCSS); + + $style = array(); + foreach ($sheets as $sheet) { + $style[] = ''; + } + $style = implode("\n\t\t", $style); + + $this->log('Got index vars 1'); + + $pagesContents = ''; + + $cache = ''; + + $beginbody = implode("\n", array_unique($this->beginBody)); + + $jstime = "?j=" . time(); + + $iscript = ''; + if (count($this->htmlmultimedia)) { + $iscript .= '' . "\n"; + } + + $script = '' . "\n"; + foreach ($this->jsLibs as $jsLib => $files) { + $script .= "\t" . '' . "\n"; + } + if ($this->fluidbookSettings->scorm_enable) { + $script .= "\t" . '' . "\n"; + } + if (count($this->specialJsFiles)) { + $script .= "\t" . '' . "\n"; + } + foreach ($this->pluginJs as $p) { + $script .= "\t" . '' . "\n"; + } + $script .= $iscript; + + $this->log('Got index vars 2'); + + $socialTitle = htmlspecialchars($this->fluidbookSettings->facebook_title ?: $titre, ENT_COMPAT); + $socialDescription = htmlspecialchars($this->fluidbookSettings->facebook_description ?: $this->fluidbookSettings->seoDescription, ENT_COMPAT); + + $socialImage = 'https://toolbox.fluidbook.com/services/socialimage/' . $this->getFluidbook()->cid; + $dim = self::getSocialImageSize($this->getFluidbook()); + + $socialImageWidth = $dim[0]; + $socialImageHeight = $dim[1]; + + $this->log('Got index vars 2.5'); + + + $titre = $this->fluidbookSettings->title; + + $description = ''; + + $twittercard = ' + + + + '; + $opengraph = ' + + + + '; + + $this->log('Got index vars 3'); + + $favicon = ''; + if ($this->theme->hasFaviconFile()) { + $pngFile = $this->theme->getFaviconPath('png'); + $this->vdir->copy($this->theme->getFaviconPath('ico'), 'data/favicon.ico'); + $this->vdir->copy($pngFile, 'data/favicon.png'); + $this->vdir->copy($pngFile, 'data/apple-touch-icon.png'); + + $datapng = 'data:image/png;base64,' . base64_encode(file_get_contents($pngFile)); + + $favicon .= '' . "\n\t"; + $favicon .= ''; + } + + $print = $this->writePrint(); + $message = ''; + + $this->log('Got index vars 4'); + + $splash = ''; + $splashstyles = ''; + $splashImage = $this->themeAsset('splashImage'); + if ($splashImage) { + $this->vdir->copy($splashImage->getPathname(), 'data/images/' . $splashImage->getFilename()); + $splashstyles = 'background-image:url(' . 'data/images/' . $splashImage->getFilename() . ');background-size:contain;background-position:50% 50%;'; + if ($this->fluidbookSettings->splashURL !== '') { + $splash = ''; + } + } else if ($logoLoader = $this->themeAsset('logoLoader')) { + $dim = Image::getimagesize($logoLoader->getPathname()); + if ($dim !== false) { + $this->vdir->copy($logoLoader->getPathname(), 'data/images/' . $logoLoader->getFilename()); + $splash .= ''; + } + } + $svg = $this->_mergeSVG(); + + if ($this->phonegap) { + $csp = "securityPolicyWhitelist)) . "; img-src * data:\">"; + } + $lang = $this->getFluidbook()->lang; + + $console = ''; + if ($this->fluidbookSettings->debugConsole) { + $console = '
+
+ +'; + } + + $this->log('Got index vars 5'); + $vars = array('lang', 'titre', 'credits', 'style', 'script', 'pagesContents', 'print', 'hiddenContents', 'splash', 'splashstyles', 'cache', 'bgcolor', 'message', 'favicon', 'svg', 'beginbody', 'csp', 'opengraph', 'twittercard', 'description', 'console'); + + $res = []; + foreach ($vars as $v) { + if (isset($$v)) { + $res[''] = $$v; + } else { + $res[''] = ''; + } + } + $this->_indexVars = $res; + $this->log('Got index vars 6'); + } + return $this->_indexVars; + } + + protected function _mergeSVG() + { + $symbols = []; + foreach ($this->svgfiles as $svgfile) { + $symbols = array_merge($symbols, $this->_getSVGSymbols($svgfile)); + } + $symbols = array_merge($symbols, $this->_svgSymbols); + return '' . str_replace('> <', '><', Text::removeNewLines(implode('', $symbols))) . ''; + } + + protected function _getSVGSymbols($svg) + { + if (file_exists($svg)) { + $svg = file_get_contents($svg); + } + $svg = str_replace('$bookmark-color', Color::colorToCSS($this->themeSettings->bookmarkBackgroundColor), $svg); + $res = []; + $xml = simplexml_load_string($svg); + if (!$xml) { + return $res; + } + $xml->registerXPathNamespace('svg', 'http://www.w3.org/2000/svg'); + foreach ($xml->xpath('//svg:symbol') as $item) { + $res[(string)$item['id']] = $item->asXML(); + } + + return $res; + } + + protected function writeIndex() + { + $iv = $this->getIndexVars(); + $this->log('Got index vars'); + foreach ($iv as $k => $v) { + $this->seo->html = str_replace($k, $v, $this->seo->html); + } + if ($this->fluidbookSettings->seoVersion) { + foreach ($this->pages as $page => $infos) { + $this->_writeIndex($page); + } + } else { + $this->_writeIndex(1); + } + } + + protected function writeWidget() + { + // Write widget html +// if ($this->widget) { +// $whtml = file_get_contents($this->assets . '/widget.html'); +// $script = ''; +// $script .= ''; +// +// $style = ''; +// $vars = array('titre', 'style', 'script'); +// foreach ($vars as $v) { +// if (isset($$v)) { +// $whtml = str_replace('', $$v, $whtml); +// } else { +// $whtml = str_replace('', '', $whtml); +// } +// } +// $this->vdir->file_put_contents('widget.html', $whtml); +// } + } + + function writeSEO() + { + foreach ($this->seoArticles as $seoArticle) { + if ($this->hybrid) { + $html = file_get_contents($this->assets . '/_seohybrid.html'); + } else { + $html = file_get_contents($this->assets . '/_seo.html'); + } + $a = $seoArticle; + unset($a['image']); + $a['imageurl'] = 'https://workshop.fluidbook.com/services/facebook_thumbnail?cid=' . $this->getFluidbook()->cid . '&j=' . time(); + if ($seoArticle['image']) { + $a['imageurl'] .= '&image=' . $seoArticle['image']; + } + $dim = Image::getimagesize($a['imageurl']); + $a['imagewidth'] = $dim[0]; + $a['imageheight'] = $dim[1]; + foreach ($a as $k => $v) { + $html = str_replace('$' . $k, $v, $html); + } + $this->vdir->file_put_contents('p/' . $seoArticle['url'], $html); + } + $this->seo = new Document($this); + } + + public function addContentLock($page, $unlockConditions = '') + { + $this->config->hasContentLock = true; + $unlockConditions = Text::explodeNewLines($unlockConditions); + $conditions = []; + foreach ($unlockConditions as $unlockCondition) { + $e = explode(',', $unlockCondition); + if (!isset($e[1])) { + $e[1] = 'click'; + } + $conditions[] = $e; + } + $page = max(1, $page); + if (!isset($this->content_lock[$page])) { + $this->content_lock[$page] = ['unlocked' => 0, 'conditions' => []]; + } + $this->content_lock[$page]['conditions'] = array_merge($this->content_lock[$page]['conditions'], $conditions); + } + + protected function writeScorm() + { + $manifestfiles = ['1.2' => '_imsmanifest.12.xml', '2004' => '_imsmanifest.2004.xml', '2004.3' => '_imsmanifest.2004-3.xml']; + $manifestfile = $manifestfiles[(string)$this->fluidbookSettings->scorm_version]; + + $manifest = file_get_contents($this->assets . '/' . $manifestfile); + if (!$this->fluidbookSettings->scorm_title) { + $this->fluidbookSettings->scorm_title = $this->fluidbookSettings->title; + } + if (!$this->fluidbookSettings->scorm_id || ($this->getFluidbook()->book_id > 16614 && $this->fluidbookSettings->scorm_id === 'MFMCTE091mobile')) { + $this->fluidbookSettings->scorm_id = 'fb_' . $this->getFluidbook()->book_id; + } + if (!$this->fluidbookSettings->scorm_org) { + $this->fluidbookSettings->scorm_org = 'Fluidbook'; + } + + $vars = array('scorm_id', 'scorm_org', 'scorm_title'); + foreach ($vars as $v) { + $manifest = str_replace('$' . $v, htmlspecialchars($this->fluidbookSettings->$v, ENT_QUOTES), $manifest); + } + $this->vdir->file_put_contents('imsmanifest.xml', $manifest); + + + $this->config->scorm_variables = $this->fluidbookSettings->scorm_variables = $this->parseVariables($this->fluidbookSettings->scorm_variables); + if ($this->fluidbookSettings->scorm_quizdata) { + $this->config->scorm_quizdata = ExcelToArray::excelToArray($this->wdir . '/' . $this->fluidbookSettings->scorm_quizdata); + } + } + + protected function parseVariables($f) + { + $variables = []; + $f = str_replace("\r", "\n", $f); + $e = Text::explodeNewLines($f); + foreach ($e as $item) { + $item = trim($item); + if ($item == '') { + continue; + } + $f = explode('=', $item, 2); + $variables[trim($f[0])] = trim($f[1]); + } + return $variables; + } + + protected function writePrint() + { + if (!$this->fluidbookSettings->print && !$this->fluidbookSettings->pdf) { + return; + } + + $res = PDF::compilePDF($this->getFluidbook()); + if (!$this->config->pdfName) { + $this->config->pdfName = 'document.pdf'; + } + $this->config->pdfName = Text::removeAccents($this->config->pdfName); + if (mb_strtolower(substr($this->config->pdfName, -4)) !== '.pdf') { + $this->config->pdfName .= '.pdf'; + } + + if ($res !== false) { + $this->vdir->copy($res, 'data/' . $this->config->pdfName); + } + $this->log('Print written'); + return ''; + } + + protected function addFilesInfos($key, $file) + { + if (!file_exists($file)) { + return; + } + if (!isset($this->config->filesInfos)) { + $this->config->filesInfos = array(); + } + $infos = array('filesize' => filesize($file)); + $dim = Image::getimagesize($file); + if ($dim !== false) { + $infos['width'] = $dim[0]; + $infos['height'] = $dim[1]; + } + $this->config->filesInfos[$key] = $infos; + } + + protected function __($str) + { + if (!isset($this->config->l10n)) { + $this->writeLangs(); + } + return $this->config->get('l10n.default.' . $str, $str); + } + + protected function writeLangs() + { + $this->config->defaultLang = $this->getFluidbook()->locale; + $l10n = FluidbookTranslate::getCompiledTranslations(); + $l10n['default'] = $this->getFluidbook()->getDefaultTranslations($l10n); + $this->config->setRaw('l10n', $l10n); + + + $multilang = Text::explodeNewLines($this->config->get('multilang', '')); + if (count($multilang)) { + $m = array(); + foreach ($multilang as $line) { + $line = trim($line); + if ($line == '') { + continue; + } + $l = explode(',', $line); + $locale = $l[0]; + $flag = $l[1]; + $this->getVirtualDirectory()->copy(resource_path('fluidbookpublication/flags/' . $flag . '.png'), 'images/flags/' . $flag . '.png'); + $l[3] = Text::ucfirst(Locale::translate($locale, $locale)); + $l[4] = Country::translate($flag, $locale); + $m[] = implode(',', $l); + } + + $this->config->setRaw('multilang', implode("\n", $m)); + } + } + + protected function writeExtras() + { + //dd($this->themeAsset('afterSearch')); + if ($afterSearch = $this->themeAsset('afterSearch')) { + $this->vdir->copy($afterSearch->getPathname(), 'data/images/' . $afterSearch->getFilename()); + } + if ($this->fluidbookSettings->externalArchives != '') { + $this->addFilesInfos('archives', $this->wdir . '/' . $this->fluidbookSettings->externalArchives); + $this->vdir->copy($this->wdir . '/' . $this->fluidbookSettings->externalArchives, 'data/images/' . $this->fluidbookSettings->externalArchives); + } + + if (stristr($this->fluidbookSettings->navExtraImage, '.')) { + $this->vdir->copy($this->wdir . '/' . $this->fluidbookSettings->navExtraImage, 'data/images/' . $this->fluidbookSettings->navExtraImage); + } + + if (stristr($this->fluidbookSettings->navExtraImageMobile, '.')) { + $this->vdir->copy($this->wdir . '/' . $this->fluidbookSettings->navExtraImageMobile, 'data/images/' . $this->fluidbookSettings->navExtraImageMobile); + } + + for ($i = 1; $i <= 5; $i++) { + $ic = $this->fluidbookSettings->{'navExtraIcon' . $i}; + if ($ic != '') { + if (stristr($ic, '.svg')) { + $e = explode('.', $ic); + $sname = 'external-' . $e[0]; + $this->addSVGSymbolFromFile($this->wdir . '/' . $ic, $sname); + $this->config->{'navExtraIcon' . $i} = $sname; + } else if (stristr($ic, '.')) { + $this->vdir->copy($this->wdir . '/' . $ic, 'data/images/' . $ic); + } + } + } + } + + protected function addSVGSymbolFromFile($svg, $symbolName) + { + $svg = SVGTools::optimizeSVG($svg); + + $xml = simplexml_load_string(file_get_contents($svg)); + $viewBox = (string)$xml['viewBox']; + + $this->_svgSymbols[$symbolName] = '' . $this->SimpleXMLElement_innerXML($xml) . ''; + } + + protected function writeLinks() + { + + switch ($this->fluidbookSettings->customLinkClass) { + case 'WescoSalesLink': + $this->specialJsFiles[] = 'js/libs/interact.min.js'; + $this->specialJsFiles[] = 'js/libs/fluidbook/special/wescosales.js'; + $this->specialCSS[] = 'wescosales'; + break; + case 'AtlanticDownloadLink': + $this->specialJsFiles[] = 'js/libs/fluidbook/special/atlanticdownload.js'; + $this->specialCSS[] = 'atlanticdownload'; + break; + case 'MiraklEaster2021': + $this->specialJsFiles[] = 'js/libs/fluidbook/special/mirakleaster2021.js'; + $this->specialCSS[] = 'mirakleaster2021'; + break; + } + + $this->config->links = array(); + $this->config->clinks = array(); + $this->config->bookmarkGroups = array(); + + $ignore = $this->fluidbookSettings->ignoreLinksTypes; + if (!$ignore) { + $ignore = array(); + } else { + $ignore = explode(',', $ignore); + } + + if ($this->fluidbookSettings->externalChaptersHTML != '') { + $d = $this->unzipFile($this->fluidbookSettings->externalChaptersHTML, false, 'data/chapters/'); + $meta = $this->getConfigZIP($d['dir']); + $this->config->externalChaptersSize = new \stdClass(); + $this->config->externalChaptersSize->width = $meta['width']; + $this->config->externalChaptersSize->height = $meta['height']; + $this->vdir->copyDirectory($d['dir'], $d['fdir']); + } + + $this->getLinksAndRulers($links, $rulers); + $this->_fluidbook->normalizeLinks($links); + + if ($this->fluidbookSettings->basketManager === 'Puma') { + foreach ($links as $k => $init) { + if ($init['type'] == 12 && isset($this->config->product_zoom_references[$init['to']]) && count($this->config->product_zoom_references[$init['to']]) > 0 && implode('', $this->config->product_zoom_references[$init['to']]) != '') { + $init['infobulle'] = 'Digital information'; + $init['animation'] = 'reflet-anim.html'; + $links[$k] = $init; + } + if ($init['type'] == 7) { + $init['image'] = ''; + $init['display_area'] = false; + $links[$k] = $init; + } + } + + } + + // Custom landing page content + if ($this->fluidbookSettings->landingPage != '') { + $d = $this->unzipFile($this->fluidbookSettings->landingPage, false, 'data/landing-page/'); + $this->vdir->copyDirectory($d['dir'], $d['fdir']); + } + + if ($this->fluidbookSettings->tabsHTML5 != '' && file_exists($this->wdir . '/' . $this->fluidbookSettings->tabsHTML5)) { + $ext = Files::getExtension($this->fluidbookSettings->tabsHTML5); + if ($ext === 'zip') { + $links['tabs'] = [ + 'page' => 'background', + 'top' => 0, + 'left' => 0, + 'width' => 100, + 'height' => 100, + 'type' => 6, + 'to' => $this->fluidbookSettings->tabsHTML5, + 'image' => '', + 'inline' => 1, + 'interactive' => 1, + 'class' => 'tabslink', + 'uid' => 'tabs', + ]; + } else if ($ext === 'svg') { + $this->vdir->copy($this->wdir . '/' . $this->fluidbookSettings->tabsHTML5, 'data/tabs.svg'); + $this->config->svgTabs = true; + $pagesLists = ['tabsPages', 'tabsSections']; + + foreach ($pagesLists as $pagesList) { + $e = explode(',', $this->fluidbookSettings->$pagesList); + $list = []; + foreach ($e as $k => $v) { + $v = trim($v); + if ($v === '') { + continue; + } + if ($v !== '-') { + if ($this->fluidbookSettings->tabsPagesNumbers === 'virtual') { + $v = $this->virtualToPhysical($v); + } + } + $list[] = $v; + } + $this->config->$pagesList = $list; + } + } + } + + $pagesOfCustomLinks = []; + $hiddenLinks = []; + $anchorExists = []; + $closedLinks = []; + + $linksCopy = $links; + + + foreach ($linksCopy as $k => $linkData) { + if ($this->fluidbookSettings->PDFRendererIframe === 'svg' && (($linkData['type'] == 31 && stristr($linkData['to'], '.pdf')) || ($linkData['type'] == 6 && stristr($linkData['alternative'], '.pdf')))) { + $ofile = $this->wdir . '/' . $linkData['to']; + $dfile = $this->wdir . '/' . $linkData['to'] . '.svg'; + if (!file_exists($dfile) || filemtime($dfile) < filemtime($ofile)) { + $cmd = "pdftocairo -svg -f 1 -l 1 $ofile $dfile"; + `$cmd`; + } + $linkData['type'] = 6; + $linkData['to'] = $linkData['to'] . '.svg'; + $linkData['backgroundColor'] = '#fff'; + $links[$k] = $linkData; + } + if ($linkData['type'] == 26 || $linkData == 40) { + $linkData['to'] = AnchorLink::normalizeAnchor($linkData['to']); + $anchorExists[$linkData['to']] = $linkData; + } + if ($linkData['type'] == 35 || $linkData['type'] == 15 || $linkData['type'] == 39) { + $linkData = Link::decryptLink($linkData); + $animations = ContentLink::parseAnimations($linkData['image_rollover']); + foreach ($animations as $animation) { + if (isset($animation['backgroundcolor']) && $animation['backgroundcolor'] !== 'transparent') { + $dupData = $linkData; + $dupData['type'] = 14; + $dupData['to'] = $animation['backgroundcolor']; + + $dupData['uid'] = 'b_' . $linkData['uid']; + $dupData['addzindex'] = -1; + $dupData['image_rollover'] = ''; + array_push($links, $dupData); + array_push($links, $linkData); + unset($links[$k]); + } + } + } + if (isset($linkData['image']) && $linkData['image'] && $linkData['type'] != 28 && $linkData['type'] != 35) { + $dupData = $linkData; + $dupData['image'] = ''; + $dupData['animation'] = ''; + $dupData['to'] = self::_SVGCleanAsset($linkData['image']); + if ($dupData['image_rollover'] != 'none' && !stristr($dupData['image_rollover'], '=')) { + $dupData['rollover'] = $dupData['image_rollover']; + } + $dupData['image_rollover'] = ''; + $dupData['type'] = 15; + $dupData['uid'] = 'i_' . $linkData['uid']; + if (Link::isScorm($linkData)) { + $dupData['scorm'] = true; + } + array_push($links, $dupData); + } + if (isset($linkData['animation']) && $linkData['animation']) { + $dupData = $linkData; + $dupData['image'] = ''; + $dupData['animation'] = ''; + $dupData['inline'] = true; + $dupData['interactive'] = false; + $dupData['to'] = $linkData['animation']; + $dupData['type'] = 6; + $linkData['relatedAnimation'] = $dupData['uid'] = 'a_' . $linkData['uid']; + $dupData['video_width'] = $dupData['video_height'] = 0; + if (Link::isScorm($linkData)) { + $dupData['scorm'] = true; + } + $links[$k] = $linkData; + array_push($links, $dupData); + } + if ($linkData['type'] == 7) { + $k = $linkData['to']; + $e = explode(':', $k); + if (count($e) > 1) { + $k = $e[1]; + } + if (!isset($pagesOfCustomLinks[$k])) { + $pagesOfCustomLinks[$k] = []; + } + if (!in_array($linkData['page'], $pagesOfCustomLinks[$k])) { + $pagesOfCustomLinks[$k][] = $linkData['page']; + } + } + + if ($linkData['type'] == 32 && $linkData['target'] !== 'hide') { + $ids = explode(',', $linkData['to']); + $close = ($linkData['video_service'] && $linkData['video_service'] !== 'none'); + foreach ($ids as $id) { + $id = trim($id); + if ($id === 'tabs') { + $this->config->tabsHiddenAtStartup = true; + } else { + if ($close) { + $closedLinks[] = $id; + $closedLinks[] = 'i_' . $id; + } + $hiddenLinks[] = $id; + $hiddenLinks[] = 'i_' . $id; + } + } + } + } + + if ($this->fluidbookSettings->anchorsAliases && file_exists($this->fluidbookSettings->anchorsAliases)) { + $aliases = []; + $anchors = []; + for ($i = 0; $i <= 2; $i++) { + $lines = Text::explodeNewLines(file_get_contents($this->fluidbookSettings->anchorsAliases)); + foreach ($lines as $line) { + $e = explode("\t", $line); + $from = AnchorLink::normalizeAnchor($e[0]); + $to = AnchorLink::normalizeAnchor($e[1]); + $aliases[$from] = $to; + if (is_numeric($to) && !isset($anchorExists[$from])) { + $anchor = [ + 'page' => $to, + 'top' => 0, + 'left' => 0, + 'width' => 100, + 'height' => 100, + 'type' => 26, + 'to' => $from, + 'uid' => Links::generateUID() + ]; + $anchorExists[$from] = $anchor; + $links[] = $anchor; + } else { + if (!isset($anchorExists[$from]) && isset($anchorExists[$to])) { + $anchor = $anchorExists[$to]; + $anchor['to'] = $from; + $anchor['uid'] = Links::generateUID(); + $anchorExists[$from] = $anchor; + $links[] = $anchor; + } + } + } + } + } + + + $this->config->pagesOfCustomLinks = $pagesOfCustomLinks; + + $i = 1; + $pages = array(); + $cpages = array(); + $ctpages = array(); + $css = array(); + $linkPages = []; + $allLinksData = []; + $gamifyCoins = []; + + usort($links, array($this, '_sortLinks')); + + foreach ($links as $linkData) { + if (in_array($linkData['type'], $ignore)) { + continue; + } + if ($linkData['uid'] === 'slider') { + $linkData['page'] = 'background'; + } + + $linkData['hidden'] = in_array($linkData['uid'], $hiddenLinks); + if (isset($linkData['zindex']) && $linkData['zindex'] < 50 && in_array($linkData['uid'], $closedLinks)) { + $linkData['zindex'] = 50; + } + if ($linkData['type'] == 28) { + $this->addSEOArticle('#/page/' . $linkData['page'], $linkData['to'], $linkData['extra'], $linkData['image']); + continue; + } + + $link = Link::getInstance($this->base62($i), $linkData, $this); + if (is_null($link) || $link->ignore()) { + continue; + } + + + + $linksToAdd = [$link]; + if ($link->overlapDoublePage() && !$this->isOnePage()) { + $linksToAdd[] = $link->getRightClone(); + } + + + + foreach ($linksToAdd as $lta) { + try { + /** @var $lta Link */ + // Keep this line because some properties of the link (like blend mode) are parsed with this function + $c = $lta->getHTMLContainer(); + + + $css[] = $lta->getCSSContainer(); + if (!isset($pages[$lta->page])) { + $pages[$lta->page] = ['normal' => []]; + $cpages[$lta->page] = ['normal' => []]; + $ctpages[$lta->page] = ['normal' => []]; + } + + + $d = $lta->getDepth(); + if ($d < 30) { + $v = 'ctpages'; + } else if ($d < 50) { + $v = 'cpages'; + } else { + $v = 'pages'; + } + + $lta->setInitialOrder($i); + if (!isset($$v[$lta->page][$lta->blendmode])) { + $$v[$lta->page][$lta->blendmode] = []; + } + + array_push($$v[$lta->page][$lta->blendmode], $lta); + $i++; + } catch (\Exception $e) { + $this->triggerLinkError($e, $lta); + } + } + + + // Make old "aftersearch" link compatible with new "extra" menu option by extracting link URL + if ($link->page == 'aftersearch') { + $this->config->afterSearchLink = $link->to; + $this->config->afterSearchTooltip = $link->infobulle; + } + + if (strpos($link->page, 'link_') === 0) { + $linkPages[$link->page] = true; + } + + if ($link->gamifyCoins) { + $gamifyCoins[$linkData['uid']] = $link->gamifyCoins; + } + + $allLinksData[$linkData['uid']] = $linkData; + + if ($link->keep()) { + $this->hiddenContents[] = $link->getHTMLContainer(); + } + } + + $allpages = range(0, $this->getFluidbook()->getPagesNumber() + 1); + if ($this->fluidbookSettings->themeEnableAfterSearch) { + $allpages[] = 'aftersearch'; + } + $allpages[] = 'background'; + $allpages[] = 'archives'; + $allpages[] = 'slider'; + foreach ($linkPages as $linkPage => $true) { + $allpages[] = $linkPage; + } + + foreach ($allpages as $i) { + $this->config->set('links.' . $i, $this->_htmlLinkList($pages[$i] ?? [])); + $this->config->set('clinks.' . $i, $this->_htmlLinkList($cpages[$i] ?? [])); + $this->config->set('ctlinks.' . $i, $this->_htmlLinkList($ctpages[$i] ?? [])); + } + + if ($this->writeLinksData) { + $this->config->linksData = $allLinksData; + } + $this->config->gamifyCoins = $gamifyCoins; + + return $css; + } + + /** + * @return bool + */ + public function isOnePage(): bool + { + return $this->_fluidbook->isOnePage(); + } + + /** + * @param $e \Exception + * @param $link Link + * @return void + * @throws \Exception + */ + protected function triggerLinkError($e, $link) + { + Log::error($e); + + /*$c = explode('\\', get_class($link)); + $type = array_pop($c); + $type = str_replace('Link', '', $type); + + throw new \Exception('Error on the ' . $type . ' link to ' . $link->to . ' on page ' . $link->page . ' with message : ' . $e->getMessage());*/ + } + + protected function _htmlLinkList($list) + { + if (!count($list)) { + return []; + } + $res = []; + foreach ($list as $blendmode => $l) { + usort($l, [$this, '_sortLinksByDepth']); + $res[$blendmode] = []; + foreach ($l as $item) { + $res[$blendmode][] = $item->getHTMLContainer(); + } + + } + return $res; + + } + + public function getBookSurface() + { + return $this->width * $this->height; + } + + protected function _sortLinksByDepth($a, $b) + { + $c = $a->getDepth() - $b->getDepth(); + if ($c === 0) { + $c = $b->getSurface() - $a->getSurface(); + } + if ($c === 0) { + $c = $b->getInitialOrder() - $a->getInitialOrder(); + } + + return $c; + } + + public function addSlideshowLibrary($inline = true) + { + $l = ($inline ? $this->config->inlineSlideshowLibrary : $this->config->popupSlideshowLibrary); + if ($l === 'splide') { + $this->addJsLib('splide', 'js/libs/splide/splide.js'); + } + + $this->addJsLib('slideshow', ['js/libs/fluidbook/slideshow/fluidbook.slideshow.js', + 'js/libs/fluidbook/slideshow/fluidbook.slideshow.' . $l . '.js']); + $this->addLess('slideshow/' . $l); + } + + public function addSEOArticle($page, $title, $intro, $image, $id = null, $url = null, $content = '') + { + if (null === $url) { + $url = Text::str2URL($title) . '.html'; + } + if (null === $id) { + $id = $title; + } + + $this->seoArticles[$id] = ['title' => $title, 'description' => $intro, 'image' => $image, 'content' => $content, 'page' => $page, 'url' => $url, 'id' => $id]; + } + + public function _sortLinks($a, $b) + { + + $priorities = array(26 => -1, 35 => 1); + + $pa = isset($priorities[$a['type']]) ? -$priorities[$a['type']] : 0; + $pb = isset($priorities[$b['type']]) ? -$priorities[$b['type']] : 0; + return $pb - $pa; + } + + public function addBookmarkGroup($link) + { + if ($link['left'] > $this->fluidbookSettings->width) { + //$link['page']++; + } + if ($link['page'] <= 0 || $link['page'] > $this->fluidbookSettings->pages) { + return; + } + + $this->config->bookmarkGroups[] = array('page' => ($link['page']), 'nb' => $link['to'], 'name' => $link['extra']); + } + + public function addTriggersLink($page, $link, $delay = 0) + { + $this->config->push('triggersLinks', ['page' => $page, 'link' => $link, 'delay' => $delay]); + } + + public function addAudiodescription($link) + { + + $e = explode('.', $link['to']); + $ext = mb_strtolower(array_pop($e)); + if ($ext === 'txt') { + $file = $this->wdir . '/' . $link['to']; + if (file_exists($file)) { + $this->audioDescriptionTextsList[$link['page']] = ['text' => file_get_contents($file)]; + } + } else { + $this->config->set('audiodescription.' . $link['page'], $link['to']); + $this->copyLinkFile($link['to'], 'data/audiodescription/'); + } + } + + protected function beforeWriteConfig() + { + // Dynamic background + $dbc = []; + $p = $this->parseVariables($this->fluidbookSettings->dynamicBackgroundColor); + foreach ($p as $range => $color) { + $e = explode(',', $color); + $pages = ArrayUtil::parseRange($range); + foreach ($pages as $page) { + $dbc[$page] = $e; + } + } + $this->config->dynamicBackgroundColor = $dbc; + if ($this->fluidbookSettings->textsThickness > 1) { + if ($this->fluidbookSettings->textsThicknessPages == '') { + $this->config->textsThicknessPages = range(1, $this->fluidbookSettings->pages); + } else { + $this->config->textsThicknessPages = ArrayUtil::parseRange($this->fluidbookSettings->textsThicknessPages); + } + } else { + $this->config->textsThickness = 1; + $this->config->textsThicknessPages = []; + } + + // Content locks + uasort($this->content_lock, function ($a, $b) { + return $a['page'] - $b['page']; + }); + + // Gamify + $p = $this->parseVariables($this->fluidbookSettings->gamify_coins_pages); + foreach ($p as $range => $coins) { + $pages = ArrayUtil::parseRange($range); + foreach ($pages as $page) { + $this->config->gamifyCoins['visit_page_' . $page] = (int)$coins; + } + } + + $this->config->content_lock = $this->content_lock; + } + + public function addPDFJS($force = false) + { + if ($this->_addedPDFJS) { + return; + } + + if (stripos($this->fluidbookSettings->PDFRenderer, 'pdfjs') !== false) { + $renderer = $this->fluidbookSettings->PDFRenderer; + } else if ($force) { + $renderer = 'pdfjs-legacy'; + } else { + return; + } + + $this->_addedPDFJS = true; + + + if ($renderer === 'pdfjs') { + $resource = resource_path('pdfjs/dist-min'); + } else if ($renderer === 'pdfjs-legacy') { + $resource = resource_path('pdfjs/legacy-min'); + } + $this->vdir->copyDirectory($resource, 'pdfjs'); + $this->vdir->copy($this->assets . '/js/libs/pdfjs/custom.js', 'pdfjs/web/custom.js'); + + + $css = '.seamless #sidebarContainer, .seamless .toolbar {display:none !important;}'; + $css .= '.seamless .pdfViewer{padding:0 !important;}'; + $css .= '.seamless #viewerContainer{top:0 !important;overflow:visible !important;}'; + $css .= '.seamless{--page-border:0;--page-margin:0;--body-bg-color:transparent;}'; + $css .= '.openFile,.rotateCw,.rotateCcw,.rotateCcw + .horizontalToolbarSeparator{display:none !important;}' . $this->fluidbookSettings->PDFJSCSS; + $this->vdir->file_put_contents('pdfjs/web/viewer.css', file_get_contents($resource . '/web/viewer.css') . $css); + } + + protected function writeJs() + { + $this->beforeWriteConfig(); + + $config = $this->writeConfig(); + $this->vdir->file_put_contents('data/datas.js', $config); + + $finals = $this->jsLibs; + $this->addPDFJS(); + + if ($this->fluidbookSettings->scorm_enable) { + $finals['scorm'] = array(); + $finals['scorm'][] = 'js/libs/scorm/apiwrapper.js'; + $finals['scorm'][] = 'js/libs/scorm/scorm.js'; + } + if (count($this->specialJsFiles)) { + $finals['special'] = $this->specialJsFiles; + } + if ($this->widget) { + $finals['widget'] = $this->widgetJsFiles; + } + + $dirminimized = Files::mkdir($this->compiledAssets . '/js/min'); + + foreach ($finals as $jsfinal => $files) { + $mintime = 0; + $hash = hash('sha256', json_encode($files)); + $minimized = $dirminimized . $jsfinal . '-' . $hash . '-min.js'; + if (file_exists($minimized) && filesize($minimized) > 0) { + $mintime = filemtime($minimized); + $reminimize = false; + } else { + $mintime = 0; + $reminimize = true; + } + + if (!$reminimize) { + foreach ($files as $file) { + $f = $this->assets . '/' . $file; + if (file_exists($f) && filemtime($f) > $mintime) { + $reminimize = true; + break; + } + } + } + + if (!$reminimize) { + if (filemtime(__FILE__) > $mintime || (file_exists(__DIR__ . '/class.ws.html5.links.php') && filemtime(__DIR__ . '/class.ws.html5.links.php') > $mintime)) { + $reminimize = true; + } + } + + if ($reminimize) { + $js = ''; + $hasNonMin = false; + foreach ($files as $file) { + $f = $this->assets . '/' . $file; + if (!file_exists($f)) { + continue; + } + if (strpos($f, '.min.') === false) { + $hasNonMin = true; + } + $js .= file_get_contents($f); + $js .= ";\n\n"; + } + $tmp = Files::tempnam(); + file_put_contents($tmp, $js); + + if (file_exists($minimized)) { + unlink($minimized); + } + + if (file_exists($tmp) && filesize($tmp) > 0) { + if ($hasNonMin) { + $uglify = new CommandLine('uglifyjs'); + $uglify->setArg('o', $minimized); + $uglify->setArg(null, $tmp); + $uglify->execute(); + } else { + $uglify = null; + copy($tmp, $minimized); + } + + if (!file_exists($minimized) || filesize($minimized) == 0) { + die('An error occured while uglifying ' . $hasNonMin . '? ' . $minimized . ': ' . ($uglify ? $uglify->commande : '') . ' :: ' . ($uglify ? $uglify->output : '') . '(' . implode(',', $files) . ')'); + } + } + } + $dest = 'data/' . $jsfinal . '.js'; + $this->vdir->copy($minimized, $dest); + } + + + if ($this->phonegap) { + // $this->vdir->copy(WS_COMPILE_ASSETS . '/_html5/js/libs/phonegap/' . $this->phonegapVersion . '/cordova-' . $this->phonegap . '.js', 'data/cordova.js'); + } + $this->vdir->copyDirectory($this->assets . '/js/libs/fluidbook/workers', 'js/libs/fluidbook/workers'); + $this->vdir->copyDirectory($this->assets . '/js/libs/stand', 'js/libs/stand'); + $this->vdir->copyDirectory($this->assets . '/js/libs/polyfills', 'js/libs/polyfills'); + } + + public function getCacheDir($path): string + { + return Files::mkdir(protected_path('fluidbookpublication/cache/' . $path)); + } + + public function writeTexts() + { + $cache = sha1($this->fluidbookSettings->highlightResults . '/--/' . $this->fluidbookSettings->searchWordSelectionAlgorithm . '///' . $this->fluidbookSettings->textExtraction . '|--|' . $this->fluidbookSettings->ignoreSearchSeparators . '|||' . $this->getFluidbook()->getCompositionUpdate() . '()()()' . FWSTK::lastUpdate()); + $cacheDir = Files::mkdir(protected_path('fluidbookpublication/index/' . $this->book_id . '/' . $cache . '/')); + $indexFile = $cacheDir . '/search.index.js'; + $textFile = $cacheDir . '/search.texts.js'; + $hightlightsFile = $cacheDir . '/search.highlight.js'; + + if (!file_exists($indexFile) || !file_exists($textFile)) { + SearchIndex::makeTextsIndexes($this, $cacheDir, $index, $texts, true); + file_put_contents($indexFile, 'var INDEX=' . $index . ';' . "\r"); + file_put_contents($textFile, 'var TEXTS=' . $texts . ";\r"); + } + if ($this->fluidbookSettings->highlightResults && !file_exists($hightlightsFile)) { + file_put_contents($hightlightsFile, 'var HIGHLIGHTS=' . json_encode(SearchIndex::makeHighlightIndex($this)) . ";\r"); + } + $this->vdir->copy($cacheDir . '/search.index.js', 'data/search.index.js'); + if ($this->fluidbookSettings->highlightResults) { + $this->vdir->copy($cacheDir . '/search.highlight.js', 'data/search.highlight.js'); + } + if ($this->fluidbookSettings->searchWordSelectionAlgorithm == 'expression') { + $this->vdir->copy($cacheDir . '/search.texts.js', 'data/search.texts.js'); + } + } + + protected function _SVGCleanAsset($a) + { + if (!stristr($a, '.svg')) { + return $a; + } + $clean = str_replace('.svg', '.o.svg', $a); + $path = $this->wdir . '/' . $a; + $opt = $this->wdir . '/' . $clean; + + if (!file_exists($opt) || !filesize($opt) || filemtime($path) > filemtime($opt)) { + if (file_exists($opt) && is_link($opt)) { + unlink($opt); + } + SVGTools::_optimizeSVG($path, $opt); + } + return $clean; + + } + + public function supportSVG() + { + if (!$this->phonegap) { + return false; + } else if ($this->phonegap == 'ios') { + return true; + } else { + return false; + } + } + + protected function writeConfig() + { + $data = $this->config->getRawData(); + foreach ($data as $k => $v) { + if (null === $v) { + $v = $data[$k] = ''; + } + if ($this->getFluidbook()->getField($k) instanceof Checkbox) { + $v = $data[$k] = !!$v; + } + } + return 'var SETTINGS=' . json_encode($data) . ';' . "\n"; + } + + protected function writeCountries() + { + $c = Country::getList($this->getFluidbook()->locale); + asort($c); + $this->config->countries = $c; + } + + protected function writeManifest() + { + $res = array(); + // TODO: Why was this function missing a return statement? It's called from populateConfig() is expected to return a value. + return $res; + } + + protected function writeImages() + { + switch ($this->fluidbookSettings->mobileVersion) { + case 'html5-desktop': + $this->backgroundsPrefix = array(true, false); + $this->svg = true; + break; + case 'html5-images': + $this->backgroundsPrefix = array(true); + $this->svg = false; + break; + default: + $this->backgroundsPrefix = array(false); + $this->svg = true; + break; + } + + $rasterizePages = $this->config->rasterizePages; + $this->config->pagesDimensions = []; + + if ($this->fluidbookSettings->mobileNavigationType === 'mobilefirst') { + $imdir = 'mf'; + } else { + $imdir = 'html'; + } + + $thumbs = array(); + foreach ($this->pages as $page => $infos) { + $thisrasterize = in_array($page, $rasterizePages); + $thisimagesvg = !$thisrasterize && $this->svg; + $thisbackgroundPrefix = $thisrasterize ? [true] : $this->backgroundsPrefix; + + foreach ($this->getResolutions() as $r) { + foreach ($thisbackgroundPrefix as $backgroundsPrefix) { + $source = $this->getFluidbook()->getFile($page, $this->imageFormat, $r, $backgroundsPrefix, true, $imdir); + if ($r === $this->maxRes) { + $this->getPageDimension($page); + } + $this->vdir->copy($source, 'data/background/' . $r . '/' . ($backgroundsPrefix ? 't' : 'p') . $page . '.' . $this->imageFormat); + } + } + + if ($thisimagesvg) { + $this->vdir->copy( + $this->getFluidbook()->getFile($page, 'svg', 150, true, + in_array($page, $this->config->vectorPages), 'html') + , 'data/contents/p' . $page . '.svg'); + } + + $t = $this->getFluidbook()->getThumbFile($page, $this->imageFormat); + $this->vdir->copy($t, 'data/thumbnails/p' . $page . '.' . $this->imageFormat); + $this->log('Made image page ' . $page); + } + + + $this->_makeCover($this->getFluidbook()->getFile(1, 'jpg', 150, true, true)); + $this->log('Made cover for apps'); + + $this->log('Made images'); + } + + + /** + * @param $page + * @return array + */ + protected function getPageDimension($page) + { + $k = 'pagesDimensions.' . $page; + if (!$this->config->has($k)) { + $d = $this->getFluidbook()->getDocumentSize($page); + $res = [round($this->cssWidth, 2), round($d[1] * ($this->cssWidth / $d[0]), 2)]; + $this->config->set($k, $res); + return $res; + } + return $this->config->get($k); + } + + public function getWidth() + { + return $this->getPageDimension(1)[0]; + } + + public function getHeight() + { + return $this->getPageDimension(1)[1]; + } + + + protected function _makeCover($orig) + { + $cached = $this->wdir . '/_cover.jpg'; + + if (!file_exists($cached) || filemtime($cached) < filemtime($orig)) { + $size = Image::getimagesize($orig); + $w = $size[0]; + $h = $size[1]; + + $tmp = Files::tempnam() . '.png'; + + $c = new CommandLine('convert'); + $c->setArg(null, resource_path('fluidbookpublication/cover/shade-cover-app.png')); + $c->setManualArg('-resize ' . round($w / 3) . 'x' . $h); + $c->setArg(null, $tmp); + $c->execute(); + + $convert = new CommandLine('composite'); + $cmd = '-compose Multiply '; + $cmd .= $tmp . ' ' . $orig . ' '; + $cmd .= $cached; + $convert->setManualArg($cmd); + $convert->execute(); + + unlink($tmp); + } + $this->vdir->copy($cached, 'cover.jpg', true); + + } + + protected function _lessBoolean($val) + { + return $this->_themeBoolean($val) ? 'true' : 'false'; + } + + protected function _font($f) + { + $default = 'Arial, Helvetica, sans-serif'; + if ($f === 'OpenSans') { + $f = 'Open Sans'; + } + switch ($f) { + case 'Montserrat': + case 'Open Sans': + $this->addFontKit($f); + return "'" . $f . "', " . $default; + case 'sans-serif': + return $f; + case 'Arial': + return $default; + default: + return "'Open Sans', Arial, Helverica, sans-serif"; + } + } + + protected function _themeBoolean($v) + { + return !(null === $v || $v === '0' || $v === 0 || $v === false || !$v); + } + + protected function writeCSS($links) + { + $res = array(); + + $this->addFontKit('OpenSans'); + + $lessContents = ''; + + $this->lessVariables['font'] = $this->_font($this->themeSettings->interfaceFont); + $this->lessVariables['text-transform'] = $this->_themeBoolean($this->themeSettings->interfaceFontUppercase) ? 'uppercase' : 'inherit'; + + $this->lessVariables['css-scale'] = $this->cssScale; + + $this->lessVariables['slider-background'] = Color::colorToCSS(!$this->themeSettings->sliderBackground ? 'rgba(0,0,0,0.1)' : $this->themeSettings->sliderBackground); + $this->lessVariables['slider-handle'] = Color::colorToCSS(!$this->themeSettings->sliderHandle ? '#ffffff' : $this->themeSettings->sliderHandle); + $this->lessVariables['slider-display'] = $this->_lessBoolean($this->themeSettings->pagesBar); + $this->lessVariables['slider-thumb-background'] = Color::colorToCSS($this->themeSettings->pageBarThumbBack); + $this->lessVariables['pages-background'] = $this->fluidbookSettings->forceWhiteBackground ? '#ffffff' : 'transparent'; + + $this->log('CSS 1'); + + // General theme + $cssWidth = $this->cssWidth; + $cssHeight = $this->cssHeight; + $cssScale = $this->cssScale; + $w2 = ($cssWidth * 2) . 'px'; + $h = $cssHeight . 'px'; + + $wm = ($this->width * $this->multiply) . 'px'; + $hm = ($this->height * $this->multiply) . 'px'; + $w = $cssWidth . 'px'; + $offsetLeft = round(($this->optimalWidth - $cssWidth) / 2, 3); + $offsetLeft2 = $offsetLeft * 2; + $offsetTop = round(($this->optimalHeight - $cssHeight) / 2, 3); + $navTop = ($cssHeight - 40 - 100) / 2; + $leftOfRightPage = (floor($cssWidth) - 1) . 'px'; + + $this->lessVariables['z'] = $this->z; + $this->lessVariables['book-page-width'] = $w; + + + $this->lessVariables['book-page-correct-width'] = $w; + $this->lessVariables['book-page-correct-height'] = $h; + + + $this->log('CSS 2'); + $this->lessVariables['book-page-height'] = $h; + $this->lessVariables['book-page-ratio'] = floatval($w) / floatval($h); + + $this->lessVariables['page-shade-opacity'] = min(1, $this->themeSettings->shadeAlpha / 50); + $c = new Color($this->themeSettings->bookShadeColor); + $this->lessVariables['shadow-opacity'] = $c->getAlpha() * 1.2; + $this->lessVariables['edges-display'] = $this->_lessBoolean($this->themeSettings->usePageEdges); + $this->lessVariables['edge-left-offset'] = 0; + $this->lessVariables['edge-right-offset'] = 0; + $this->lessVariables['edges-opacity'] = 1; + + + $this->lessVariables['audioplayer-background-color'] = Color::colorToCSS($this->themeSettings->audioplayerBackgroundColor ?: $this->themeSettings->couleurL); + $this->lessVariables['audioplayer-icon-color'] = Color::colorToCSS($this->themeSettings->audioplayerIconColor); + $this->config->audioplayerStrokeColor = $this->lessVariables['audioplayer-stroke-color'] = Color::colorToCSS($this->themeSettings->audioplayerStrokeColor ?: $this->themeSettings->couleurL); + + $this->lessVariables['page-number-color'] = Color::colorToCSS($this->themeSettings->colorPageNumber); + $this->lessVariables['display-page-number'] = $this->_lessBoolean($this->themeSettings->displayPageNumber); + $this->lessVariables['page-transition-duration'] = $this->fluidbookSettings->mobileTransitionDuration . 's'; + + $this->config->mobileTransitionDurationSlide = ($this->fluidbookSettings->mobileTransitionDurationSlide ?: $this->fluidbookSettings->mobileTransitionDuration); + $this->lessVariables['page-transition-slide-duration'] = $this->config->mobileTransitionDurationSlide . 's'; + + $corrText = $this->isMobileFirst() ? 0 : 4; + $this->log('CSS 3'); + + // Theme + $shade = '.page .shade{'; + $shade .= 'opacity:' . min(($this->themeSettings->shadeAlpha * 2) / 100, 1) . ';'; + $shade .= '}'; + $res[] = $shade; + + // SVG + $res[] = 'svg .fill-c-menu-back{fill:' . Color::colorToCSS($this->themeSettings->couleurB) . ';}'; + $res[] = 'svg .fill-c-menu-text{fill:' . Color::colorToCSS($this->themeSettings->subTextColor) . ';}'; + + // Background + $res[] = $this->_cssBackground(); + $this->log('CSS 4'); + // Archives + // Header + $header = 'header{'; + $header .= 'height:' . $this->themeSettings->menuHeight . 'px;'; + if ($mi = $this->themeAsset('menuImage')) { + $this->vdir->copy($mi->getPathname(), 'data/images/' . $mi->getFilename()); + $header .= 'background-image:url(../images/' . $mi->getFilename() . ');'; + $header .= 'background-repeat:no-repeat;'; + $header .= 'background-size:100% ' . $this->themeSettings->menuHeight . 'px;'; + } else { + // Force redo + $header .= 'background-color:' . Color::colorToCSS($this->themeSettings->menuColor) . ';'; + } + $header .= '}'; + $res[] = $header; + $this->log('CSS 5'); + // Logo + $logo = '#logo{'; + if ($l = $this->themeAsset('logo')) { + $this->vdir->copy($l->getPathname(), 'data/images/' . $l->getFilename()); + $dim = Image::getimagesize($l->getPathname()); + $logo .= 'background-image:url(../images/' . $l->getFilename() . ');width:' . $dim[0] . 'px;height:' . $dim[1] . 'px;'; + } + $logo .= '}'; + $res[] = $logo; + + // Credits + $res[] = 'footer,footer a{color:' . Color::colorToCSS($this->themeSettings->creditsColor) . ';}'; + $this->log('CSS 6'); + // Arrows + $this->lessVariables['arrows-background'] = Color::colorToCSS($this->themeSettings->couleurA); + $this->lessVariables['arrows-color'] = Color::colorToCSS($this->themeSettings->arrowsColor); + + // Loader + $this->lessVariables['loader-background-color'] = Color::colorToCSS($this->themeSettings->couleurL); + $this->lessVariables['loader-foreground-color'] = Color::colorToCSS($this->themeSettings->loadingSecColor); + + // Audio description buttons + $this->lessVariables['audiodescription-background'] = Color::colorToCSS($this->themeSettings->couleurA); + $this->lessVariables['audiodescription-color'] = Color::colorToCSS($this->themeSettings->couleurA); + $this->log('CSS 7'); + // Links Styles + $this->lessVariables['links-color'] = Color::colorToCSS($this->themeSettings->linksColor); + $this->lessVariables['slideshow-color'] = Color::colorToCSS($this->themeSettings->slideshowColor ?: $this->themeSettings->couleurB); + $this->lessVariables['inlineslideshow-transition-time'] = (floatval($this->fluidbookSettings->inlineSlideshowTransitionDuration) * 1000) . 'ms'; + $this->lessVariables['slideshow-caption-size'] = $this->fluidbookSettings->slideshowCaptionSize ?: '16px'; + + $res = array_merge($res, $links); + + // Bookmarks + if (!isset($this->fluidbookSettings->bookmarkCornerSize)) { + $this->fluidbookSettings->bookmarkCornerSize = 10; + } + $this->log('CSS 8'); + $this->lessVariables['bookmark-star-disabled-color'] = Color::colorToCSS($this->themeSettings->bookmarkStarDisabledColor); + $this->lessVariables['bookmark-star-enabled-color'] = Color::colorToCSS($this->themeSettings->bookmarkStarEnabledColor); + $this->lessVariables['bookmark-color'] = Color::colorToCSS($this->themeSettings->bookmarkBackgroundColor); + $this->lessVariables['bookmark-corner-size'] = round($this->width * $this->fluidbookSettings->bookmarkCornerSize * 0.0075 * $this->z) . 'px'; + $this->lessVariables['bookmark-corner-offset'] = $this->fluidbookSettings->bookmarkOffset . 'px'; + + // Menus + $menuColor = new Color($this->themeSettings->couleurB); + $menuColor->setAlpha(1); + $menuTextColor = Color::colorToCSS($this->themeSettings->subTextColor); + $menuBreakpoint = empty($this->fluidbookSettings->menuBreakpoint) ? '1023px' : $this->fluidbookSettings->menuBreakpoint; + + $this->lessVariables['menu-breakpoint'] = $menuBreakpoint; + $this->lessVariables['menu-background'] = $menuColor->toCSS(); + if ($this->themeSettings->subSecondaryColor) { + $this->lessVariables['menu-button-background'] = Color::colorToCSS($this->themeSettings->subSecondaryColor); + } else { + $this->lessVariables['menu-background-green'] = 'max(45, min(255-45, green(@menu-background)))'; + $this->lessVariables['menu-background-red'] = 'max(45, min(255-45, red(@menu-background)))'; + $this->lessVariables['menu-background-blue'] = 'max(45, min(255-45, blue(@menu-background)))'; + $this->lessVariables['menu-button-background'] = 'overlay(rgb(@menu-background-red, @menu-background-green, @menu-background-blue), #c0c0c0)'; + } + $this->log('CSS 9'); + $this->lessVariables['menu-text'] = $menuTextColor; + $this->lessVariables['menu-field-background'] = Color::colorToCSS($this->themeSettings->subFieldColor); + $this->lessVariables['menu-field-text'] = Color::colorToCSS($this->themeSettings->subTextFieldColor); + $this->lessVariables['menu-select-background'] = Color::colorToCSS($this->themeSettings->subSelectColor); + $this->lessVariables['menu-select-text'] = Color::colorToCSS($this->themeSettings->subTextSelectColor); + $this->lessVariables['icon-color'] = Color::colorToCSS($this->themeSettings->couleurI); + $this->lessVariables['menu-overlay'] = Color::colorToCSS($this->themeSettings->popupVideoOverlay); + + $this->log('CSS 10'); + // Chapters + $this->lessVariables['menu-chapters-columns-count'] = max(1, min(6, $this->fluidbookSettings->chaptersColumns)); + $this->lessVariables['menu-chapters-columns-width'] = $this->fluidbookSettings->chaptersColMaxWidth; + $this->lessVariables['menu-chapters-font-size'] = $this->fluidbookSettings->chaptersFontSize; + + foreach ($this->getFluidbook()->getChapters() as $chapter) { + if ($chapter['color'] == '') { + continue; + } + $color = trim($chapter['color'], '#'); + $lessContents .= '.mview.c_' . $color . '{.menu-color(' . Color::colorToCSS($color) . ');}'; + } + + // Archives +// if ($this->>getFluidbook()->parametres->externalArchivesBack) { +// $this->vdir->copy($this->wdir . '/' . $this->>getFluidbook()->parametres->externalArchivesBack, 'data/images/' . $this->>getFluidbook()->parametres->externalArchivesBack); +// $res[] = '.mview.archives{background-image:url("../images/' . $this->>getFluidbook()->parametres->externalArchivesBack . '");}'; +// } + $this->log('CSS 11'); + # Index + $thumbw = $this->fluidbookSettings->mobileNavigationType === 'portrait' ? 200 : 100; + $this->lessVariables['thumb-width'] = $thumbw . 'px'; + $ratio = $this->width / $this->height; + $thumbh = round($thumbw / $ratio); + $this->config->thumbWidth = $thumbw; + $this->config->thumbHeight = $thumbh; + + $this->lessVariables['thumb-height'] = $thumbh . 'px'; + + #tooltip + $this->lessVariables['tooltip-background'] = Color::colorToCSS($this->themeSettings->tooltipBackColor); + $this->lessVariables['tooltip-color'] = Color::colorToCSS($this->themeSettings->tooltipTextColor); + $this->lessVariables['tooltip-font-size'] = $this->themeSettings->tooltipTextSize == 100 ? 14 : $this->themeSettings->tooltipTextSize; + $this->lessVariables['tooltip-padding'] = $this->themeSettings->tooltipPadding ?: 20; + $this->log('CSS 12'); + #Videos + if ($this->fluidbookSettings->bigPlayImage) { + $this->lessVariables['video-bigplay-image'] = '~"../data/links/' . $this->fluidbookSettings->bigPlayImage . '"'; + $this->vdir->copy($this->wdir . '/' . $this->fluidbookSettings->bigPlayImage, 'data/links/' . $this->fluidbookSettings->bigPlayImage); + } else { + $this->lessVariables['video-bigplay-image'] = '~"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMTMuNCAxMTMuNCI+PHN0eWxlPi5zdDB7b3BhY2l0eTowLjg7fSAuc3Qxe2ZpbGw6I0ZGRkZGRjt9PC9zdHlsZT48cGF0aCBjbGFzcz0ic3QwIiBkPSJNMTEwLjUgMTEzLjRIMi45Yy0xLjYgMC0yLjktMS4zLTIuOS0yLjlWMi45QzAgMS4zIDEuMyAwIDIuOSAwaDEwNy42YzEuNiAwIDIuOSAxLjMgMi45IDIuOXYxMDcuNmMwIDEuNi0xLjMgMi45LTIuOSAyLjl6Ii8+PHBhdGggY2xhc3M9InN0MSIgZD0iTTQ1LjggMzcuOGwzMS41IDE3LjljLjguNS44IDEuNiAwIDIuMUw0NS44IDc1LjZjLS44LjUtMS44LS4xLTEuOC0xVjM4LjhjMC0uOSAxLTEuNSAxLjgtMXoiLz48L3N2Zz4="'; + } + + #fonts + foreach ($this->cssfont as $hash => $item) { + $res[] = '@font-face{font-family: "' . $hash . '";src:url("../../data/fonts/' . $hash . '.woff") format("woff");}'; + } + + if ($this->fluidbookSettings->textPopupStylesheet) { + $res[] = file_get_contents($this->wdir . '/' . $this->fluidbookSettings->textPopupStylesheet); + } + $this->log('CSS 13'); + $this->_writeLess($this->lessVariables, $lessContents); + $this->stylesheets[] = 'data/style/style.css'; + $this->vdir->file_put_contents('data/style/style.css', implode("\n", $res)); + $this->log('Write CSS'); + } + + protected function _writeLess($variables, $lessContents = '') + { + if ($this->widget) { + $this->lessFiles[] = 'widget'; + } + foreach ($this->specialCSS as $s) { + $this->lessFiles[] = 'special/' . $s; + } + + $tmp = Files::tmpdir(); + + $from = $this->assets . '/style/*'; + `cp -r $from $tmp`; + + $bookVariables = array(); + foreach ($variables as $k => $v) { + $bookVariables[] = '@' . trim($k) . ':' . $v . ';'; + } + file_put_contents($tmp . '/book-variables.less', implode("\n", $bookVariables)); + file_put_contents($tmp . '/additional.less', $lessContents); + + foreach ($this->lessFiles as $f) { + $source_less = $this->assets . '/style/' . $f . '.less'; + $destination_less = $tmp . '/' . $f . '.less'; + $destination_css = $tmp . '/' . $f . '.css'; + + if (!file_exists($source_less)) { + die($source_less); + continue; + } + + $dir = dirname($destination_css); + + if (file_exists($dir) && !is_dir($dir)) { + unlink($dir); + } + // LESS file might be in a subfolder, so create if it doesn't exist + if (!file_exists($dir)) { + mkdir($dir, 0777, true); + } + + // Less files must be copied to temporary directory so they'll + // have access to the variables generated in book-variables.less + copy($source_less, $destination_less); + $less = new CommandLine('lessc'); + $less->setArg(null, $destination_less); + $less->setArg(null, $destination_css); + $less->execute(); + if (!file_exists($destination_css)) { + die($less->output); + continue; + } + $this->vdir->copy($destination_css, 'style/' . $f . '.css'); + if ($f != 'widget') { + $this->stylesheets[] = 'style/' . $f . '.css'; + } + } + } + + protected function _cssBackground() + { + $body = '#background, #splash {'; + + switch ($this->themeSettings->repeat) { + case FluidbookTheme::REPEAT: + $body .= 'background-repeat:repeat;'; + break; + case FluidbookTheme::NONE: + $body .= 'background-repeat:no-repeat;'; + break; + case FluidbookTheme::RATIO: + $body .= 'background-repeat:no-repeat;'; + $body .= 'background-size:cover;'; + break; + case FluidbookTheme::STRETCH: + $body .= 'background-repeat:no-repeat;'; + $body .= 'background-size:100% 100%;'; + break; + } + + if ($backgroundImage = $this->themeAsset('backgroundImage')) { + + $dbi = Image::getimagesize($backgroundImage->getPathname()); + $this->config->backgroundImageDimensions = array('width' => $dbi[0], 'height' => $dbi[1]); + + + $this->vdir->copy($backgroundImage->getPathname(), 'data/images/' . $backgroundImage->getFilename()); + $body .= 'background-image:url(../images/' . $backgroundImage->getFilename() . ');'; + $body .= 'background-position:'; + + switch ($this->themeSettings->backgroundVAlign) { + case FluidbookTheme::TOP: + $body .= 'top'; + break; + case FluidbookTheme::MIDDLE: + $body .= 'center'; + break; + case FluidbookTheme::BOTTOM: + $body .= 'bottom'; + break; + } + + $body .= ' '; + + switch ($this->themeSettings->backgroundHAlign) { + case FluidbookTheme::LEFT: + $body .= 'left'; + break; + case FluidbookTheme::CENTER: + $body .= 'center'; + break; + case FluidbookTheme::RIGHT: + $body .= 'right'; + break; + } + $body .= ';'; + } + + $body .= '}'; + + + if ($this->_themeBoolean($this->themeSettings->displayBackgroundDuringLoading)) { + $body .= '#background, #splash { + background-color:' . Color::colorToCSS($this->themeSettings->backgroundColor) . ' !important; + }'; + } else { + $body .= '#background { + visibility: hidden; + opacity: 0; + background-color:' . Color::colorToCSS($this->themeSettings->backgroundColor) . ' !important; + }'; + + $body .= '#splash { + background-color:' . Color::colorToCSS($this->themeSettings->loadingBackColor) . ' !important; + background-image: none; + }'; + } + + return $body; + } + + public static function writeCSSUA($property, $value) + { + $res = array(); + foreach (self::$uaPrefixes as $prefix) { + $res[] = $prefix . $property . ':' . $value; + } + return implode(';', $res); + } + + protected function base62($val) + { + $chars = '0123456789abcdefghijklmnopqrstuvwxyz'; + $base = strlen($chars); + $str = ''; + do { + $i = $val % $base; + $str = $chars[$i] . $str; + $val = ($val - $i) / $base; + } while ($val > 0); + return $str; + } + + public function copyLinkDir($source, $dest) + { + $this->vdir->copyDirectory($source, $dest); + } + + public function simpleCopyLinkFile($source, $dest) + { + if (stripos($source, '.svg') !== false) { + $source = $this->_fixSVG($source); + } + + $this->vdir->copy($source, $dest); + } + + protected function _fixSVG($source) + { + $fixed = str_replace('.svg', '.f.svg', $source); + if (file_exists($fixed) && filemtime($fixed) >= filemtime($source)) { + return $fixed; + } + if (file_exists($fixed) && is_link($fixed)) { + unlink($fixed); + } + $svg = simplexml_load_string(file_get_contents($source)); + $attr = $svg->attributes(); + if (isset($attr['width'], $attr['height'])) { + copy($source, $fixed); + return $fixed; + } + + $dim = Image::getimagesize($source); + $svg->addAttribute('preserveAspectRatio', 'none'); + $svg->addAttribute('width', $dim[0]); + $svg->addAttribute('height', $dim[1]); + file_put_contents($fixed, $svg->asXML()); + + return $fixed; + } + + public function addVideoJs() + { + $locale = $this->getFluidbook()->lang; + $map = ['pt' => 'pt-PT', 'pt-br' => 'pt-BR', 'zh' => 'zh-CN', 'es-pr' => 'es']; + if (isset($map[$locale])) { + $locale = $map[$locale]; + } + + $this->addJsLib('videojs', ['js/libs/videojs/video.min.js', 'js/libs/videojs/lang/' . $locale . '.js']); + $this->addLess('videojs/videojs'); + } + + public function addParallax() + { + $this->addJsLib('parallax', ['js/libs/fluidbook/fluidbook.parallax.js']); + } + + public function addLottie($animationData, $params, $hash) + { + if (isset($this->_lottieIDByHash[$hash])) { + return $this->_lottieIDByHash[$hash]; + } + + $this->addJsLib('lottie', 'js/libs/lottie.min.js'); + + if (!isset($this->config->lottieAnimations)) { + $this->config->lottieAnimations = []; + } + + $id = count($this->config->lottieAnimations); + $this->config->lottieAnimations[] = [$params, $animationData]; + $this->_lottieIDByHash[$hash] = $id; + return $id; + } + + public function addFont($fontFile) + { + $f = $this->wdir . '/' . $fontFile; + $e = explode('.', $f); + $ext = array_pop($e); + $hash = 'fb_' . substr(md5($fontFile), 0, 10); + if (!isset($this->cssfont[$hash])) { + $final = $hash . '.woff'; + $dest = $this->wdir . '/' . $final; + if (!file_exists($dest) || filemtime($dest) < filemtime($f)) { + $script = resource_path('tools/fonts/convertrn.pe'); + if (!is_executable($script)) { + chmod($script, 755); + } + $fontforge = new CommandLine('fontforge'); + $fontforge->setArg('-script', $script); + $fontforge->setArg(null, $f); + $fontforge->setArg(null, $dest); + $fontforge->execute(); + //$fontforge->debug(); + } + $this->vdir->copy($dest, 'data/fonts/' . $hash . '.woff'); + $fontline = new CommandLine('font-line'); + $fontline->setArg(null, 'report'); + $fontline->setArg(null, $f); + $fontline->execute(); + //$fontline->debug(); + $report = explode("\n", $fontline->getOutput()); + + foreach ($report as $item) { + $item = trim($item); + if (!stristr($item, ':')) { + continue; + } + list($k, $v) = explode(':', $item, 2); + $v = trim($v); + if ($k == '[head] Units per Em') { + $fontHeight = $v; + } + if ($k == '[OS/2] CapHeight') { + $fontCapHeight = $v; + } + if ($k == '[OS/2] TypoAscender') { + $ascender = abs($v); + } + if ($k == '[OS/2] TypoDescender') { + $descender = abs($v); + } + } + $capHeight = 1; + if (isset($fontCapHeight) && isset($fontHeight)) { + $capHeight = $fontCapHeight / $fontHeight; + } + $font = ['family' => $hash, 'capHeight' => $capHeight, 'ascender' => $ascender / $fontHeight, 'descender' => $descender / $fontHeight]; + $this->cssfont[$hash] = $font; + } + return $this->cssfont[$hash]; + } + + public function addJsLib($name, $files) + { + if (!isset($this->jsLibs[$name])) { + $this->jsLibs[$name] = []; + } + if (!is_array($files)) { + $files = [$files]; + } + $diff = array_diff($files, $this->jsLibs[$name]); + if (count($diff)) { + $this->jsLibs[$name] = array_merge($this->jsLibs[$name], $diff); + } + } + + public static function encodeWebVideos($file, $dir = null, $async = true, $force = false, $format = 'all') + { + $file = new SplFileInfo($file); + if (is_null($dir)) { + $dir = $file->getPath(); + } + + $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 . '/' . $file->getBasename('.' . $file->getExtension()); + $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 CommandLine('/application/scripts/webvideo', $log); + $webvideo->setArg(null, $file); + $webvideo->setArg(null, $dir); + $webvideo->execute(); + } + + + public function copyLinkFile($source, $dest, $video = false) + { + if ($video && $this->fluidbookSettings->mobileVideosPath != '') { + + } + $origDir = $this->wdir; + $types = $this->getVideosFormats(); + if ($video) { + self::encodeWebVideos($origDir . $source, null, true); + $e = explode('.', $source); + array_pop($e); + $base = implode('.', $e); + $source = array(); + foreach ($types as $type) { + $source[] = $base . '.' . $type; + } + } + + if (!is_array($source)) { + $source = array($source); + } + + foreach ($source as $so) { + $s = $origDir . $so; + if (file_exists($s)) { + $d = $dest . '/' . $so; + $this->simpleCopyLinkFile($s, $d, false); + } + } + } + + public function __destruct() + { + + } + + public function unzipFile($file, $moveAssets = false, $baseDir = null, $junkPaths = false) + { + $fdir = is_null($baseDir) ? 'data/links/' . str_replace('.', '_', $file) : $baseDir; + + $zipPath = $this->wdir . '/' . $file; + $dir = protected_path('fluidbookpublication/cache/unzip') . '/' . Files::hashFileAttributes($zipPath) . '_' . ($moveAssets ? '1' : '0') . '_' . ($junkPaths ? '1' : '0'); + + if (!file_exists($dir)) { + mkdir($dir, 0777, true); + Zip::extract($zipPath, $dir, $junkPaths); + if ($moveAssets) { + `mv $dir/Assets/* $dir`; + rmdir($dir . '/Assets'); + } + } + + return array('dir' => $dir, 'fdir' => $fdir); + } + + public function getConfigZIP($d) + { + $res = array('type' => 'zip', 'width' => 0, 'height' => 0); + if (file_exists($d . '/index.html')) { + $doc = new DOMDocument(); + @$doc->loadHTMLFile($d . '/index.html'); + $xpath = new DOMXPath($doc); + $c = $xpath->query("//canvas"); + foreach ($c as $canvas) { + /* @var $canvas DOMElement */ + $res['width'] = intval((string)$canvas->getAttribute('width')); + $res['height'] = intval((string)$canvas->getAttribute('height')); + } + + $m = $xpath->query('//meta[@name="width"]'); + foreach ($m as $meta) { + $res['width'] = intval((string)$meta->getAttribute('content')); + } + + $m = $xpath->query('//meta[@name="height"]'); + foreach ($m as $meta) { + $res['height'] = intval((string)$meta->getAttribute('content')); + } + + $r = array('html' => 'index.html', 'inject' => array(), 'injectcss' => array(), 'injectjs' => array()); + } else { + $r = array('html' => false, 'inject' => array(file_get_contents($d . '/init.js')), 'injectcss' => array('multimedia.css'), 'injectjs' => array('multimedia.js')); + } + return array_merge($res, $r); + } + + public function addFontKit($font) + { + if ($font === 'sans-serif') { + return; + } + if ($font === 'Open Sans') { + $font = 'OpenSans'; + } + + $path = 'style/fonts/' . $font; + $css = $path . '/font.css'; + if (in_array($css, $this->stylesheets)) { + return; + } + $this->stylesheets[] = $css; + $this->vdir->copyDirectory($this->assets . '/' . $path, $path); + return $path . '/font.css'; + } + + + public function SimpleXMLElement_innerXML($xml) + { + $innerXML = ''; + foreach (dom_import_simplexml($xml)->childNodes as $child) { + $innerXML .= $child->ownerDocument->saveXML($child); + } + return $innerXML; + } + + public function writeXMLArticles() + { + $f = $this->fluidbookSettings->articlesFile; + + $this->lessVariables['articles-title-color'] = '#000000'; + $this->lessVariables['articles-font'] = 'Open Sans'; + + if (!$f || !file_exists($this->wdir . '/' . $f) || !is_file($this->wdir . '/' . $f)) { + return; + } + $f = $this->wdir . '/' . $f; + $mapFonts = ['OpenSans' => 'Open Sans']; + $this->addLess('articles'); + if ($this->fluidbookSettings->articlesStyle !== 'default') { + $this->lessVariables['articles-styles'] = $this->fluidbookSettings->articlesStyle; + } + $this->lessVariables['articles-font'] = $mapFonts[$this->fluidbookSettings->articlesFont] ?? $this->fluidbookSettings->articlesFont; + $fontPath = $this->addFontKit($this->fluidbookSettings->articlesFont); + + $list = $this->config->articlesList ?? []; + + + $this->lessVariables['articles-title-color'] = '#565657'; + + + $svg = ' + + '; + + + $x = simplexml_load_string(file_get_contents($f)); + foreach ($x->xpath('/articles/article') as $k => $a) { + $dir = isset($a['dir']) ? (string)$a['dir'] : null; + $url = (string)$a['url']; + $id = (string)$a['id']; + $color = (string)$a['color']; + if (!$color) { + $color = '#000'; + } + + $specificStyles = '## h3, ## figure figcaption{background-color:' . $color . '}'; + $specificStyles .= '## .chapo, ## blockquote, ## a{color:' . $color . ';}'; + + $inner = '
'; + $inner .= '
'; + if ($this->fluidbookSettings->articlesShare && $this->fluidbookSettings->share) { + $inner .= ''; + } + $inner .= ''; + $inner .= '
'; + + $inner .= '
'; + + $title = ''; + $lead = ''; + $image = ''; + + $first = true; + + foreach ($a->children() as $child) { + if ($first) { + $first = false; + if ($child->getName() !== 'category') { + $inner .= '

 

'; + } + } + $inner .= $this->_articleToHTML($child, $title, $lead, $image, $dir); + } + $inner .= '
'; + + if (!$title) { + $title = 'Article sans titre ' . $k; + } + + if (!$id) { + $id = Text::str2URL($title); + } + + if (!$url) { + $url = $id . '.html'; + } + + $inner = str_replace(array('$id', '$url'), array($id, $url), $inner); + + $article = ['id' => $id, + 'url' => $url, + 'color' => $color, + 'contents' => '', + 'type' => 'xml']; + + $article['contents'] = $inner; + $content = ''; + $content .= ''; + $content .= ''; + $content .= ''; + $content .= ''; + $content .= ''; + $content .= $svg; + $content .= $inner; + $content .= ''; + $article['print'] = $content; + $list[] = $article; + + $this->addSEOArticle('#/article/' . $article['url'], $title, $lead, $image, $article['id'], $article['url'], $inner); + } + + if (isset($list)) { + $this->config->articlesList = $list; + } + } + + public function findArticleById($id) + { + foreach ($this->config->articlesList as $item) { + if ($item['id'] === $id) { + return $item; + } + } + return null; + } + + public function updateArticleById($id, $article) + { + foreach ($this->config->articlesList as $k => $item) { + if ($item['id'] === $id) { + $this->config->articlesList[$k] = $article; + break; + } + } + } + + public function writeArticles() + { + $list = $this->config->articlesList ?? []; + + $nb = count($list); + + usort($list, function ($a, $b) { + if ($a['page'] == $b['page']) { + $ea = explode('-', $a['id']); + $eb = explode('-', $b['id']); + if (is_numeric($ea[0]) && is_numeric($eb[0])) { + return $ea[0] - $eb[0]; + } + return strcmp($a['id'], $b['id']); + } + return $a['page'] - $b['page']; + }); + + foreach ($list as $k => $item) { + $nextIndex = ($k + 1) % $nb; + $prevIndex = ($k - 1 + $nb) % $nb; + $list[$k]['prev'] = $list[$prevIndex]['url']; + $list[$k]['next'] = $list[$nextIndex]['url']; + } + + + $idlist = []; + foreach ($list as $item) { + $idlist[$item['id']] = $item; + } + + $this->config->articlesList = $idlist; + } + + /** + * @param $child SimpleXMLElement + * @param $title + * @param $lead + * @param $image + * @return string|void + */ + protected function _articleToHTML($child, &$title, &$lead, &$image, $dir = null) + { + $markupMap = ['category' => 'h3', + 'subtitle' => 'h2', + 'legend' => 'figcaption', + 'title' => 'h1', + 'lead' => 'div.chapo', + 'paragraph' => 'p', + 'note' => 'div.note', + 'quote' => 'blockquote', + 'signature' => 'div.author', + 'intertitle' => 'h2.inter', + 'bigfont' => 'h2.bigfont', + 'separator' => 'hr', + 'link' => 'a']; + + $attrsmap = ['a' => ['link' => 'href']]; + + $dirattr = ''; + if (isset($child['dir'])) { + $d = (string)$child['dir']; + if ($d !== $dir) { + $dirattr = ' dir="' . $d . '"'; + $dir = $d; + } + } + + $res = ''; + $tag = $child->getName(); + if ($tag === 'encadre') { + $res .= ''; + foreach ($child->children() as $sub) { + $res .= $this->_articleToHTML($sub, $a1, $a2, $a3, $dir); + } + $res .= ''; + } else if ($tag === 'youtube') { + $res .= '
'; + } else if ($tag === 'image') { + $srcattrs = ['href', 'src', 'file']; + $file = ''; + foreach ($srcattrs as $srcattr) { + if (isset($child[$srcattr])) { + $file = (string)$child[$srcattr]; + break; + } + } + if ($image === '') { + $image = 'articles/' . $file; + } + $filepath = $this->wdir . '/articles/' . $file; + $this->vdir->copy($filepath, 'data/articles/' . $file); + $legend = (string)$child; + $caption = $legend ? '
' . $legend . '
' : ''; + if (file_exists($filepath)) { + $dim = getimagesize($filepath); + } else { + $dim = [0 => 1024, 1 => 10]; + } + $res .= '' . $legend . '' . $caption . ''; + } else { + $c = trim($this->SimpleXMLElement_innerXML($child)); + if (!$c) { + return; + } + if ($title === '' && $tag === 'title') { + $title = $c; + } + if ($lead === '' && $tag === 'lead') { + $lead = $c; + } + $m = $markupMap[$tag] ?? $tag; + $e = explode('.', $m); + $markup = $e[0]; + $attrs = $dirattr; + if (count($e) === 2) { + $attrs .= ' class="' . $e[1] . '"'; + } + if ($m === 'a') { + $attrs .= ' target="_blank"'; + } + foreach ($child->attributes() as $name => $v) { + $n = $attrsmap[$m][$name] ?? $name; + $attrs .= ' ' . $n . '="' . htmlspecialchars($v) . '"'; + } + $res .= '<' . $markup . $attrs . '>' . $c . ''; + } + return $res; + } + + public static function getPhonegapVersion($v = 'latest') + { + if ($v != 'latest') { + return $v; + } + + $versions = self::getPhonegapVersions(); + return array_pop($versions); + } + + public static function getPhonegapVersions() + { + $versions = array(); + $phonegap_dir = resource_path('fluidbookpublication/phonegap'); + + if (is_dir($phonegap_dir)) { + $dr = opendir($phonegap_dir); + while ($file = readdir($dr)) { + if ($file == '.' || $file == '..' || $file == 'plugins') { + continue; + } + $versions[] = $file; + } + usort($versions, 'version_compare'); + } + + return $versions; + } + + public static function getSourcesPath($version, $dir = null) + { + $res = self::getFluidbookPlayerBaseDirectory(); + if (null !== $dir) { + $res .= $dir . '/'; + } + if ($version === 'stable') { + $res .= 'branches/master'; + } else if ($version === 'dev') { + $res .= 'local/master'; + } else { + list($branch, $location) = explode('|', $version); + $res .= ($location === 'git' ? 'branches' : $location) . '/' . $branch; + } + return $res; + } + + public static function getCompiledSourcesPath($version) + { + return self::getSourcesPath($version, 'compiled'); + } + + public function source_path($path = ''): string + { + $res = $this->assets; + if ($path) { + $res .= '/' . $path; + } + return $res; + } + + public function page_path($page, $path = ''): string + { + return ''; + } + + public function getPagePDFSource($page): string + { + return $this->getFluidbook()->getPDFSplitSource($page); + } + + public function getWidthForLinks() { + return $this->getWidth() / $this->getLinkScale(); + } + + public function getHeightForLinks() { + return $this->getHeight() / $this->getLinkScale(); + } +} diff --git a/app/Fluidbook/Link/Custom/InpesPopinLink.php b/app/Fluidbook/Link/Custom/InpesPopinLink.php index 9af1605e5..2f2a6806d 100644 --- a/app/Fluidbook/Link/Custom/InpesPopinLink.php +++ b/app/Fluidbook/Link/Custom/InpesPopinLink.php @@ -2,7 +2,7 @@ namespace App\Fluidbook\Link\Custom; -use App\Jobs\FluidbookCompiler; +use App\Fluidbook\Compiler; use Fluidbook\Tools\Links\HTMLMultimediaLink; class InpesPopinLink extends HTMLMultimediaLink @@ -36,8 +36,8 @@ class InpesPopinLink extends HTMLMultimediaLink $css .= 'width:' . $this->width * $this->getCssScale() . 'px;height:' . $this->height * $this->getCssScale() . 'px;'; $css .= $this->getCSSZIndex(); if ($this->rot) { - $css .= FluidbookCompiler::writeCSSUA('transform', 'rotate(' . $this->rot . 'deg)'); - $css .= FluidbookCompiler::writeCSSUA('transform-origin', '0% 0%'); + $css .= Compiler::writeCSSUA('transform', 'rotate(' . $this->rot . 'deg)'); + $css .= Compiler::writeCSSUA('transform-origin', '0% 0%'); } $css .= $this->getCSS(); $css .= '}'; diff --git a/app/Fluidbook/Packager/Packager.php b/app/Fluidbook/Packager/Packager.php index 06ae359c3..e095c533b 100644 --- a/app/Fluidbook/Packager/Packager.php +++ b/app/Fluidbook/Packager/Packager.php @@ -2,7 +2,7 @@ namespace App\Fluidbook\Packager; -use App\Jobs\FluidbookCompiler; +use App\Fluidbook\Compiler; use App\Models\FluidbookPublication; use App\Models\FluidbookTheme; use Cubist\Util\CommandLine; @@ -170,7 +170,7 @@ class Packager extends \App\Jobs\Base */ protected function compile($forceCompile = false) { - $compiler = new FluidbookCompiler($this->book, $this->scormVariant); + $compiler = new Compiler($this->book, $this->scormVariant); $compiler->handle(); } @@ -301,7 +301,7 @@ class Packager extends \App\Jobs\Base $dest = $this->vdir; } - $compiler = new FluidbookCompiler($book, $this->scormVariant, false, "latest", null, false, false, false, null, $hybrid); + $compiler = new Compiler($book, $this->scormVariant, false, "latest", null, false, false, false, null, $hybrid); $compiler->handle(); $rsync = new CommandLine\Rsync(rtrim($compiler->getFinalPath(), '/'), $dest, true); diff --git a/app/Fluidbook/SEO/Document.php b/app/Fluidbook/SEO/Document.php index 4ac319a2e..73e7da2e8 100644 --- a/app/Fluidbook/SEO/Document.php +++ b/app/Fluidbook/SEO/Document.php @@ -2,19 +2,17 @@ namespace App\Fluidbook\SEO; -use App\Http\Controllers\Admin\Operations\Tools\Excel2JSON; -use App\Jobs\FluidbookCompiler; +use App\Fluidbook\Compiler; use App\Models\FluidbookDocument; - - use Cubist\Excel\ExcelToArray; use Cubist\Util\Text; + class Document { /** - * @var FluidbookCompiler + * @var Compiler */ public $compiler; /** @@ -26,7 +24,7 @@ class Document /** - * @param $compiler FluidbookCompiler + * @param $compiler Compiler */ public function __construct($compiler) { diff --git a/app/Fluidbook/SearchIndex.php b/app/Fluidbook/SearchIndex.php index 2745aaedd..d82296f55 100644 --- a/app/Fluidbook/SearchIndex.php +++ b/app/Fluidbook/SearchIndex.php @@ -2,15 +2,13 @@ namespace App\Fluidbook; -use App\Jobs\FluidbookCompiler; use App\Models\FluidbookDocument; -use Cubist\PDF\CommandLine\FWSTK; use Cubist\Util\Gzip; class SearchIndex { /** - * @param $compiler FluidbookCompiler + * @param $compiler Compiler * @param $pages * @param $index * @param $textes @@ -55,7 +53,7 @@ class SearchIndex } /** - * @param $compiler FluidbookCompiler + * @param $compiler Compiler * @return array */ public static function makeHighlightIndex($compiler): array diff --git a/app/Http/Controllers/Admin/Operations/FluidbookCollection/PreviewOperation.php b/app/Http/Controllers/Admin/Operations/FluidbookCollection/PreviewOperation.php index 17a255d59..dfc0ee599 100644 --- a/app/Http/Controllers/Admin/Operations/FluidbookCollection/PreviewOperation.php +++ b/app/Http/Controllers/Admin/Operations/FluidbookCollection/PreviewOperation.php @@ -4,7 +4,6 @@ namespace App\Http\Controllers\Admin\Operations\FluidbookCollection; use App\Http\Controllers\Admin\Operations\FluidbookPreviewOperation; use App\Jobs\FluidbookCollectionDownload; -use App\Jobs\FluidbookCompiler; use App\Models\FluidbookCollection; use App\Models\FluidbookPublication; use App\Models\FluidbookTheme; diff --git a/app/Http/Controllers/Admin/Operations/FluidbookPublication/PreviewOperation.php b/app/Http/Controllers/Admin/Operations/FluidbookPublication/PreviewOperation.php index e762e0317..bf8f7af1b 100644 --- a/app/Http/Controllers/Admin/Operations/FluidbookPublication/PreviewOperation.php +++ b/app/Http/Controllers/Admin/Operations/FluidbookPublication/PreviewOperation.php @@ -2,9 +2,9 @@ namespace App\Http\Controllers\Admin\Operations\FluidbookPublication; +use App\Fluidbook\Compiler; use App\Http\Controllers\Admin\Operations\FluidbookPreviewOperation; use App\Http\Middleware\CheckIfAdmin; -use App\Jobs\FluidbookCompiler; use App\Models\FluidbookPublication; use App\Models\FluidbookTheme; use Cubist\Backpack\Http\Controllers\Base\XSendFileController; @@ -115,7 +115,7 @@ trait PreviewOperation $dest = $fluidbook->getFinalPath($theme, $isScorm); if ($path === 'index.html') { $fluidbook->incrementPreviewVisit(); - $compiler = new FluidbookCompiler($fluidbook, $isScorm, theme: $theme); + $compiler = new Compiler($fluidbook, $isScorm, theme: $theme); $compiler->handle(); } diff --git a/app/Jobs/FluidbookCompiler.php b/app/Jobs/FluidbookCompiler.php deleted file mode 100644 index 046001503..000000000 --- a/app/Jobs/FluidbookCompiler.php +++ /dev/null @@ -1,4099 +0,0 @@ - - ['js/libs/cube/util.js', - 'js/libs/cube/fb.js',], - 'modernizr' => - ['js/libs/modernizr/modernizr.min.js', - 'js/libs/modernizr/tests.js',], - 'modifier' => ['js/libs/threejs/modifier.min.js'], - 'threejs' => - ['js/libs/threejs/legacy/three.min.js', - 'js/libs/threejs/legacy/Projector.js', - 'js/libs/threejs/legacy/CanvasRenderer.js', - ], -// 'threejs-latest' => -// ['js/libs/threejs/latest/three.min.js', -// ], - 'jquery' => - [ - 'js/libs/jquery/jquery.min.js', - ], - 'jquery-extras' => ['js/libs/jquery/jquery.transform.js', - 'js/libs/jquery/jquery.form.min.js', - 'js/libs/jquery/jquery.mousewheel.min.js', - 'js/libs/jquery/jquery.hashchange.min.js', - 'js/libs/jquery/jquery.scrollto.min.js', - ], - 'aria' => ['js/libs/aria/radio.js',], - 'bluebird' => ['js/libs/bluebird.min.js'], - 'screenfull' => ['js/libs/screenfull.min.js'], - 'storage' => ['js/libs/storage.js',], - 'hotkeys' => ['js/libs/hotkeys.min.js',], - 'forge' => ['js/libs/forge/forge-sha256.min.js',], - 'perfectscrollbar' => ['js/libs/perfect-scrollbar/perfect-scrollbar.js', - 'js/libs/perfect-scrollbar/perfect-scrollbar.jquery.js'], - 'confirm' => ['js/libs/jquery/jquery.confirm.min.js'], - 'mmenu' => - ['js/libs/mmenu/jquery.mmenu.all.js'], - 'gsap' => - ['js/libs/gsap/gsap.min.js', - 'js/libs/gsap/ScrollToPlugin.min.js', - 'js/libs/gsap/InertiaPlugin.min.js', - 'js/libs/gsap/Draggable.min.js', - ], - 'hammer' => ['js/libs/hammer.min.js',], - 'interactjs' => ['js/libs/interact.min.js'], - 'gal' => - ['js/libs/gal/gal.js', - 'js/libs/gal/gal.filesystem.js',], - 'raphael' => - ['js/libs/raphael/raphael.min.js', - 'js/libs/gsap/plugins/RaphaelPlugin.min.js'], - 'countup' => - ['js/libs/countup/countup.min.js'], - 'clipboard' => ['js/libs/clipboard.min.js'], - 'fluidbook' => - ['js/libs/fluidbook/fluidbook.utils.js', - 'js/libs/fluidbook/fluidbook.networkcontrol.js', - 'js/libs/fluidbook/fluidbook.splash.js', - 'js/libs/fluidbook/fluidbook.links.js', - 'js/libs/fluidbook/fluidbook.support.js', - 'js/libs/fluidbook/fluidbook.video.js', - 'js/libs/fluidbook/fluidbook.viewport.js', - 'js/libs/fluidbook/fluidbook.desktop.js', - 'js/libs/fluidbook/fluidbook.service.js', - 'js/libs/fluidbook/fluidbook.share.js', - 'js/libs/fluidbook/fluidbook.l10n.js', - 'js/libs/fluidbook/fluidbook.slider.js', - 'js/libs/fluidbook/fluidbook.pagetransitions.js', - 'js/libs/fluidbook/fluidbook.nav.js', - 'js/libs/fluidbook/fluidbook.interface.js', - 'js/libs/fluidbook/fluidbook.input.js', - 'js/libs/fluidbook/fluidbook.touch.js', - 'js/libs/fluidbook/fluidbook.loader.js', - 'js/libs/fluidbook/fluidbook.search.js', - 'js/libs/fluidbook/fluidbook.help.js', - 'js/libs/fluidbook/fluidbook.resize.js', - 'js/libs/fluidbook/fluidbook.stats.js', - 'js/libs/fluidbook/fluidbook.cache.js', - 'js/libs/fluidbook/fluidbook.tooltip.js', - 'js/libs/fluidbook/fluidbook.bookmarks.js', - 'js/libs/fluidbook/fluidbook.background.js', - 'js/libs/fluidbook/fluidbook.pad.js', - 'js/libs/fluidbook/fluidbook.audiodescription.js', - 'js/libs/fluidbook/fluidbook.audioplayer.js', - 'js/libs/fluidbook/fluidbook.accessibility.js', - 'js/libs/fluidbook/fluidbook.privacy.js', - 'js/libs/fluidbook/fluidbook.zoom.js', - 'js/libs/fluidbook/fluidbook.menu.js', - 'js/libs/fluidbook/fluidbook.sound.js', - 'js/libs/fluidbook/fluidbook.contentlock.js', - 'js/libs/fluidbook/fluidbook.scorm.js', - 'js/libs/fluidbook/fluidbook.3dflip.js', - 'js/libs/fluidbook/menu/fluidbook.chapters.js', - 'js/libs/fluidbook/menu/fluidbook.index.js', - 'js/libs/fluidbook/fluidbook.landingpage.js', - 'js/libs/fluidbook/fluidbook.print.js', - 'js/libs/fluidbook/fluidbook.secure.js', - 'js/libs/fluidbook/fluidbook.tabs.js', - 'js/libs/fluidbook/fluidbook.articles.js', - 'js/libs/fluidbook/fluidbook.widget.js', - 'js/libs/fluidbook/fluidbook.keyboard.js', - 'js/libs/fluidbook/fluidbook.posad.js', - 'js/libs/fluidbook/fluidbook.notes.js', - 'js/libs/fluidbook/fluidbook.gamify.js', - 'js/libs/fluidbook/fluidbook.js', - 'js/main.js'], - 'mobilefirst' => [ - 'js/libs/fluidbook/fluidbook.mobilefirst.js', - 'js/libs/fluidbook/mobilefirst/fluidbook.mobilefirst.slider.js', - ], - ]; - - protected $specialJsFiles = array(); - - - public $jsFiles = []; - - // Collection of LESS files to be compiled - // Filename with no extension, relative to the /style directory in the player build folder - public $lessFiles = ['fluidbook']; - - public $specialCSS = array(); - public $phonegapStandardPlugins = array('ios' => array('ExternalFileUtil'), - 'android' => array('webintent')); - public $pluginCSS = array(); - public $pluginJs = array(); - public $htmlmultimedia = array(); - public $cssX = array(); - public $cssY = array(); - public $cssWidths = array(); - public $pdf2htmlRatio; - public $scale; - public $multiply; - public $div = array(); - public $numerotation; - public $fontDocs = array(); - public $dir; - public $z = 3; - protected $_lottieIDByHash = []; - - public $pages; - public $theme; - public $devversion; - public $book_id; - public $themeRoot; - public $needToRecompileContents = true; - public $needToRecompileSettings = true; - public $width; - public $height; - public $cssWidth; - public $cssHeight; - public $cssOneWidth; - public $cssOneHeight; - public $cssScale; - public $linkScale; - public $optimalWidth = 567; - public $optimalHeight = 709; - public $additionalConfig = array(); - public $fontScale = 1; - public $cache = array(); - public $backgroundsPrefix = array(); - public $svg = true; - public $assets = ''; - public $phonegap = false; - public $phonegapVersion; - public $standalone = false; - public $hiddenContents = array(); - public $appcache; - public $home; - public $widget = true; - public $multiApp = false; - public $pageLabels = array(); - public $stylesheets = array(); - public $logfp = null; - public $logtime = null; - public $beginBody = array(); - public $seoArticles = []; - public $securityPolicyWhitelist = ['*.google-analytics.com', '*.youtube.com', '*.ytimg.com', '*.googletagmanager.com']; - public $writeLinksData = false; - public $content_lock = []; - public $cssfont = []; - public $lessVariables = ["import-cart-styles" => 'none']; - protected $_indexVars = null; - public $accessibleTexts = []; - protected $_svgSymbols = []; - protected $_addedPDFJS = false; - protected $audioDescriptionTextsList = []; - protected $hybrid = false; - - protected $_docDimensions = []; - - public $_signature; - public $seo = null; - - /** - * @var \Cubist\Backpack\Magic\EntityData - */ - public $fluidbookSettings; - - /** - * @var \Cubist\Backpack\Magic\EntityData - */ - public $themeSettings; - - /** - * @var Command - */ - protected $_command = null; - - /** - * @param FluidbookPublication $book - * @param bool $scormVariant - * @param $phonegap - * @param $phonegapVersion - * @param $dir - * @param $standalone - * @param $appcache - * @param $home - * @param $theme FluidbookTheme|null - * @param $hybrid - * @param Command|null $command - * @throws \Exception - */ - - function __construct(FluidbookPublication $book, $scormVariant = false, $phonegap = false, $phonegapVersion = 'latest', $dir = null, $standalone = false, $appcache = false, $home = false, FluidbookTheme $theme = null, $hybrid = false, Command $command = null) - { - ExcelToArray::setCache(protected_path('fluidbookpublication/cache/exceltoarray')); - - parent::__construct(); - - $this->setFluidbook($book); - $this->setCommand($command); - - $this->phonegapVersion = self::getPhonegapVersion($phonegapVersion); - $this->appcache = $appcache; - $this->multiApp = $this->home = $home; - $this->devversion = $this->getFluidbook()->mobileLVersion; - $this->scormVariant = $scormVariant; - - $this->hybrid = $hybrid; - - $this->assets = self::getSourcesPath($this->devversion); - $this->compiledAssets = self::getCompiledSourcesPath($this->devversion); - - $this->phonegap = $phonegap; - $this->standalone = $standalone || $this->phonegap; - $this->appcache = $appcache; - $this->widget = !$this->phonegap; - - $this->fluidbookSettings = $this->getFluidbook()->getSettings(); - - PHP::memoryAllocate('12G'); - - $this->book_id = $this->getFluidbook()->id; - $this->log('Start compilation'); - - $this->dir = $this->getFluidbook()->getFinalPath($theme, $scormVariant); - $this->vdir = new VirtualDirectory($this->dir); - - $this->wdir = $this->getFluidbook()->getAssetDir(); - - $this->widget = false; - - $this->pages = $this->getFluidbook()->composition; - $this->maxRes = min(self::MAX_RES, $this->fluidbookSettings->maxResolution); - - $this->theme = $theme ?? $this->getFluidbook()->getTheme(); - - $this->themeSettings = $this->theme->getPageData(); - $this->log('Got data from database'); - - $this->width = round($this->getFluidbook()->getPageWidth(), 8); - $this->height = round($this->getFluidbook()->getPageHeight(), 8); - - $this->imageFormat = $this->fluidbookSettings->imageFormat; - - $p1 = $this->getFluidbook()->getFile(1, $this->imageFormat, 150); - $imagesize = Image::getimagesize($p1); - $this->pdf2htmlRatio = round(($imagesize[0] * 0.48) / $this->width, 12); - - $this->linkScale = $this->cssScale = $this->z * min($this->optimalWidth / $this->width, $this->optimalHeight / $this->height); - - $this->cssOneScale = $this->z * min(($this->optimalWidth * 2) / $this->width, $this->optimalHeight / $this->height); - - $this->cssWidth = $this->width * $this->cssScale; - $this->cssHeight = $this->height * $this->cssScale; - - $this->cssOneWidth = $this->width * $this->cssOneScale; - $this->cssOneHeight = $this->height * $this->cssOneScale; - - $this->scale = 1; - - if ($this->isMobileFirst()) { - $this->cssScale = $this->cssOneScale = 480 / $this->width; - $this->linkScale = $this->cssScale; - $this->cssOneWidth = $this->cssWidth = $this->width * $this->cssScale; - $this->cssOneHeight = $this->cssHeight = $this->height * $this->cssScale; - $this->initMobileFirst(); - } - - $this->svgfiles = array_unique([$this->assets . '/images/symbols/interface.svg', - storage_path('icons/15.svg')]); - if ($this->themeSettings->iconSet > 15) { - $this->svgfiles[] = storage_path('icons/' . $this->themeSettings->iconSet . '.svg'); - } - if ($symbols = $this->themeAsset('symbols')) { - $this->svgfiles[] = $symbols->getPathname(); - } - - if ($this->isMobileFirst()) { - $this->multiply = $this->pdf2htmlRatio * $this->scale * $this->cssOneScale; - } else { - $this->multiply = $this->pdf2htmlRatio * $this->scale * $this->cssScale; - } - - $this->initConfig(); - $this->log('Defined dimensions'); - } - - public function getFinalPath() - { - return $this->dir; - } - - - public function getSetting($key, $default = null) - { - if ($this->fluidbookSettings->has($key)) { - return $this->fluidbookSettings->get($key, $default); - } - if ($this->themeSettings->has($key)) { - return $this->themeSettings->get($key, $default); - } - return $default; - } - - - public function setSetting($key, $value) - { - if ($this->themeSettings->has($key)) { - $this->themeSettings->set($key, $value); - return; - } - $this->fluidbookSettings->set($key, $value); - } - - - /** - * @param $key - * @param $default - * @return null|SplFileInfo - */ - public function themeAsset($key) - { - $collection = $this->theme->{$key}; - - if ($collection) { - $path = $this->theme->getFirstMediaPath($collection); - if (!$path || !file_exists($path)) { - $res = false; - } else { - $res = new SplFileInfo($path); - } - } else { - $res = false; - } - if (null !== $this->config) { - if ($res instanceof SplFileInfo) { - $this->config->set($key, $res->getFilename()); - } else { - $this->config->set($key, ''); - } - } - return $res; - } - - /** - * @return FluidbookPublication - */ - public function getFluidbook(): FluidbookPublication - { - return $this->_fluidbook; - } - - /** - * @param FluidbookPublication $fluidbook - */ - public function setFluidbook(FluidbookPublication $fluidbook): void - { - $this->_fluidbook = $fluidbook; - } - - /** - * @return Command|null - */ - public function getCommand(): ?Command - { - return $this->_command; - } - - /** - * @param Command|null $command - */ - public function setCommand(?Command $command): void - { - $this->_command = $command; - } - - public function isMobileFirst() - { - return $this->fluidbookSettings->mobileNavigationType === 'mobilefirst'; - } - - public function initMobileFirst() - { - $this->themeSettings->usePageEdges = false; - } - - public function initConfig() - { - if (!$this->scormVariant) { - $this->fluidbookSettings->scorm_enable = false; - } - - $this->config = new Data(array_merge($this->fluidbookSettings->getRawData()['settings'], $this->themeSettings->getRawData())); - $this->config->pages = count($this->getFluidbook()->composition); - $this->config->bookmarkDisablePages = ArrayUtil::parseRange($this->config->bookmarkDisablePages); - $this->config->rasterizePages = ArrayUtil::parseRange($this->config->rasterizePages); - $this->config->vectorPages = array_diff(ArrayUtil::parseRange($this->config->vectorPages), $this->config->rasterizePages); - $this->numerotation = $this->config->numerotation = explode(',', $this->getFluidbook()->page_numbers); - - $hideOnPages = ArrayUtil::parseRange($this->config->tabsHideOnPages); - $this->config->tabsDisabledOnPages = ArrayUtil::parseRange($this->config->tabsDisabledOnPages); - - if ($this->config->tabsHideOnCover) { - $hideOnPages[] = 0; - $hideOnPages[] = 1; - } - if ($this->config->tabsHideOnLastPage) { - $hideOnPages[] = count($this->pages); - } - $this->config->tabsHideOnPages = $hideOnPages; - $this->config->triggersLinks = []; - $this->config->hasContentLock = false; - } - - public function populateConfig() - { - $this->config->id = $this->getFluidbook()->book_id; - $this->config->cid = $this->getFluidbook()->cid; - $this->config->cacheDate = time(); - $this->config->width = round($this->cssWidth, 2); - $this->config->height = round($this->cssHeight, 2); - $this->config->optimalWidth = round($this->optimalWidth, 2); - $this->config->optimalHeight = round($this->optimalHeight, 2); - $this->config->chapters = $this->getFluidbook()->chapters; - $this->config->videoFormats = $this->getVideosFormats(false); - $this->config->htmlmultimedia = $this->htmlmultimedia; - $this->config->phonegap = $this->phonegap; - $this->config->retinaResolution = min($this->fluidbookSettings->maxResolution, $this->maxRes); - $this->config->standardResolution = min($this->fluidbookSettings->maxResolution, 150); - $this->config->pageLabels = $this->pageLabels; - $this->config->pageZoomFactor = $this->z; - $this->config->multiply = round($this->multiply, 6); - $this->config->cssScale = round($this->cssScale, 6); - $this->config->pdfZoomFactor = round($this->pdf2htmlRatio, 6); - if ($this->home) { - $this->config->home = 'http://home'; - } - $this->config->multiApp = $this->multiApp; - foreach ($this->additionalConfig as $k => $v) { - $this->config->$k = $v; - } - if ($this->phonegap && ($this->fluidbookSettings->offlineLink == '' || $this->fluidbookSettings->offlineLink == 'http://')) { - $this->config->share = false; - } - if ($this->config->maxPages > 0) { - $this->addContentLock($this->config->maxPages); - } - - // We need to be able to reference both navOrder and navOrderH so convert both to arrays - // We also make sure there are no empty items in the arrays (see: http://php.net/manual/en/function.array-filter.php#111091) - $this->config->navOrder = array_filter(array_map('trim', explode(',', $this->config->navOrder)), 'strlen'); - $this->config->navOrderH = array_filter(array_map('trim', explode(',', $this->config->navOrderH)), 'strlen'); - - $this->config->standalone = $this->standalone; - if ($this->config->phonegap) { - $this->config->manifest = $this->writeManifest(); - } - - if ($this->config->form == 'bulle') { - $this->addJsLib('bulle', 'js/libs/fluidbook/forms/fluidbook.form.bulle.js'); - } else if ($this->config->form == 'bourbon') { - $this->addJsLib('parsley', 'js/libs/parsley.min.js'); - $this->addJsLib('bourbon', 'js/libs/fluidbook/forms/fluidbook.form.bourbon.js'); - } else if ($this->config->form == 'avery') { - $this->addJsLib('parsley', 'js/libs/parsley.min.js'); - $this->addJsLib('avery', 'js/libs/fluidbook/forms/fluidbook.form.avery.js'); - $this->addLess('form/avery'); - $this->writeCountries(); - } - $this->config->seoArticles = $this->seoArticles; - } - - public function writeGrandVisionCart() - { - $this->lessVariables['import-cart-styles'] = 'grandvision'; - - $this->addJsLib('cookie', 'js/libs/jquery/jquery.cookie.js'); - $this->addJsLib('grandvision', 'js/libs/fluidbook/cart/fluidbook.cart.grandvision.js'); - $this->addJsLib('html2pdf', 'js/libs/html2pdf/html2pdf.min.js'); - $this->addJsLib('multiselect', 'js/libs/jquery/jquery.multi-select.js'); - $this->addJsLib('jqueryui', 'js/libs/jquery/jquery-ui.min.js'); - $this->addJsLib('exceljs', 'js/libs/exceljs.min.js'); - $this->svgfiles[] = $this->assets . '/images/symbols/grandvision.svg'; - - $cdir = $this->wdir . '/commerce/'; - $file = $cdir . $this->fluidbookSettings->basketReferences; - $refs = ExcelToArray::excelToArrayKeyVars($file, 'Excel2007', true); - $this->config->basketReferences = []; - foreach ($refs as $ean => $ref) { - $this->config->basketReferences[$ean] = $ref; - $this->config->basketReferences[$ean]['angle_url'] = base64_encode(file_get_contents($this->wdir . '/commerce/opt/' . $ean . '-angle.jpg')); - } - - $odir = $cdir . '/opt/'; - if (!file_exists($odir)) { - mkdir($odir, 0777, true); - } - - $it = Files::getDirectoryIterator($cdir); - $exts = ['png', 'jpg', 'tif', 'mp4']; - foreach ($it as $file) { - - /** @var $file SplFileInfo */ - if ($file->isDir()) { - continue; - } - $ext = $file->getExtension(); - if (!in_array($ext, $exts)) { - continue; - } - - $e = Text::multiExplode('_-.', mb_strtolower($file->getFilename())); - $ean = $this->findEAN($e); - if (!$ean) { - continue; - } - if (!isset($this->config->basketReferences[$ean])) { - continue; - } - $f = $file->getPathname(); - - if ($ext === 'mp4') { - $n = $ean . '-360.mp4'; - $this->config->basketReferences[$ean]['360'] = true; - $opt = $odir . '/' . $n; - if (!file_exists($opt) || !filesize($opt) || filemtime($opt) < filemtime($f)) { - // Optimize original video - `ffmpeg -i $f -filter:v scale=360:-2 -vcodec libx264 -an $opt`; - touch($opt, filemtime($f)); - } - } else { - if (in_array('front', $e)) { - $type = 'front'; - } else if (in_array('angle', $e)) { - $type = 'angle'; - } else { - continue; - } - - $n = $ean . '-' . $type . '.jpg'; - $this->config->basketReferences[$ean][$type] = true; - $opt = $odir . '/' . $n; - if (!file_exists($opt) || !filesize($opt) || filemtime($opt) < filemtime($f)) { - // Optimize original image - $convert = new Resizer(); - $convert->loadImage($f); - $convert->resize(1080, null, 'ratio', false, 'C', 'M', 'white'); - $convert->output('jpg', $opt, 75); - touch($opt, filemtime($f)); - } - } - $this->vdir->copy($opt, 'data/commerce/' . $n); - } - } - - public function findEAN($array) - { - foreach ($array as $item) { - if (strlen($item) === 13 && preg_match('/^\d{13}$/', $item)) { - return $item; - } - } - return false; - } - - public function writeFlexipanCart() - { - $this->lessVariables['import-cart-styles'] = 'flexipan'; - - $this->addJsLib('cookie', 'js/libs/jquery/jquery.cookie.js'); - $this->addJsLib('flexipan', 'js/libs/fluidbook/cart/fluidbook.cart.flexipan.js'); - $this->addJsLib('html2pdf', 'js/libs/html2pdf/html2pdf.min.js'); - - $cdir = $this->wdir . '/commerce/'; - - - $file = $cdir . $this->fluidbookSettings->basketReferences; - $this->config->basketReferences = ExcelToArray::excelToArrayKeyVars($file); - - $this->getLinksAndRulers($links, $rulers); - - foreach ($links as $link) { - if ($link['type'] == '12') { - - } - } - - $this->config->product_zoom_references = []; - foreach ($this->config->basketReferences as $ref => $data) { - $this->config->product_zoom_references[$ref] = [$ref]; - } - } - - public function getLinksAndRulers(&$links, &$rulers) - { - Links::getLinksAndRulers($this->book_id, $links, $rulers, 'latest', true); - } - - public function writeMIFCart() - { - $this->lessVariables['import-cart-styles'] = 'mif'; - - $this->addJsLib('cookie', 'js/libs/jquery/jquery.cookie.js'); - $this->addJsLib('mif', 'js/libs/fluidbook/cart/fluidbook.cart.mif.js'); - $this->addJsLib('html2pdf', 'js/libs/html2pdf/html2pdf.min.js'); - - $cdir = $this->wdir . '/commerce/'; - $odir = $cdir . '/opt/'; - if (!file_exists($odir)) { - mkdir($odir, 0777, true); - } - - $file = $cdir . $this->fluidbookSettings->basketReferences; - $this->config->basketReferences = ExcelToArray::excelToArrayKeyVars($file); - - $this->getLinksAndRulers($links, $rulers); - - foreach ($this->config->basketReferences as $ref => $data) { - $source = $cdir . '/' . $data['Image']; - if (!file_exists($source)) { - continue; - } - $d = Text::str2URL($ref) . '.jpg'; - $dest = $odir . '/' . $d; - if (!file_exists($dest) || !filesize($dest) || filemtime($dest) < filemtime($source)) { - $convert = new Resizer(); - $convert->loadImage($source); - $convert->resize(500, 500, 'ratio', false, 'C', 'M', 'ffffff'); - $convert->output('jpg', $dest, 80); - } - $vdest = 'data/commerce/opt/' . $d; - $this->vdir->copy($dest, $vdest); - $this->config->basketReferences[$ref]['Image'] = $vdest; - } - - foreach ($links as $link) { - if ($link['type'] == '12') { - - } - } - -// $this->config->product_zoom_references = []; -// foreach ($this->config->basketReferences as $ref => $data) { -// $r = [$data['Lien']]; -// $this->config->product_zoom_references[$ref] = $r; -// } - } - - public function writeJoueClub2021Cart() - { - $this->lessVariables['import-cart-styles'] = 'joueclub2021'; - $extra = Link::parseExtras($this->fluidbookSettings->cartExtraSettings, true); - - /** - * buttonColor=#d7b646 - * buttonTextColor=#ffffff - * headerBackgroundColor=#0e1a3c - * headerTextColor=#ffffff - */ - $this->lessVariables['cart-button-color'] = $extra['buttoncolor'] ?? '#e30613'; - $this->lessVariables['cart-button-text-color'] = $extra['buttontextcolor'] ?? '#ffffff'; - $this->lessVariables['cart-button-radius'] = $extra['buttonradius'] ?? '50%'; - $this->lessVariables['cart-header-background-color'] = $extra['headerbackgroundcolor'] ?? '#26348b'; - $this->lessVariables['cart-header-text-color'] = $extra['headertextcolor'] ?? '#ffffff'; - $this->lessVariables['cart-close-color'] = $extra['closecolor'] ?? '#ffffff'; - $this->lessVariables['cart-close-background-color'] = $extra['closebackgroundcolor'] ?? '#e30613'; - $this->lessVariables['cart-close-radius'] = $extra['closeradius'] ?? '50%'; - $this->lessVariables['cart-actions-radius'] = $extra['actionsradius'] ?? '8px'; - $this->lessVariables['cart-actions-background-color'] = $extra['actionsbackgroundcolor'] ?? '#26348b'; - $this->lessVariables['cart-actions-text-color'] = $extra['actionstextcolor'] ?? '#ffffff'; - $this->lessVariables['cart-scrollbar-color'] = $extra['scrollbarcolor'] ?? '#26348b'; - - $this->addJsLib('cookie', 'js/libs/jquery/jquery.cookie.js'); - $this->addJsLib('joueclub2021', 'js/libs/fluidbook/cart/fluidbook.cart.joueclub2021.js'); - $this->addJsLib('html2pdf', 'js/libs/html2pdf/html2pdf.min.js'); - - $cdir = $this->wdir . '/commerce/'; - - $file = $cdir . $this->fluidbookSettings->basketReferences; - $this->config->basketReferences = ExcelToArray::excelToArrayKeyVars($file); - - foreach ($this->config->basketReferences as $ref => $data) { - $dest = $cdir . $ref . '.jpg'; - if (!file_exists($dest)) { - copy($data['img'], $dest); - } - $this->vdir->copy($dest, 'data/commerce/' . $ref . '.jpg'); - } - $addFiles = [$this->config->cartHeaderImage, $this->config->cartHeaderMobileImage]; - foreach ($addFiles as $f) { - if (!$f) { - return; - } - - $this->vdir->copy($cdir . $f, 'data/commerce/' . $f); - } - - $this->getLinksAndRulers($links, $rulers); - } - - public function writeGrandPavoisCart() - { - $this->lessVariables['import-cart-styles'] = 'grandpavois'; - - $this->addJsLib('cookie', 'js/libs/jquery/jquery.cookie.js'); - $this->addJsLib('grandpavois', 'js/libs/fluidbook/cart/fluidbook.cart.grandpavois.js'); - $this->addJsLib('html2pdf', 'js/libs/html2pdf/html2pdf.min.js'); - - $cdir = $this->wdir . '/commerce/'; - $odir = $cdir . '/opt/'; - if (!file_exists($odir)) { - mkdir($odir, 0777, true); - } - - $file = $cdir . $this->fluidbookSettings->basketReferences; - $this->config->basketReferences = ExcelToArray::excelToArrayKeyVars($file); - - $this->getLinksAndRulers($links, $rulers); - } - - - public function writePumaCart() - { - $this->lessVariables['import-cart-styles'] = 'puma'; - - $this->addJsLib('parsley', 'js/libs/parsley.min.js'); - $this->addJsLib('cookie', 'js/libs/jquery/jquery.cookie.js'); - $this->addJsLib('puma', 'js/libs/fluidbook/cart/fluidbook.cart.puma.js'); - $this->addJsLib('html2pdf', 'js/libs/html2pdf/html2pdf.min.js'); - $this->addJsLib('exceljs', 'js/libs/exceljs.min.js'); - $this->addVideoJs(); - - $this->config->basketReferences = ExcelToArray::excelToArrayKeyVars($this->wdir . 'commerce/' . $this->fluidbookSettings->basketReferences); - $eanFile = $this->wdir . 'commerce/ean.xlsx'; - if (file_exists($eanFile)) { - $this->config->eanReferences = ExcelToArray::excelToArrayIndexKeyVars($eanFile); - } - - $this->getLinksAndRulers($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'; - $this->config->basketReferences[$link['to']]['zoom_url'] = base64_encode(file_get_contents($this->dir . '/data/links/zoom_' . $link['uid'] . '.jpg')); - $this->config->basketReferences[$link['to']]['zoom_image_ratio'] = $link['width'] / $link['height']; - } - } - - $this->config->product_zoom_references = []; - $files = ['360°', 'Image supplémentaire', 'Fiche technique']; - foreach ($this->config->basketReferences as $ref => $data) { - $r = []; - foreach ($files as $file) { - if (!isset($data[$file])) { - $data[$file] = ''; - } - $fname = trim($data[$file]); - if ($fname !== '') { - $fname = str_replace(' ', '-', $fname); - $wfile = $this->wdir . 'commerce/' . $fname; - if (file_exists($wfile)) { - $fname = 'data/commerce/' . $fname; - $this->vdir->copy($wfile, $fname); - } else { - $fname = ''; - } - } - $r[] = $fname; - } - $this->config->product_zoom_references[$ref] = $r; - } - } - - - public function writeThirietCart() - { - $this->config->cartLinkAppearance = 'overlay'; - $this->svgfiles[] = $this->assets . '/images/symbols/cart-overlay.svg'; - $this->addJsLib('thiriet', 'js/libs/fluidbook/cart/fluidbook.cart.thiriet.js'); - } - - public function writeCFOCCart() - { - - $this->lessVariables['import-cart-styles'] = 'cfoc'; - - $this->addJsLib('cfoc', 'js/libs/fluidbook/cart/fluidbook.cart.cfoc.js'); - - if (!empty($this->config->basketReferences) && is_string($this->config->basketReferences)) { - if (file_exists($this->config->basketReferences) || Url::isDistant($this->config->basketReferences)) { - $referencesFile = $this->config->basketReferences; - } else { - $referencesFile = $this->wdir . 'commerce/' . $this->config->basketReferences; - } - } - - $references = []; - - if (file_exists($referencesFile) || Url::isDistant($referencesFile)) { - $rows = ExcelToArray::excelToArrayFirstSheet($referencesFile); - - - // Expected headings are: EXCLU, LIGNE, EAN, REF, DESIGNATION, COULEUR, QTE MINI, PRIX TTC - $column_headings = array_shift($rows); // We assume the first row will be the headings, so we slice it off - $column_headings = array_map(function ($heading) { // Clean the headings a bit - return trim(strtoupper($heading)); - }, $column_headings); - - foreach ($rows as $row) { - - // First, trim values in case there are any stray spaces - $row = array_map('trim', $row); - - // Next, set the headings as keys to make it easier to refer to the row values by name - $row = array_combine($column_headings, $row); - - // For the 'EXCLU' field, this should be converted to a boolean - $row['EXCLU'] = ('exclu boutique' === $row['EXCLU']); - - // Make sure the PRIX TTC field doesn't have any stray commas or spaces in the numbers - // because this will cause unexpected problems for the calculations - $row['PRIX TTC'] = str_replace([',', ' '], '', $row['PRIX TTC']); - - // The EAN and REF are required, so if they don't exist, we can assume it's not a valid row - if (empty($row['EAN']) || empty($row['REF'])) { - continue; - } - - $references[$row['REF']][$row['EAN']] = $row; - } - - } - - $this->config->basketReferences = $references; - - // It's possible to use the cartExtraSettings field in the parameters to define publication specific settings, - // such as the subject line used in the e-mail that is sent from the cart validation - $extra = Link::parseExtras($this->fluidbookSettings->cartExtraSettings, true); - - $this->config->cartEmailSubject = $extra['email_subject'] ?? 'Récapitulatif de votre commande CFOC'; - } - - /** - * @throws \Exception - */ - public function writeBastideCart() - { - - $this->lessVariables['import-cart-styles'] = 'bastide'; - - $this->addJsLib('bastide', 'js/libs/fluidbook/cart/fluidbook.cart.bastide.js'); - - - if (!empty($this->config->basketReferences)) { - if (file_exists($this->config->basketReferences) || Url::isDistant($this->config->basketReferences)) { - $referencesFile = $this->config->basketReferences; - } else { - $referencesFile = $this->wdir . 'commerce/' . $this->config->basketReferences; - } - } - - $references = []; - - if (file_exists($referencesFile) || Url::isDistant($referencesFile)) { - $rows = ExcelToArray::excelToArrayFirstSheet($referencesFile); - - // Expected headings are: n° page, Chapitre, Article Code, Article, Conditionnement - $column_headings = array_shift($rows); // We assume the first row will be the headings, so we slice it off - $column_headings = array_map(function ($heading) { // Normalise the headings, removing extra spaces and line breaks - return trim(strtoupper(preg_replace('/\s+/', ' ', $heading))); - }, $column_headings); - - foreach ($rows as $row) { - - // First, trim values in case there are any stray spaces - $row = array_map('trim', $row); - - // Next, set the headings as keys to make it easier to refer to the row values by name - $row = array_combine($column_headings, $row); - - // The ARTICLE CODE is required, so if it doesn't exist, we can assume it's not a valid row - if (empty($row['ARTICLE CODE'])) { - continue; - } - - $references[$row['ARTICLE CODE']] = $row; - } - - } - - $this->config->basketReferences = $references; - - // Allow individual Fluidbooks to override the columns shown in the cart - $extra = Link::parseExtras($this->config->cartExtraSettings, true); - - if (!empty($extra['cart_columns'])) { - // In the "Paramètres panier" field (cartExtraSettings), the cart columns can be defined in this format: - // cart_columns=XLS COL NAME|Display name,XLS COL 2|Display name 2 - // This setting needs to be trimmed and converted into an associative array with column_name => display_name - // Split by commas, then by pipes | - $columns = array_map(function ($heading) { - return explode('|', $heading); - }, explode(',', $extra['cart_columns'])); - $processed_columns = []; - foreach ($columns as $column) { - $processed_columns[strtoupper(trim($column[0]))] = trim($column[1] ?? ''); - } - - // Ensure that special QUANTITY and DELETE columns are present (see getColumns() in fluidbook.cart.bastide.js) - $processed_columns['QUANTITY'] = $processed_columns['QUANTITY'] ?? 'Quantité'; - $processed_columns['DELETE'] = ''; - - $this->config->cartColumns = $processed_columns; - } - } - - public function writeCartConfig() - { - if ($this->fluidbookSettings->cartLinkAppearance == 'overlay') { - $this->svgfiles[] = $this->assets . '/images/symbols/cart-overlay.svg'; - } - - if ($this->config->basket) { - $this->addJsLib('cart', 'js/libs/fluidbook/fluidbook.cart.js'); - switch ($this->config->basketManager) { - case 'Thiriet': - $this->writeThirietCart(); - return; - case 'Flexipan': - $this->writeFlexipanCart(); - return; - case 'Puma': - $this->writePumaCart(); - return; - case 'MIF': - $this->writeMIFCart(); - return; - case 'GrandVision': - $this->writeGrandVisionCart(); - return; - case 'GrandPavois': - $this->writeGrandPavoisCart(); - return; - case 'JoueclubWishlist2021': - $this->writeJoueClub2021Cart(); - return; - case 'Remarkable': - $this->addJsLib('parsley', 'js/libs/parsley.min.js'); - $this->addJsLib('cookie', 'js/libs/jquery/jquery.cookie.js'); - $this->addJsLib('remarkable', 'js/libs/fluidbook/cart/fluidbook.cart.remarkable.js'); - break; - case 'Mopec': - $this->addJsLib('parsley', 'js/libs/parsley.min.js'); - $this->addJsLib('cookie', 'js/libs/jquery/jquery.cookie.js'); - $this->addJsLib('mopec', 'js/libs/fluidbook/cart/fluidbook.cart.mopec.js'); - break; - case 'CFOC': - $this->writeCFOCCart(); - break; - case 'Bastide': - $this->writeBastideCart(); - break; - default: - break; - } - } - if (!$this->config->product_zoom_references && $this->config->basketReferences && $this->config->basketManager == "ZoomProductLink") { - $this->config->product_zoom_references = $this->config->basketReferences; - $this->config->basketReferences = ''; - } - - if ($this->config->product_zoom_references) { - if (file_exists($this->config->product_zoom_references) || Url::isDistant($this->config->product_zoom_references)) { - $referencesFile = $this->config->product_zoom_references; - } else { - $referencesFile = $this->wdir . '/commerce/' . $this->config->product_zoom_references; - } - if (file_exists($referencesFile) || Url::isDistant($referencesFile)) { - $this->config->product_zoom_references = ExcelToArray::excelToArrayKeyValMulti($referencesFile, 'Excel2007', true); - } - } - - if ($this->config->basketReferences && is_string($this->config->basketReferences)) { - if (file_exists($this->config->basketReferences) || Url::isDistant($this->config->basketReferences)) { - $referencesFile = $this->config->basketReferences; - } else { - $referencesFile = $this->wdir . '/commerce/' . $this->config->basketReferences; - } - - if (file_exists($referencesFile) || Url::isDistant($referencesFile)) { - $ext = Files::getExtension($referencesFile); - if ($ext == 'xlsx') { - if ($this->config->basketManager == "ZoomProductLink") { - $function = 'excelToArrayKeyVal'; - } else { - $function = 'excelToArray'; - } - $this->config->basketReferences = ExcelToArray::$function($referencesFile); - if ($this->fluidbookSettings->customLinkClass == 'AtlanticDownloadLink') { - $this->config->basketReferences = self::atlanticReferences($this->config->basketReferences, 'local/', array($this, 'log'), array($this->vdir, "copy")); - } - } - $this->log("Done cart references"); - } - } - } - - public static function atlanticReferences($references, $dir, $log = null, $copy = 'copy') - { - foreach ($references as $i => $sheet) { - foreach ($sheet as $j => $line) { - foreach ($line as $k => $v) { - if (preg_match('|^http:\/\/atlantic-international-book-com\.com\/files\/(.*)$|', $v, $matches)) { - $local = $dir . '/' . $matches[1]; - $url = str_replace(' ', '%20', $v); - $cache = protected_path('fluidbookpublication/atlantic') . md5($url); - if (!file_exists($cache) || filemtime($url) > filemtime($cache)) { - copy($url, $cache); - $copylog = ' (copy) '; - } else { - $copylog = ' '; - } - call_user_func($copy, $cache, $dir . '/' . $matches[1]); - $references[$i][$j][$k] = 'local/' . $matches[1]; - if (null !== $log) { - call_user_func($log, 'Done' . $copylog . $matches[1]); - } - } - } - } - } - - return $references; - } - - public function log($step) - { - $currenttime = microtime(true); - if (null === $this->logfp) { - $this->logfp = fopen(Files::mkdir(storage_path('logs/htmlconversions')) . $this->book_id . '.log', 'w+'); - } - if (null === $this->logtime) { - $this->logtime = $currenttime; - } - $time = $currenttime - $this->logtime; - $log = $step . ' | ' . round($time, 3) . 's' . "\n"; - fwrite($this->logfp, $log); - fflush($this->logfp); - $this->logtime = $currenttime; - - if (null !== $this->getCommand()) { - $this->getCommand()->info(trim($log)); - } - } - - public function addFacebookSDK() - { - $lang = str_replace('-', '_', $this->getFluidbook()->lang); - $e = explode('_', $lang); - if (count($e) > 1) { - $e[1] = mb_strtoupper($lang); - } - $lang = implode('_', $e); - - $langsMap = ['fr' => 'fr_FR', 'en' => 'en_US']; - - if (isset($langsMap[$lang])) { - $lang = $langsMap[$lang]; - } - - $this->beginBody[] = "
-"; - $this->securityPolicyWhitelist[] = '*.facebook.net'; - $this->securityPolicyWhitelist[] = 'data:'; - } - - public function addPageLabel($page, $label) - { - $this->pageLabels[$label] = $page; - } - - public function getResolutions() - { - return self::getBookResolutions($this->getFluidbook()); - } - - - public static function getBookResolutions($book) - { - $maxRes = min(self::MAX_RES, $book->settings['maxResolution']); - $res = []; - if ($maxRes == self::MAX_RES) { - $res = [150, self::MAX_RES]; - } else if ($maxRes <= 150) { - $res = [$maxRes]; - } - return $res; - } - - public function getCssScale() - { - return $this->cssScale; - } - - public function getLinkScale() - { - return $this->linkScale; - } - - public function virtualToPhysical($virtual): string|int - { - if (isset($this->pageLabels[$virtual])) { - return $virtual; - } - if (!in_array($virtual, $this->numerotation)) { - return $virtual; - } - $p = array_search($virtual, $this->numerotation); - return $p + 1; - } - - public function handle() - { - $this->log('Preprocess images'); - FluidbookImagesPreprocess::dispatchSync($this->book_id); - $this->log('Start compile process'); - - // Raw copy of some directories - $directories = array('style/fonts/OpenSans', 'images', 'sound'); - foreach ($directories as $directory) { - $from = $this->assets . '/' . $directory; - $this->vdir->copyDirectory($from, $directory); - } - - if ($this->fluidbookSettings->scorm_enable || $this->fluidbookSettings->secureClientSidePassword) { - $this->fluidbookSettings->seoVersion = false; - } - if ($this->fluidbookSettings->embedAllLibraries) { - $this->addVideoJs(); - $this->addSlideshowLibrary(false); - $this->addSlideshowLibrary(true); - } - - $this->log('Copied assets'); - $this->writeSecure(); - $this->loadPlugins(); - $this->log('Plugins loaded'); - $this->writeImages(); - $this->log('Images written'); - $this->writeCartConfig(); - $this->writeXMLArticles(); - $this->log('XML Articles written'); - $this->writeSlider(); - $this->log('Slider written'); - $linksCSS = $this->writeLinks(); - $this->log('Links written'); - $this->writeArticles(); - $this->log('Articles written'); - $this->writeStats(); - $this->log('Stats written'); - $this->writeLangs(); - $this->log('Langs written'); - $this->writeSEO(); - $this->log('SEO written'); - $this->writeWidget(); - $this->log('Widget written'); - $this->writeSounds(); - $this->log('Sound written'); - $this->writeTexts(); - $this->log('Texts written'); - $this->writeAccessibility(); - $this->log('Accessibility written'); - $this->writeExtras(); - $this->log('Extras written'); - $this->populateConfig(); - $this->log('Config populated'); - $this->writeCSS($linksCSS); - $this->log('CSS written'); - $this->writeIndex(); - $this->log('Index written'); - if ($this->fluidbookSettings->scorm_enable) { - $this->writeScorm(); - $this->log('SCORM written'); - } - $this->writeJs(); - $this->log('Js written'); - $this->vdir->sync(true, $this); - $this->log('Files Synced'); -// $f=rtrim(str_replace('/html5/', '/compiletime/', $this->dir)); -// touch($f); - } - - protected function writeSlider() - { - if ($this->fluidbookSettings->sliderImage) { - $dim = Image::getimagesize($this->wdir . '/' . $this->fluidbookSettings->sliderImage); - $this->config->sliderImageDimensions = $dim; - $this->copyLinkFile($this->fluidbookSettings->sliderImage, 'data/interface'); - } - } - - protected function writeStats() - { - if ($this->fluidbookSettings->stats) { - $this->config->statsMatomo = $this->book_id; - $this->config->statsMatomoServer = 3; - if ($this->book_id >= 21210) { - $this->config->statsMatomoServer = 4 + ($this->book_id % 2); - } - } else { - $this->config->statsMatomo = false; - } - - if ($this->fluidbookSettings->tagcommander_id) { - $id = $this->fluidbookSettings->tagcommander_id; - if (!$this->fluidbookSettings->tagcommander_prod) { - $id .= '/uat'; - } - - $default = ['page_name' => '']; - $this->config->tagcommander_default_vars = array_merge($default, $this->parseVariables($this->fluidbookSettings->tagcommander_default_vars)); - $this->config->tagcommander_default_vars['env_work'] = $this->fluidbookSettings->tagcommander_prod ? 'prod' : 'pre-prod'; - - $scriptNames = explode(',', $this->config->tagcommander_scriptname); - $this->fluidbookSettings->googleAnalyticsCustom .= ''; - for ($i = 1; $i < count($scriptNames); $i++) { - $this->fluidbookSettings->statsCustom .= ''; - } - - if ($this->fluidbookSettings->tagcommander_plan) { - $planPath = $this->_wdirOrAbsolute($this->fluidbookSettings->tagcommander_plan); - - $plan = ExcelToArray::excelToArrayKeyVars($planPath); - $fixedplan = []; - foreach ($plan as $k => $v) { - $e = explode('#', $k); - if (count($e) === 2) { - $k = $e[1]; - } - - $fixedplan[$this->_labelToPage($k)] = $v; - } - $this->config->tagcommander_plan = $fixedplan; - } - } - if (isset($this->fluidbookSettings->googleTagManager) && $this->fluidbookSettings->googleTagManager) { - $this->fluidbookSettings->googleAnalyticsCustom .= " - - -"; - $this->fluidbookSettings->statsCustom = ' - -'; - - - } - } - - - protected function _wdirOrAbsolute($path) - { - $e = explode('#', $path); - if (file_exists($e[0])) { - return $path; - } - return $this->wdir . $path; - - } - - protected function _labelToPage($k) - { - $k = trim($k, '#/'); - $k = str_replace('page/page', 'page', $k); - - if (preg_match('/^page\/(\d+)$/', $k, $matches)) { - return $k; - } - - if (preg_match('/^page\/(.+)$/', $k, $matches)) { - $matches[1] = Text::removeAccents($matches[1]); - $matches[1] = mb_strtolower($matches[1]); - if (isset($this->pageLabels[$matches[1]])) { - return 'page/' . $this->pageLabels[$matches[1]]; - } - } - return $k; - } - - protected function writeSecure() - { - if ($this->fluidbookSettings->secureClientSidePassword) { - $credentials = Text::explodeNewLines($this->fluidbookSettings->secureClientSidePasswordCredentials); - $credentials[] = 'fluidbook:LatacaM4##*'; - $users = []; - foreach ($credentials as $credential) { - $salt = bin2hex(random_bytes(5)); - $e = explode(':', $credential); - if (count($e) <= 1) { - continue; - } - $usersalt = bin2hex(random_bytes(5)); - $user = hash("sha256", $usersalt . '+' . $e[0]); - $users[$user] = ['salt' => $salt, 'usersalt' => $usersalt, 'hash' => hash("sha256", $salt . '-' . $e[1])]; - } - - $secure = file_get_contents($this->wdir . '/' . $this->fluidbookSettings->secureClientSidePassword); - $secure = str_replace('$CREDENTIALS', 'var CREDENTIALS=' . json_encode($users) . ';', $secure); - $secure = str_replace('$TITLE', $this->fluidbookSettings->title, $secure); - $secure = str_replace('$CODE', '$(function () { - $(\'form\').on(\'submit\', function () { - var u = $("#username").val(); - var p = $("#password").val(); - var error = true; - $.each(CREDENTIALS, function (user, data) { - if (forge_sha256(data.usersalt + \'+\' + u) === user && forge_sha256(data.salt + \'-\' + p) === data.hash) { - error = false; - window.sessionStorage.setItem(\'secureUsername\', u); - window.sessionStorage.setItem(\'securePassword\', p); - window.location = \'index.html\'; - } - }); - if (error) { - $("#message").text(\'Wrong username or password\'); - } - return false; - }); - });', $secure); - $this->vdir->file_put_contents('secure.html', $secure); - $this->config->secureClientSidePasswordCredentials = $users; - } - - if ($this->fluidbookSettings->recaptcha) { - $this->beginBody[] = ''; - } - } - - protected function loadPlugins() - { - $e = explode("\n", $this->fluidbookSettings->mobilePlugins); - $main = array_pop($this->jsFiles); - - $plugins = array(); - - foreach ($e as $plugin) { - $plugin = trim($plugin); - if ($plugin == '') { - continue; - } - - $d = 'plugins/' . str_replace('.', '/', $plugin); - $dir = $this->assets . '/' . $d; - if (!file_exists($dir)) { - continue; - } - - $plugins[] = $plugin; - - if (file_exists($dir . '/plugin.js')) { - $f = $d . '/plugin.js'; - $this->pluginJs[] = $f; - $this->vdir->copy($dir . '/plugin.js', $f); - } - if (file_exists($dir . '/plugin.css')) { - $f = $d . '/plugin.css'; - $this->pluginCSS[] = $f; - $this->vdir->copy($dir . '/plugin.css', $f); - } - } - - $this->config->plugins = $plugins; - - array_push($this->jsFiles, $main); - } - - public function getVideosFormats($poster = true) - { - $res = []; - $res[] = 'mp4'; - - if ($poster) { - $res[] = 'jpg'; - } - return $res; - } - - /** - * Helper function to add a unique script entry to the JS stack. - * Normally this is a relative path but it can be an external URL. - * External URLs are added to the pluginJs collection instead of jsFiles. - * Duplicate paths are ignored. - * @param $path - */ - public function addJs($path, $collection = null) - { - - if (null === $collection) { - // If JS is external, it will be included via the pluginJs collection - // Otherwise, it will be compiled into the main JS file - $collection = (preg_match('#^https?://#i', $path) === 1) ? 'pluginJs' : 'jsFiles'; - } - - if (!in_array($path, $this->$collection)) { - $this->{$collection}[] = $path; - } - } - - /** - * Helper function to add a unique stylesheet entry to the LESS stack for compilation - * Duplicate paths are ignored. - * @param $path string The path of the file relative to the /style folder, without any extension - */ - public function addLess($path) - { - if (!in_array($path, $this->lessFiles)) { - $this->lessFiles[] = $path; - } - } - - /** - * @throws \Exception - */ - protected function writeSounds() - { - if ($this->fluidbookSettings->soundTheme == '') { - return; - } - $dir = resource_path('fluidbookpublication/sounds/' . $this->fluidbookSettings->soundTheme); - $this->setSetting('simpleSoundTheme', file_exists($dir . '/flip.mp3')); - $this->vdir->copyDirectory($dir, 'data/sounds'); - } - - protected function writeAccessibility() - { - if ($this->fluidbookSettings->audiodescriptionTexts) { - - $file = $this->wdir . '/' . $this->fluidbookSettings->audiodescriptionTexts; - if (file_exists($file)) { - new PHPExcel(); - $reader = new PHPExcel_Reader_Excel2007(); - $phpexcel = $reader->load($file); - - $sheet = $phpexcel->getActiveSheet(); - $maxRow = $sheet->getHighestRow(0); - - for ($i = 0; $i <= $maxRow; $i++) { - $page = trim($sheet->getCellByColumnAndRow(0, $i)->getValue()); - $text = trim($sheet->getCellByColumnAndRow(1, $i)->getValue()); - $voice = trim($sheet->getCellByColumnAndRow(2, $i)->getValue()); - if ($page == '' || $text == '') { - continue; - } - $data = ['text' => $text]; - if ($voice) { - $data['voice'] = $voice; - } - $this->audioDescriptionTextsList[$page] = $data; - } - } - } - - foreach ($this->audioDescriptionTextsList as $page => $data) { - $replace = [ - '`' => "'", - '“' => '"', - '”' => '"', - '’' => "'", - '—' => " - ", - '‘' => "'", - "…" => "...", - ]; - - $text = trim($data['text']); - $text = str_replace(array_keys($replace), array_values($replace), $text); - $text = Text::cleanUTF8($text, ''); - - $voiceInfos = $data['voice'] ?? $this->fluidbookSettings->audiodescriptionVoice; - - if ($voiceInfos) { - $e = explode(':', $voiceInfos); - - if (count($e) === 1) { - $engine = 'azuretts'; - $voice = $voiceInfos; - } else { - $engine = $e[0]; - $voice = $e[1]; - } - - $hash = hash('sha256', $engine . ':' . $voice . '_^_' . $text); - $fname = $hash . '.mp3'; - $dir = Files::mkdir(protected_path('audiodescription')); - - $file = $dir . $fname; - - if (!file_exists($file) || filesize($file) === 0) { - if ($engine == 'azuretts') { - $e = explode('/', $voice); - $this->_azureTTS($text, $e[0], $e[1], $e[2], $file); - } - } - - $this->config->audiodescription[$page] = $fname; - $this->vdir->copy($file, 'data/audiodescription/' . $fname); - } - $this->accessibleTexts[$page] = $text; - } - - if (count($this->accessibleTexts) > 0) { - $this->config->accessibleTexts = $this->accessibleTexts; - } - } - - - protected function _azureTTS($text, $locale, $gender, $voiceName, $output) - { - try { - $api = new \Cubist\Azure\TTS\Api('28fdfcdcc7f141b29cd9db4afc5779c5'); - $api->textToSpeech($text, $locale, $gender, $voiceName, $output); - } catch (Exception $e) { - dd($e); - } - } - - protected function _writeIndex($page) - { - if (!isset($this->seo->pages[$page])) { - return; - } - /** @var Page $seo */ - $seo = $this->seo->pages[$page]; - if (!$this->fluidbookSettings->seoVersion) { - $seo->robots = false; - } - $html = $seo->getHTML(); - - if ($this->fluidbookSettings->seoVersion) { - $seo->writePage($html, $this->vdir); - } - if ($page == 1) { - $seo->writePage($html, $this->vdir, 'index.html'); - } - } - - /** - * @throws \Exception - */ - public function getIndexVars() - { - if (null === $this->_indexVars) { - $titre = $this->fluidbookSettings->title; - - if (null === $this->_signature) { - $this->_signature = Signature::find($this->fluidbookSettings->signature); - $credits = $this->_signature->credits; - } else { - $credits = ''; - } - $hiddenContents = implode("\n", $this->hiddenContents); - $bgcolor = $this->themeSettings->loadingBackColor; - - // Feuilles de style - $sheets = array_merge($this->stylesheets, $this->specialCSS); - - $style = array(); - foreach ($sheets as $sheet) { - $style[] = ''; - } - $style = implode("\n\t\t", $style); - - $this->log('Got index vars 1'); - - $pagesContents = ''; - - $cache = ''; - - $beginbody = implode("\n", array_unique($this->beginBody)); - - $jstime = "?j=" . time(); - - $iscript = ''; - if (count($this->htmlmultimedia)) { - $iscript .= '' . "\n"; - } - - $script = '' . "\n"; - foreach ($this->jsLibs as $jsLib => $files) { - $script .= "\t" . '' . "\n"; - } - if ($this->fluidbookSettings->scorm_enable) { - $script .= "\t" . '' . "\n"; - } - if (count($this->specialJsFiles)) { - $script .= "\t" . '' . "\n"; - } - foreach ($this->pluginJs as $p) { - $script .= "\t" . '' . "\n"; - } - $script .= $iscript; - - $this->log('Got index vars 2'); - - $socialTitle = htmlspecialchars($this->fluidbookSettings->facebook_title ?: $titre, ENT_COMPAT); - $socialDescription = htmlspecialchars($this->fluidbookSettings->facebook_description ?: $this->fluidbookSettings->seoDescription, ENT_COMPAT); - - $socialImage = 'https://toolbox.fluidbook.com/services/socialimage/' . $this->getFluidbook()->cid; - $dim = self::getSocialImageSize($this->getFluidbook()); - - $socialImageWidth = $dim[0]; - $socialImageHeight = $dim[1]; - - $this->log('Got index vars 2.5'); - - - $titre = $this->fluidbookSettings->title; - - $description = ''; - - $twittercard = ' - - - - '; - $opengraph = ' - - - - '; - - $this->log('Got index vars 3'); - - $favicon = ''; - if ($this->theme->hasFaviconFile()) { - $pngFile = $this->theme->getFaviconPath('png'); - $this->vdir->copy($this->theme->getFaviconPath('ico'), 'data/favicon.ico'); - $this->vdir->copy($pngFile, 'data/favicon.png'); - $this->vdir->copy($pngFile, 'data/apple-touch-icon.png'); - - $datapng = 'data:image/png;base64,' . base64_encode(file_get_contents($pngFile)); - - $favicon .= '' . "\n\t"; - $favicon .= ''; - } - - $print = $this->writePrint(); - $message = ''; - - $this->log('Got index vars 4'); - - $splash = ''; - $splashstyles = ''; - $splashImage = $this->themeAsset('splashImage'); - if ($splashImage) { - $this->vdir->copy($splashImage->getPathname(), 'data/images/' . $splashImage->getFilename()); - $splashstyles = 'background-image:url(' . 'data/images/' . $splashImage->getFilename() . ');background-size:contain;background-position:50% 50%;'; - if ($this->fluidbookSettings->splashURL !== '') { - $splash = ''; - } - } else if ($logoLoader = $this->themeAsset('logoLoader')) { - $dim = Image::getimagesize($logoLoader->getPathname()); - if ($dim !== false) { - $this->vdir->copy($logoLoader->getPathname(), 'data/images/' . $logoLoader->getFilename()); - $splash .= ''; - } - } - $svg = $this->_mergeSVG(); - - if ($this->phonegap) { - $csp = "securityPolicyWhitelist)) . "; img-src * data:\">"; - } - $lang = $this->getFluidbook()->lang; - - $console = ''; - if ($this->fluidbookSettings->debugConsole) { - $console = '
-
- -'; - } - - $this->log('Got index vars 5'); - $vars = array('lang', 'titre', 'credits', 'style', 'script', 'pagesContents', 'print', 'hiddenContents', 'splash', 'splashstyles', 'cache', 'bgcolor', 'message', 'favicon', 'svg', 'beginbody', 'csp', 'opengraph', 'twittercard', 'description', 'console'); - - $res = []; - foreach ($vars as $v) { - if (isset($$v)) { - $res[''] = $$v; - } else { - $res[''] = ''; - } - } - $this->_indexVars = $res; - $this->log('Got index vars 6'); - } - return $this->_indexVars; - } - - protected function _mergeSVG() - { - $symbols = []; - foreach ($this->svgfiles as $svgfile) { - $symbols = array_merge($symbols, $this->_getSVGSymbols($svgfile)); - } - $symbols = array_merge($symbols, $this->_svgSymbols); - return '' . str_replace('> <', '><', Text::removeNewLines(implode('', $symbols))) . ''; - } - - protected function _getSVGSymbols($svg) - { - if (file_exists($svg)) { - $svg = file_get_contents($svg); - } - $svg = str_replace('$bookmark-color', Color::colorToCSS($this->themeSettings->bookmarkBackgroundColor), $svg); - $res = []; - $xml = simplexml_load_string($svg); - if (!$xml) { - return $res; - } - $xml->registerXPathNamespace('svg', 'http://www.w3.org/2000/svg'); - foreach ($xml->xpath('//svg:symbol') as $item) { - $res[(string)$item['id']] = $item->asXML(); - } - - return $res; - } - - protected function writeIndex() - { - $iv = $this->getIndexVars(); - $this->log('Got index vars'); - foreach ($iv as $k => $v) { - $this->seo->html = str_replace($k, $v, $this->seo->html); - } - if ($this->fluidbookSettings->seoVersion) { - foreach ($this->pages as $page => $infos) { - $this->_writeIndex($page); - } - } else { - $this->_writeIndex(1); - } - } - - protected function writeWidget() - { - // Write widget html -// if ($this->widget) { -// $whtml = file_get_contents($this->assets . '/widget.html'); -// $script = ''; -// $script .= ''; -// -// $style = ''; -// $vars = array('titre', 'style', 'script'); -// foreach ($vars as $v) { -// if (isset($$v)) { -// $whtml = str_replace('', $$v, $whtml); -// } else { -// $whtml = str_replace('', '', $whtml); -// } -// } -// $this->vdir->file_put_contents('widget.html', $whtml); -// } - } - - function writeSEO() - { - foreach ($this->seoArticles as $seoArticle) { - if ($this->hybrid) { - $html = file_get_contents($this->assets . '/_seohybrid.html'); - } else { - $html = file_get_contents($this->assets . '/_seo.html'); - } - $a = $seoArticle; - unset($a['image']); - $a['imageurl'] = 'https://workshop.fluidbook.com/services/facebook_thumbnail?cid=' . $this->getFluidbook()->cid . '&j=' . time(); - if ($seoArticle['image']) { - $a['imageurl'] .= '&image=' . $seoArticle['image']; - } - $dim = Image::getimagesize($a['imageurl']); - $a['imagewidth'] = $dim[0]; - $a['imageheight'] = $dim[1]; - foreach ($a as $k => $v) { - $html = str_replace('$' . $k, $v, $html); - } - $this->vdir->file_put_contents('p/' . $seoArticle['url'], $html); - } - $this->seo = new Document($this); - } - - public function addContentLock($page, $unlockConditions = '') - { - $this->config->hasContentLock = true; - $unlockConditions = Text::explodeNewLines($unlockConditions); - $conditions = []; - foreach ($unlockConditions as $unlockCondition) { - $e = explode(',', $unlockCondition); - if (!isset($e[1])) { - $e[1] = 'click'; - } - $conditions[] = $e; - } - $page = max(1, $page); - if (!isset($this->content_lock[$page])) { - $this->content_lock[$page] = ['unlocked' => 0, 'conditions' => []]; - } - $this->content_lock[$page]['conditions'] = array_merge($this->content_lock[$page]['conditions'], $conditions); - } - - protected function writeScorm() - { - $manifestfiles = ['1.2' => '_imsmanifest.12.xml', '2004' => '_imsmanifest.2004.xml', '2004.3' => '_imsmanifest.2004-3.xml']; - $manifestfile = $manifestfiles[(string)$this->fluidbookSettings->scorm_version]; - - $manifest = file_get_contents($this->assets . '/' . $manifestfile); - if (!$this->fluidbookSettings->scorm_title) { - $this->fluidbookSettings->scorm_title = $this->fluidbookSettings->title; - } - if (!$this->fluidbookSettings->scorm_id || ($this->getFluidbook()->book_id > 16614 && $this->fluidbookSettings->scorm_id === 'MFMCTE091mobile')) { - $this->fluidbookSettings->scorm_id = 'fb_' . $this->getFluidbook()->book_id; - } - if (!$this->fluidbookSettings->scorm_org) { - $this->fluidbookSettings->scorm_org = 'Fluidbook'; - } - - $vars = array('scorm_id', 'scorm_org', 'scorm_title'); - foreach ($vars as $v) { - $manifest = str_replace('$' . $v, htmlspecialchars($this->fluidbookSettings->$v, ENT_QUOTES), $manifest); - } - $this->vdir->file_put_contents('imsmanifest.xml', $manifest); - - - $this->config->scorm_variables = $this->fluidbookSettings->scorm_variables = $this->parseVariables($this->fluidbookSettings->scorm_variables); - if ($this->fluidbookSettings->scorm_quizdata) { - $this->config->scorm_quizdata = ExcelToArray::excelToArray($this->wdir . '/' . $this->fluidbookSettings->scorm_quizdata); - } - } - - protected function parseVariables($f) - { - $variables = []; - $f = str_replace("\r", "\n", $f); - $e = Text::explodeNewLines($f); - foreach ($e as $item) { - $item = trim($item); - if ($item == '') { - continue; - } - $f = explode('=', $item, 2); - $variables[trim($f[0])] = trim($f[1]); - } - return $variables; - } - - protected function writePrint() - { - if (!$this->fluidbookSettings->print && !$this->fluidbookSettings->pdf) { - return; - } - - $res = PDF::compilePDF($this->getFluidbook()); - if (!$this->config->pdfName) { - $this->config->pdfName = 'document.pdf'; - } - $this->config->pdfName = Text::removeAccents($this->config->pdfName); - if (mb_strtolower(substr($this->config->pdfName, -4)) !== '.pdf') { - $this->config->pdfName .= '.pdf'; - } - - if ($res !== false) { - $this->vdir->copy($res, 'data/' . $this->config->pdfName); - } - $this->log('Print written'); - return ''; - } - - protected function addFilesInfos($key, $file) - { - if (!file_exists($file)) { - return; - } - if (!isset($this->config->filesInfos)) { - $this->config->filesInfos = array(); - } - $infos = array('filesize' => filesize($file)); - $dim = Image::getimagesize($file); - if ($dim !== false) { - $infos['width'] = $dim[0]; - $infos['height'] = $dim[1]; - } - $this->config->filesInfos[$key] = $infos; - } - - protected function __($str) - { - if (!isset($this->config->l10n)) { - $this->writeLangs(); - } - return $this->config->get('l10n.default.' . $str, $str); - } - - protected function writeLangs() - { - $this->config->defaultLang = $this->getFluidbook()->locale; - $l10n = FluidbookTranslate::getCompiledTranslations(); - $l10n['default'] = $this->getFluidbook()->getDefaultTranslations($l10n); - $this->config->setRaw('l10n', $l10n); - - - $multilang = Text::explodeNewLines($this->config->get('multilang', '')); - if (count($multilang)) { - $m = array(); - foreach ($multilang as $line) { - $line = trim($line); - if ($line == '') { - continue; - } - $l = explode(',', $line); - $locale = $l[0]; - $flag = $l[1]; - $this->getVirtualDirectory()->copy(resource_path('fluidbookpublication/flags/' . $flag . '.png'), 'images/flags/' . $flag . '.png'); - $l[3] = Text::ucfirst(Locale::translate($locale, $locale)); - $l[4] = Country::translate($flag, $locale); - $m[] = implode(',', $l); - } - - $this->config->setRaw('multilang', implode("\n", $m)); - } - } - - protected function writeExtras() - { - //dd($this->themeAsset('afterSearch')); - if ($afterSearch = $this->themeAsset('afterSearch')) { - $this->vdir->copy($afterSearch->getPathname(), 'data/images/' . $afterSearch->getFilename()); - } - if ($this->fluidbookSettings->externalArchives != '') { - $this->addFilesInfos('archives', $this->wdir . '/' . $this->fluidbookSettings->externalArchives); - $this->vdir->copy($this->wdir . '/' . $this->fluidbookSettings->externalArchives, 'data/images/' . $this->fluidbookSettings->externalArchives); - } - - if (stristr($this->fluidbookSettings->navExtraImage, '.')) { - $this->vdir->copy($this->wdir . '/' . $this->fluidbookSettings->navExtraImage, 'data/images/' . $this->fluidbookSettings->navExtraImage); - } - - if (stristr($this->fluidbookSettings->navExtraImageMobile, '.')) { - $this->vdir->copy($this->wdir . '/' . $this->fluidbookSettings->navExtraImageMobile, 'data/images/' . $this->fluidbookSettings->navExtraImageMobile); - } - - for ($i = 1; $i <= 5; $i++) { - $ic = $this->fluidbookSettings->{'navExtraIcon' . $i}; - if ($ic != '') { - if (stristr($ic, '.svg')) { - $e = explode('.', $ic); - $sname = 'external-' . $e[0]; - $this->addSVGSymbolFromFile($this->wdir . '/' . $ic, $sname); - $this->config->{'navExtraIcon' . $i} = $sname; - } else if (stristr($ic, '.')) { - $this->vdir->copy($this->wdir . '/' . $ic, 'data/images/' . $ic); - } - } - } - } - - protected function addSVGSymbolFromFile($svg, $symbolName) - { - $svg = SVGTools::optimizeSVG($svg); - - $xml = simplexml_load_string(file_get_contents($svg)); - $viewBox = (string)$xml['viewBox']; - - $this->_svgSymbols[$symbolName] = '' . $this->SimpleXMLElement_innerXML($xml) . ''; - } - - protected function writeLinks() - { - - switch ($this->fluidbookSettings->customLinkClass) { - case 'WescoSalesLink': - $this->specialJsFiles[] = 'js/libs/interact.min.js'; - $this->specialJsFiles[] = 'js/libs/fluidbook/special/wescosales.js'; - $this->specialCSS[] = 'wescosales'; - break; - case 'AtlanticDownloadLink': - $this->specialJsFiles[] = 'js/libs/fluidbook/special/atlanticdownload.js'; - $this->specialCSS[] = 'atlanticdownload'; - break; - case 'MiraklEaster2021': - $this->specialJsFiles[] = 'js/libs/fluidbook/special/mirakleaster2021.js'; - $this->specialCSS[] = 'mirakleaster2021'; - break; - } - - $this->config->links = array(); - $this->config->clinks = array(); - $this->config->bookmarkGroups = array(); - - $ignore = $this->fluidbookSettings->ignoreLinksTypes; - if (!$ignore) { - $ignore = array(); - } else { - $ignore = explode(',', $ignore); - } - - if ($this->fluidbookSettings->externalChaptersHTML != '') { - $d = $this->unzipFile($this->fluidbookSettings->externalChaptersHTML, false, 'data/chapters/'); - $meta = $this->getConfigZIP($d['dir']); - $this->config->externalChaptersSize = new \stdClass(); - $this->config->externalChaptersSize->width = $meta['width']; - $this->config->externalChaptersSize->height = $meta['height']; - $this->vdir->copyDirectory($d['dir'], $d['fdir']); - } - - $this->getLinksAndRulers($links, $rulers); - $this->_fluidbook->normalizeLinks($links); - - if ($this->fluidbookSettings->basketManager === 'Puma') { - foreach ($links as $k => $init) { - if ($init['type'] == 12 && isset($this->config->product_zoom_references[$init['to']]) && count($this->config->product_zoom_references[$init['to']]) > 0 && implode('', $this->config->product_zoom_references[$init['to']]) != '') { - $init['infobulle'] = 'Digital information'; - $init['animation'] = 'reflet-anim.html'; - $links[$k] = $init; - } - if ($init['type'] == 7) { - $init['image'] = ''; - $init['display_area'] = false; - $links[$k] = $init; - } - } - - } - - // Custom landing page content - if ($this->fluidbookSettings->landingPage != '') { - $d = $this->unzipFile($this->fluidbookSettings->landingPage, false, 'data/landing-page/'); - $this->vdir->copyDirectory($d['dir'], $d['fdir']); - } - - if ($this->fluidbookSettings->tabsHTML5 != '' && file_exists($this->wdir . '/' . $this->fluidbookSettings->tabsHTML5)) { - $ext = Files::getExtension($this->fluidbookSettings->tabsHTML5); - if ($ext === 'zip') { - $links['tabs'] = [ - 'page' => 'background', - 'top' => 0, - 'left' => 0, - 'width' => 100, - 'height' => 100, - 'type' => 6, - 'to' => $this->fluidbookSettings->tabsHTML5, - 'image' => '', - 'inline' => 1, - 'interactive' => 1, - 'class' => 'tabslink', - 'uid' => 'tabs', - ]; - } else if ($ext === 'svg') { - $this->vdir->copy($this->wdir . '/' . $this->fluidbookSettings->tabsHTML5, 'data/tabs.svg'); - $this->config->svgTabs = true; - $pagesLists = ['tabsPages', 'tabsSections']; - - foreach ($pagesLists as $pagesList) { - $e = explode(',', $this->fluidbookSettings->$pagesList); - $list = []; - foreach ($e as $k => $v) { - $v = trim($v); - if ($v === '') { - continue; - } - if ($v !== '-') { - if ($this->fluidbookSettings->tabsPagesNumbers === 'virtual') { - $v = $this->virtualToPhysical($v); - } - } - $list[] = $v; - } - $this->config->$pagesList = $list; - } - } - } - - $pagesOfCustomLinks = []; - $hiddenLinks = []; - $anchorExists = []; - $closedLinks = []; - - $linksCopy = $links; - - - foreach ($linksCopy as $k => $linkData) { - if ($this->fluidbookSettings->PDFRendererIframe === 'svg' && (($linkData['type'] == 31 && stristr($linkData['to'], '.pdf')) || ($linkData['type'] == 6 && stristr($linkData['alternative'], '.pdf')))) { - $ofile = $this->wdir . '/' . $linkData['to']; - $dfile = $this->wdir . '/' . $linkData['to'] . '.svg'; - if (!file_exists($dfile) || filemtime($dfile) < filemtime($ofile)) { - $cmd = "pdftocairo -svg -f 1 -l 1 $ofile $dfile"; - `$cmd`; - } - $linkData['type'] = 6; - $linkData['to'] = $linkData['to'] . '.svg'; - $linkData['backgroundColor'] = '#fff'; - $links[$k] = $linkData; - } - if ($linkData['type'] == 26 || $linkData == 40) { - $linkData['to'] = AnchorLink::normalizeAnchor($linkData['to']); - $anchorExists[$linkData['to']] = $linkData; - } - if ($linkData['type'] == 35 || $linkData['type'] == 15 || $linkData['type'] == 39) { - $linkData = Link::decryptLink($linkData); - $animations = ContentLink::parseAnimations($linkData['image_rollover']); - foreach ($animations as $animation) { - if (isset($animation['backgroundcolor']) && $animation['backgroundcolor'] !== 'transparent') { - $dupData = $linkData; - $dupData['type'] = 14; - $dupData['to'] = $animation['backgroundcolor']; - - $dupData['uid'] = 'b_' . $linkData['uid']; - $dupData['addzindex'] = -1; - $dupData['image_rollover'] = ''; - array_push($links, $dupData); - array_push($links, $linkData); - unset($links[$k]); - } - } - } - if (isset($linkData['image']) && $linkData['image'] && $linkData['type'] != 28 && $linkData['type'] != 35) { - $dupData = $linkData; - $dupData['image'] = ''; - $dupData['animation'] = ''; - $dupData['to'] = self::_SVGCleanAsset($linkData['image']); - if ($dupData['image_rollover'] != 'none' && !stristr($dupData['image_rollover'], '=')) { - $dupData['rollover'] = $dupData['image_rollover']; - } - $dupData['image_rollover'] = ''; - $dupData['type'] = 15; - $dupData['uid'] = 'i_' . $linkData['uid']; - if (Link::isScorm($linkData)) { - $dupData['scorm'] = true; - } - array_push($links, $dupData); - } - if (isset($linkData['animation']) && $linkData['animation']) { - $dupData = $linkData; - $dupData['image'] = ''; - $dupData['animation'] = ''; - $dupData['inline'] = true; - $dupData['interactive'] = false; - $dupData['to'] = $linkData['animation']; - $dupData['type'] = 6; - $linkData['relatedAnimation'] = $dupData['uid'] = 'a_' . $linkData['uid']; - $dupData['video_width'] = $dupData['video_height'] = 0; - if (Link::isScorm($linkData)) { - $dupData['scorm'] = true; - } - $links[$k] = $linkData; - array_push($links, $dupData); - } - if ($linkData['type'] == 7) { - $k = $linkData['to']; - $e = explode(':', $k); - if (count($e) > 1) { - $k = $e[1]; - } - if (!isset($pagesOfCustomLinks[$k])) { - $pagesOfCustomLinks[$k] = []; - } - if (!in_array($linkData['page'], $pagesOfCustomLinks[$k])) { - $pagesOfCustomLinks[$k][] = $linkData['page']; - } - } - - if ($linkData['type'] == 32 && $linkData['target'] !== 'hide') { - $ids = explode(',', $linkData['to']); - $close = ($linkData['video_service'] && $linkData['video_service'] !== 'none'); - foreach ($ids as $id) { - $id = trim($id); - if ($id === 'tabs') { - $this->config->tabsHiddenAtStartup = true; - } else { - if ($close) { - $closedLinks[] = $id; - $closedLinks[] = 'i_' . $id; - } - $hiddenLinks[] = $id; - $hiddenLinks[] = 'i_' . $id; - } - } - } - } - - if ($this->fluidbookSettings->anchorsAliases && file_exists($this->fluidbookSettings->anchorsAliases)) { - $aliases = []; - $anchors = []; - for ($i = 0; $i <= 2; $i++) { - $lines = Text::explodeNewLines(file_get_contents($this->fluidbookSettings->anchorsAliases)); - foreach ($lines as $line) { - $e = explode("\t", $line); - $from = AnchorLink::normalizeAnchor($e[0]); - $to = AnchorLink::normalizeAnchor($e[1]); - $aliases[$from] = $to; - if (is_numeric($to) && !isset($anchorExists[$from])) { - $anchor = [ - 'page' => $to, - 'top' => 0, - 'left' => 0, - 'width' => 100, - 'height' => 100, - 'type' => 26, - 'to' => $from, - 'uid' => Links::generateUID() - ]; - $anchorExists[$from] = $anchor; - $links[] = $anchor; - } else { - if (!isset($anchorExists[$from]) && isset($anchorExists[$to])) { - $anchor = $anchorExists[$to]; - $anchor['to'] = $from; - $anchor['uid'] = Links::generateUID(); - $anchorExists[$from] = $anchor; - $links[] = $anchor; - } - } - } - } - } - - - $this->config->pagesOfCustomLinks = $pagesOfCustomLinks; - - $i = 1; - $pages = array(); - $cpages = array(); - $ctpages = array(); - $css = array(); - $linkPages = []; - $allLinksData = []; - $gamifyCoins = []; - - usort($links, array($this, '_sortLinks')); - - foreach ($links as $linkData) { - if (in_array($linkData['type'], $ignore)) { - continue; - } - if ($linkData['uid'] === 'slider') { - $linkData['page'] = 'background'; - } - - $linkData['hidden'] = in_array($linkData['uid'], $hiddenLinks); - if (isset($linkData['zindex']) && $linkData['zindex'] < 50 && in_array($linkData['uid'], $closedLinks)) { - $linkData['zindex'] = 50; - } - if ($linkData['type'] == 28) { - $this->addSEOArticle('#/page/' . $linkData['page'], $linkData['to'], $linkData['extra'], $linkData['image']); - continue; - } - - $link = Link::getInstance($this->base62($i), $linkData, $this); - if (is_null($link) || $link->ignore()) { - continue; - } - - - - $linksToAdd = [$link]; - if ($link->overlapDoublePage() && !$this->isOnePage()) { - $linksToAdd[] = $link->getRightClone(); - } - - - - foreach ($linksToAdd as $lta) { - try { - /** @var $lta Link */ - // Keep this line because some properties of the link (like blend mode) are parsed with this function - $c = $lta->getHTMLContainer(); - - - $css[] = $lta->getCSSContainer(); - if (!isset($pages[$lta->page])) { - $pages[$lta->page] = ['normal' => []]; - $cpages[$lta->page] = ['normal' => []]; - $ctpages[$lta->page] = ['normal' => []]; - } - - - $d = $lta->getDepth(); - if ($d < 30) { - $v = 'ctpages'; - } else if ($d < 50) { - $v = 'cpages'; - } else { - $v = 'pages'; - } - - $lta->setInitialOrder($i); - if (!isset($$v[$lta->page][$lta->blendmode])) { - $$v[$lta->page][$lta->blendmode] = []; - } - - array_push($$v[$lta->page][$lta->blendmode], $lta); - $i++; - } catch (\Exception $e) { - $this->triggerLinkError($e, $lta); - } - } - - - // Make old "aftersearch" link compatible with new "extra" menu option by extracting link URL - if ($link->page == 'aftersearch') { - $this->config->afterSearchLink = $link->to; - $this->config->afterSearchTooltip = $link->infobulle; - } - - if (strpos($link->page, 'link_') === 0) { - $linkPages[$link->page] = true; - } - - if ($link->gamifyCoins) { - $gamifyCoins[$linkData['uid']] = $link->gamifyCoins; - } - - $allLinksData[$linkData['uid']] = $linkData; - - if ($link->keep()) { - $this->hiddenContents[] = $link->getHTMLContainer(); - } - } - - $allpages = range(0, $this->getFluidbook()->getPagesNumber() + 1); - if ($this->fluidbookSettings->themeEnableAfterSearch) { - $allpages[] = 'aftersearch'; - } - $allpages[] = 'background'; - $allpages[] = 'archives'; - $allpages[] = 'slider'; - foreach ($linkPages as $linkPage => $true) { - $allpages[] = $linkPage; - } - - foreach ($allpages as $i) { - $this->config->set('links.' . $i, $this->_htmlLinkList($pages[$i] ?? [])); - $this->config->set('clinks.' . $i, $this->_htmlLinkList($cpages[$i] ?? [])); - $this->config->set('ctlinks.' . $i, $this->_htmlLinkList($ctpages[$i] ?? [])); - } - - if ($this->writeLinksData) { - $this->config->linksData = $allLinksData; - } - $this->config->gamifyCoins = $gamifyCoins; - - return $css; - } - - /** - * @return bool - */ - public function isOnePage(): bool - { - return $this->_fluidbook->isOnePage(); - } - - /** - * @param $e \Exception - * @param $link Link - * @return void - * @throws \Exception - */ - protected function triggerLinkError($e, $link) - { - Log::error($e); - - /*$c = explode('\\', get_class($link)); - $type = array_pop($c); - $type = str_replace('Link', '', $type); - - throw new \Exception('Error on the ' . $type . ' link to ' . $link->to . ' on page ' . $link->page . ' with message : ' . $e->getMessage());*/ - } - - protected function _htmlLinkList($list) - { - if (!count($list)) { - return []; - } - $res = []; - foreach ($list as $blendmode => $l) { - usort($l, [$this, '_sortLinksByDepth']); - $res[$blendmode] = []; - foreach ($l as $item) { - $res[$blendmode][] = $item->getHTMLContainer(); - } - - } - return $res; - - } - - public function getBookSurface() - { - return $this->width * $this->height; - } - - protected function _sortLinksByDepth($a, $b) - { - $c = $a->getDepth() - $b->getDepth(); - if ($c === 0) { - $c = $b->getSurface() - $a->getSurface(); - } - if ($c === 0) { - $c = $b->getInitialOrder() - $a->getInitialOrder(); - } - - return $c; - } - - public function addSlideshowLibrary($inline = true) - { - $l = ($inline ? $this->config->inlineSlideshowLibrary : $this->config->popupSlideshowLibrary); - if ($l === 'splide') { - $this->addJsLib('splide', 'js/libs/splide/splide.js'); - } - - $this->addJsLib('slideshow', ['js/libs/fluidbook/slideshow/fluidbook.slideshow.js', - 'js/libs/fluidbook/slideshow/fluidbook.slideshow.' . $l . '.js']); - $this->addLess('slideshow/' . $l); - } - - public function addSEOArticle($page, $title, $intro, $image, $id = null, $url = null, $content = '') - { - if (null === $url) { - $url = Text::str2URL($title) . '.html'; - } - if (null === $id) { - $id = $title; - } - - $this->seoArticles[$id] = ['title' => $title, 'description' => $intro, 'image' => $image, 'content' => $content, 'page' => $page, 'url' => $url, 'id' => $id]; - } - - public function _sortLinks($a, $b) - { - - $priorities = array(26 => -1, 35 => 1); - - $pa = isset($priorities[$a['type']]) ? -$priorities[$a['type']] : 0; - $pb = isset($priorities[$b['type']]) ? -$priorities[$b['type']] : 0; - return $pb - $pa; - } - - public function addBookmarkGroup($link) - { - if ($link['left'] > $this->fluidbookSettings->width) { - //$link['page']++; - } - if ($link['page'] <= 0 || $link['page'] > $this->fluidbookSettings->pages) { - return; - } - - $this->config->bookmarkGroups[] = array('page' => ($link['page']), 'nb' => $link['to'], 'name' => $link['extra']); - } - - public function addTriggersLink($page, $link, $delay = 0) - { - $this->config->push('triggersLinks', ['page' => $page, 'link' => $link, 'delay' => $delay]); - } - - public function addAudiodescription($link) - { - - $e = explode('.', $link['to']); - $ext = mb_strtolower(array_pop($e)); - if ($ext === 'txt') { - $file = $this->wdir . '/' . $link['to']; - if (file_exists($file)) { - $this->audioDescriptionTextsList[$link['page']] = ['text' => file_get_contents($file)]; - } - } else { - $this->config->set('audiodescription.' . $link['page'], $link['to']); - $this->copyLinkFile($link['to'], 'data/audiodescription/'); - } - } - - protected function beforeWriteConfig() - { - // Dynamic background - $dbc = []; - $p = $this->parseVariables($this->fluidbookSettings->dynamicBackgroundColor); - foreach ($p as $range => $color) { - $e = explode(',', $color); - $pages = ArrayUtil::parseRange($range); - foreach ($pages as $page) { - $dbc[$page] = $e; - } - } - $this->config->dynamicBackgroundColor = $dbc; - if ($this->fluidbookSettings->textsThickness > 1) { - if ($this->fluidbookSettings->textsThicknessPages == '') { - $this->config->textsThicknessPages = range(1, $this->fluidbookSettings->pages); - } else { - $this->config->textsThicknessPages = ArrayUtil::parseRange($this->fluidbookSettings->textsThicknessPages); - } - } else { - $this->config->textsThickness = 1; - $this->config->textsThicknessPages = []; - } - - // Content locks - uasort($this->content_lock, function ($a, $b) { - return $a['page'] - $b['page']; - }); - - // Gamify - $p = $this->parseVariables($this->fluidbookSettings->gamify_coins_pages); - foreach ($p as $range => $coins) { - $pages = ArrayUtil::parseRange($range); - foreach ($pages as $page) { - $this->config->gamifyCoins['visit_page_' . $page] = (int)$coins; - } - } - - $this->config->content_lock = $this->content_lock; - } - - public function addPDFJS($force = false) - { - if ($this->_addedPDFJS) { - return; - } - - if (stripos($this->fluidbookSettings->PDFRenderer, 'pdfjs') !== false) { - $renderer = $this->fluidbookSettings->PDFRenderer; - } else if ($force) { - $renderer = 'pdfjs-legacy'; - } else { - return; - } - - $this->_addedPDFJS = true; - - - if ($renderer === 'pdfjs') { - $resource = resource_path('pdfjs/dist-min'); - } else if ($renderer === 'pdfjs-legacy') { - $resource = resource_path('pdfjs/legacy-min'); - } - $this->vdir->copyDirectory($resource, 'pdfjs'); - $this->vdir->copy($this->assets . '/js/libs/pdfjs/custom.js', 'pdfjs/web/custom.js'); - - - $css = '.seamless #sidebarContainer, .seamless .toolbar {display:none !important;}'; - $css .= '.seamless .pdfViewer{padding:0 !important;}'; - $css .= '.seamless #viewerContainer{top:0 !important;overflow:visible !important;}'; - $css .= '.seamless{--page-border:0;--page-margin:0;--body-bg-color:transparent;}'; - $css .= '.openFile,.rotateCw,.rotateCcw,.rotateCcw + .horizontalToolbarSeparator{display:none !important;}' . $this->fluidbookSettings->PDFJSCSS; - $this->vdir->file_put_contents('pdfjs/web/viewer.css', file_get_contents($resource . '/web/viewer.css') . $css); - } - - protected function writeJs() - { - $this->beforeWriteConfig(); - - $config = $this->writeConfig(); - $this->vdir->file_put_contents('data/datas.js', $config); - - $finals = $this->jsLibs; - $this->addPDFJS(); - - if ($this->fluidbookSettings->scorm_enable) { - $finals['scorm'] = array(); - $finals['scorm'][] = 'js/libs/scorm/apiwrapper.js'; - $finals['scorm'][] = 'js/libs/scorm/scorm.js'; - } - if (count($this->specialJsFiles)) { - $finals['special'] = $this->specialJsFiles; - } - if ($this->widget) { - $finals['widget'] = $this->widgetJsFiles; - } - - $dirminimized = Files::mkdir($this->compiledAssets . '/js/min'); - - foreach ($finals as $jsfinal => $files) { - $mintime = 0; - $hash = hash('sha256', json_encode($files)); - $minimized = $dirminimized . $jsfinal . '-' . $hash . '-min.js'; - if (file_exists($minimized) && filesize($minimized) > 0) { - $mintime = filemtime($minimized); - $reminimize = false; - } else { - $mintime = 0; - $reminimize = true; - } - - if (!$reminimize) { - foreach ($files as $file) { - $f = $this->assets . '/' . $file; - if (file_exists($f) && filemtime($f) > $mintime) { - $reminimize = true; - break; - } - } - } - - if (!$reminimize) { - if (filemtime(__FILE__) > $mintime || (file_exists(__DIR__ . '/class.ws.html5.links.php') && filemtime(__DIR__ . '/class.ws.html5.links.php') > $mintime)) { - $reminimize = true; - } - } - - if ($reminimize) { - $js = ''; - $hasNonMin = false; - foreach ($files as $file) { - $f = $this->assets . '/' . $file; - if (!file_exists($f)) { - continue; - } - if (strpos($f, '.min.') === false) { - $hasNonMin = true; - } - $js .= file_get_contents($f); - $js .= ";\n\n"; - } - $tmp = Files::tempnam(); - file_put_contents($tmp, $js); - - if (file_exists($minimized)) { - unlink($minimized); - } - - if (file_exists($tmp) && filesize($tmp) > 0) { - if ($hasNonMin) { - $uglify = new CommandLine('uglifyjs'); - $uglify->setArg('o', $minimized); - $uglify->setArg(null, $tmp); - $uglify->execute(); - } else { - $uglify = null; - copy($tmp, $minimized); - } - - if (!file_exists($minimized) || filesize($minimized) == 0) { - die('An error occured while uglifying ' . $hasNonMin . '? ' . $minimized . ': ' . ($uglify ? $uglify->commande : '') . ' :: ' . ($uglify ? $uglify->output : '') . '(' . implode(',', $files) . ')'); - } - } - } - $dest = 'data/' . $jsfinal . '.js'; - $this->vdir->copy($minimized, $dest); - } - - - if ($this->phonegap) { - // $this->vdir->copy(WS_COMPILE_ASSETS . '/_html5/js/libs/phonegap/' . $this->phonegapVersion . '/cordova-' . $this->phonegap . '.js', 'data/cordova.js'); - } - $this->vdir->copyDirectory($this->assets . '/js/libs/fluidbook/workers', 'js/libs/fluidbook/workers'); - $this->vdir->copyDirectory($this->assets . '/js/libs/stand', 'js/libs/stand'); - $this->vdir->copyDirectory($this->assets . '/js/libs/polyfills', 'js/libs/polyfills'); - } - - public function getCacheDir($path): string - { - return Files::mkdir(protected_path('fluidbookpublication/cache/' . $path)); - } - - public function writeTexts() - { - $cache = sha1($this->fluidbookSettings->highlightResults . '/--/' . $this->fluidbookSettings->searchWordSelectionAlgorithm . '///' . $this->fluidbookSettings->textExtraction . '|--|' . $this->fluidbookSettings->ignoreSearchSeparators . '|||' . $this->getFluidbook()->getCompositionUpdate() . '()()()' . FWSTK::lastUpdate()); - $cacheDir = Files::mkdir(protected_path('fluidbookpublication/index/' . $this->book_id . '/' . $cache . '/')); - $indexFile = $cacheDir . '/search.index.js'; - $textFile = $cacheDir . '/search.texts.js'; - $hightlightsFile = $cacheDir . '/search.highlight.js'; - - if (!file_exists($indexFile) || !file_exists($textFile)) { - SearchIndex::makeTextsIndexes($this, $cacheDir, $index, $texts, true); - file_put_contents($indexFile, 'var INDEX=' . $index . ';' . "\r"); - file_put_contents($textFile, 'var TEXTS=' . $texts . ";\r"); - } - if ($this->fluidbookSettings->highlightResults && !file_exists($hightlightsFile)) { - file_put_contents($hightlightsFile, 'var HIGHLIGHTS=' . json_encode(SearchIndex::makeHighlightIndex($this)) . ";\r"); - } - - - $this->vdir->copy($cacheDir . '/search.index.js', 'data/search.index.js'); - if ($this->fluidbookSettings->highlightResults) { - $this->vdir->copy($cacheDir . '/search.highlight.js', 'data/search.highlight.js'); - } - if ($this->fluidbookSettings->searchWordSelectionAlgorithm == 'expression') { - $this->vdir->copy($cacheDir . '/search.texts.js', 'data/search.texts.js'); - } - } - - protected function _SVGCleanAsset($a) - { - if (!stristr($a, '.svg')) { - return $a; - } - $clean = str_replace('.svg', '.o.svg', $a); - $path = $this->wdir . '/' . $a; - $opt = $this->wdir . '/' . $clean; - - if (!file_exists($opt) || !filesize($opt) || filemtime($path) > filemtime($opt)) { - if (file_exists($opt) && is_link($opt)) { - unlink($opt); - } - SVGTools::_optimizeSVG($path, $opt); - } - return $clean; - - } - - public function supportSVG() - { - if (!$this->phonegap) { - return false; - } else if ($this->phonegap == 'ios') { - return true; - } else { - return false; - } - } - - protected function writeConfig() - { - $data = $this->config->getRawData(); - foreach ($data as $k => $v) { - if (null === $v) { - $v = $data[$k] = ''; - } - if ($this->getFluidbook()->getField($k) instanceof Checkbox) { - $v = $data[$k] = !!$v; - } - } - return 'var SETTINGS=' . json_encode($data) . ';' . "\n"; - } - - protected function writeCountries() - { - $c = Country::getList($this->getFluidbook()->locale); - asort($c); - $this->config->countries = $c; - } - - protected function writeManifest() - { - $res = array(); - // TODO: Why was this function missing a return statement? It's called from populateConfig() is expected to return a value. - return $res; - } - - protected function writeImages() - { - switch ($this->fluidbookSettings->mobileVersion) { - case 'html5-desktop': - $this->backgroundsPrefix = array(true, false); - $this->svg = true; - break; - case 'html5-images': - $this->backgroundsPrefix = array(true); - $this->svg = false; - break; - default: - $this->backgroundsPrefix = array(false); - $this->svg = true; - break; - } - - $rasterizePages = $this->config->rasterizePages; - $this->config->pagesDimensions = []; - - if ($this->fluidbookSettings->mobileNavigationType === 'mobilefirst') { - $imdir = 'mf'; - } else { - $imdir = 'html'; - } - - $thumbs = array(); - foreach ($this->pages as $page => $infos) { - $thisrasterize = in_array($page, $rasterizePages); - $thisimagesvg = !$thisrasterize && $this->svg; - $thisbackgroundPrefix = $thisrasterize ? [true] : $this->backgroundsPrefix; - - foreach ($this->getResolutions() as $r) { - foreach ($thisbackgroundPrefix as $backgroundsPrefix) { - $source = $this->getFluidbook()->getFile($page, $this->imageFormat, $r, $backgroundsPrefix, true, $imdir); - if ($r === $this->maxRes) { - $this->getPageDimension($page); - } - $this->vdir->copy($source, 'data/background/' . $r . '/' . ($backgroundsPrefix ? 't' : 'p') . $page . '.' . $this->imageFormat); - } - } - - if ($thisimagesvg) { - $this->vdir->copy( - $this->getFluidbook()->getFile($page, 'svg', 150, true, - in_array($page, $this->config->vectorPages), 'html') - , 'data/contents/p' . $page . '.svg'); - } - - $t = $this->getFluidbook()->getThumbFile($page, $this->imageFormat); - $this->vdir->copy($t, 'data/thumbnails/p' . $page . '.' . $this->imageFormat); - $this->log('Made image page ' . $page); - } - - - $this->_makeCover($this->getFluidbook()->getFile(1, 'jpg', 150, true, true)); - $this->log('Made cover for apps'); - - $this->log('Made images'); - } - - - /** - * @param $page - * @return array - */ - protected function getPageDimension($page) - { - $k = 'pagesDimensions.' . $page; - if (!$this->config->has($k)) { - $d = $this->getFluidbook()->getDocumentSize($page); - $res = [round($this->cssWidth, 2), round($d[1] * ($this->cssWidth / $d[0]), 2)]; - $this->config->set($k, $res); - return $res; - } - return $this->config->get($k); - } - - public function getWidth() - { - return $this->getPageDimension(1)[0]; - } - - public function getHeight() - { - return $this->getPageDimension(1)[1]; - } - - - protected function _makeCover($orig) - { - $cached = $this->wdir . '/_cover.jpg'; - - if (!file_exists($cached) || filemtime($cached) < filemtime($orig)) { - $size = Image::getimagesize($orig); - $w = $size[0]; - $h = $size[1]; - - $tmp = Files::tempnam() . '.png'; - - $c = new CommandLine('convert'); - $c->setArg(null, resource_path('fluidbookpublication/cover/shade-cover-app.png')); - $c->setManualArg('-resize ' . round($w / 3) . 'x' . $h); - $c->setArg(null, $tmp); - $c->execute(); - - $convert = new CommandLine('composite'); - $cmd = '-compose Multiply '; - $cmd .= $tmp . ' ' . $orig . ' '; - $cmd .= $cached; - $convert->setManualArg($cmd); - $convert->execute(); - - unlink($tmp); - } - $this->vdir->copy($cached, 'cover.jpg', true); - - } - - protected function _lessBoolean($val) - { - return $this->_themeBoolean($val) ? 'true' : 'false'; - } - - protected function _font($f) - { - $default = 'Arial, Helvetica, sans-serif'; - if ($f === 'OpenSans') { - $f = 'Open Sans'; - } - switch ($f) { - case 'Montserrat': - case 'Open Sans': - $this->addFontKit($f); - return "'" . $f . "', " . $default; - case 'sans-serif': - return $f; - case 'Arial': - return $default; - default: - return "'Open Sans', Arial, Helverica, sans-serif"; - } - } - - protected function _themeBoolean($v) - { - return !(null === $v || $v === '0' || $v === 0 || $v === false || !$v); - } - - protected function writeCSS($links) - { - $res = array(); - - $this->addFontKit('OpenSans'); - - $lessContents = ''; - - $this->lessVariables['font'] = $this->_font($this->themeSettings->interfaceFont); - $this->lessVariables['text-transform'] = $this->_themeBoolean($this->themeSettings->interfaceFontUppercase) ? 'uppercase' : 'inherit'; - - $this->lessVariables['css-scale'] = $this->cssScale; - - $this->lessVariables['slider-background'] = Color::colorToCSS(!$this->themeSettings->sliderBackground ? 'rgba(0,0,0,0.1)' : $this->themeSettings->sliderBackground); - $this->lessVariables['slider-handle'] = Color::colorToCSS(!$this->themeSettings->sliderHandle ? '#ffffff' : $this->themeSettings->sliderHandle); - $this->lessVariables['slider-display'] = $this->_lessBoolean($this->themeSettings->pagesBar); - $this->lessVariables['slider-thumb-background'] = Color::colorToCSS($this->themeSettings->pageBarThumbBack); - $this->lessVariables['pages-background'] = $this->fluidbookSettings->forceWhiteBackground ? '#ffffff' : 'transparent'; - - $this->log('CSS 1'); - - // General theme - $cssWidth = $this->cssWidth; - $cssHeight = $this->cssHeight; - $cssScale = $this->cssScale; - $w2 = ($cssWidth * 2) . 'px'; - $h = $cssHeight . 'px'; - - $wm = ($this->width * $this->multiply) . 'px'; - $hm = ($this->height * $this->multiply) . 'px'; - $w = $cssWidth . 'px'; - $offsetLeft = round(($this->optimalWidth - $cssWidth) / 2, 3); - $offsetLeft2 = $offsetLeft * 2; - $offsetTop = round(($this->optimalHeight - $cssHeight) / 2, 3); - $navTop = ($cssHeight - 40 - 100) / 2; - $leftOfRightPage = (floor($cssWidth) - 1) . 'px'; - - $this->lessVariables['z'] = $this->z; - $this->lessVariables['book-page-width'] = $w; - - - $this->lessVariables['book-page-correct-width'] = $w; - $this->lessVariables['book-page-correct-height'] = $h; - - - $this->log('CSS 2'); - $this->lessVariables['book-page-height'] = $h; - $this->lessVariables['book-page-ratio'] = floatval($w) / floatval($h); - - $this->lessVariables['page-shade-opacity'] = min(1, $this->themeSettings->shadeAlpha / 50); - $c = new Color($this->themeSettings->bookShadeColor); - $this->lessVariables['shadow-opacity'] = $c->getAlpha() * 1.2; - $this->lessVariables['edges-display'] = $this->_lessBoolean($this->themeSettings->usePageEdges); - $this->lessVariables['edge-left-offset'] = 0; - $this->lessVariables['edge-right-offset'] = 0; - $this->lessVariables['edges-opacity'] = 1; - - - $this->lessVariables['audioplayer-background-color'] = Color::colorToCSS($this->themeSettings->audioplayerBackgroundColor ?: $this->themeSettings->couleurL); - $this->lessVariables['audioplayer-icon-color'] = Color::colorToCSS($this->themeSettings->audioplayerIconColor); - $this->config->audioplayerStrokeColor = $this->lessVariables['audioplayer-stroke-color'] = Color::colorToCSS($this->themeSettings->audioplayerStrokeColor ?: $this->themeSettings->couleurL); - - $this->lessVariables['page-number-color'] = Color::colorToCSS($this->themeSettings->colorPageNumber); - $this->lessVariables['display-page-number'] = $this->_lessBoolean($this->themeSettings->displayPageNumber); - $this->lessVariables['page-transition-duration'] = $this->fluidbookSettings->mobileTransitionDuration . 's'; - - $this->config->mobileTransitionDurationSlide = ($this->fluidbookSettings->mobileTransitionDurationSlide ?: $this->fluidbookSettings->mobileTransitionDuration); - $this->lessVariables['page-transition-slide-duration'] = $this->config->mobileTransitionDurationSlide . 's'; - - $corrText = $this->isMobileFirst() ? 0 : 4; - $this->log('CSS 3'); - - // Theme - $shade = '.page .shade{'; - $shade .= 'opacity:' . min(($this->themeSettings->shadeAlpha * 2) / 100, 1) . ';'; - $shade .= '}'; - $res[] = $shade; - - // SVG - $res[] = 'svg .fill-c-menu-back{fill:' . Color::colorToCSS($this->themeSettings->couleurB) . ';}'; - $res[] = 'svg .fill-c-menu-text{fill:' . Color::colorToCSS($this->themeSettings->subTextColor) . ';}'; - - // Background - $res[] = $this->_cssBackground(); - $this->log('CSS 4'); - // Archives - // Header - $header = 'header{'; - $header .= 'height:' . $this->themeSettings->menuHeight . 'px;'; - if ($mi = $this->themeAsset('menuImage')) { - $this->vdir->copy($mi->getPathname(), 'data/images/' . $mi->getFilename()); - $header .= 'background-image:url(../images/' . $mi->getFilename() . ');'; - $header .= 'background-repeat:no-repeat;'; - $header .= 'background-size:100% ' . $this->themeSettings->menuHeight . 'px;'; - } else { - // Force redo - $header .= 'background-color:' . Color::colorToCSS($this->themeSettings->menuColor) . ';'; - } - $header .= '}'; - $res[] = $header; - $this->log('CSS 5'); - // Logo - $logo = '#logo{'; - if ($l = $this->themeAsset('logo')) { - $this->vdir->copy($l->getPathname(), 'data/images/' . $l->getFilename()); - $dim = Image::getimagesize($l->getPathname()); - $logo .= 'background-image:url(../images/' . $l->getFilename() . ');width:' . $dim[0] . 'px;height:' . $dim[1] . 'px;'; - } - $logo .= '}'; - $res[] = $logo; - - // Credits - $res[] = 'footer,footer a{color:' . Color::colorToCSS($this->themeSettings->creditsColor) . ';}'; - $this->log('CSS 6'); - // Arrows - $this->lessVariables['arrows-background'] = Color::colorToCSS($this->themeSettings->couleurA); - $this->lessVariables['arrows-color'] = Color::colorToCSS($this->themeSettings->arrowsColor); - - // Loader - $this->lessVariables['loader-background-color'] = Color::colorToCSS($this->themeSettings->couleurL); - $this->lessVariables['loader-foreground-color'] = Color::colorToCSS($this->themeSettings->loadingSecColor); - - // Audio description buttons - $this->lessVariables['audiodescription-background'] = Color::colorToCSS($this->themeSettings->couleurA); - $this->lessVariables['audiodescription-color'] = Color::colorToCSS($this->themeSettings->couleurA); - $this->log('CSS 7'); - // Links Styles - $this->lessVariables['links-color'] = Color::colorToCSS($this->themeSettings->linksColor); - $this->lessVariables['slideshow-color'] = Color::colorToCSS($this->themeSettings->slideshowColor ?: $this->themeSettings->couleurB); - $this->lessVariables['inlineslideshow-transition-time'] = (floatval($this->fluidbookSettings->inlineSlideshowTransitionDuration) * 1000) . 'ms'; - $this->lessVariables['slideshow-caption-size'] = $this->fluidbookSettings->slideshowCaptionSize ?: '16px'; - - $res = array_merge($res, $links); - - // Bookmarks - if (!isset($this->fluidbookSettings->bookmarkCornerSize)) { - $this->fluidbookSettings->bookmarkCornerSize = 10; - } - $this->log('CSS 8'); - $this->lessVariables['bookmark-star-disabled-color'] = Color::colorToCSS($this->themeSettings->bookmarkStarDisabledColor); - $this->lessVariables['bookmark-star-enabled-color'] = Color::colorToCSS($this->themeSettings->bookmarkStarEnabledColor); - $this->lessVariables['bookmark-color'] = Color::colorToCSS($this->themeSettings->bookmarkBackgroundColor); - $this->lessVariables['bookmark-corner-size'] = round($this->width * $this->fluidbookSettings->bookmarkCornerSize * 0.0075 * $this->z) . 'px'; - $this->lessVariables['bookmark-corner-offset'] = $this->fluidbookSettings->bookmarkOffset . 'px'; - - // Menus - $menuColor = new Color($this->themeSettings->couleurB); - $menuColor->setAlpha(1); - $menuTextColor = Color::colorToCSS($this->themeSettings->subTextColor); - $menuBreakpoint = empty($this->fluidbookSettings->menuBreakpoint) ? '1023px' : $this->fluidbookSettings->menuBreakpoint; - - $this->lessVariables['menu-breakpoint'] = $menuBreakpoint; - $this->lessVariables['menu-background'] = $menuColor->toCSS(); - if ($this->themeSettings->subSecondaryColor) { - $this->lessVariables['menu-button-background'] = Color::colorToCSS($this->themeSettings->subSecondaryColor); - } else { - $this->lessVariables['menu-background-green'] = 'max(45, min(255-45, green(@menu-background)))'; - $this->lessVariables['menu-background-red'] = 'max(45, min(255-45, red(@menu-background)))'; - $this->lessVariables['menu-background-blue'] = 'max(45, min(255-45, blue(@menu-background)))'; - $this->lessVariables['menu-button-background'] = 'overlay(rgb(@menu-background-red, @menu-background-green, @menu-background-blue), #c0c0c0)'; - } - $this->log('CSS 9'); - $this->lessVariables['menu-text'] = $menuTextColor; - $this->lessVariables['menu-field-background'] = Color::colorToCSS($this->themeSettings->subFieldColor); - $this->lessVariables['menu-field-text'] = Color::colorToCSS($this->themeSettings->subTextFieldColor); - $this->lessVariables['menu-select-background'] = Color::colorToCSS($this->themeSettings->subSelectColor); - $this->lessVariables['menu-select-text'] = Color::colorToCSS($this->themeSettings->subTextSelectColor); - $this->lessVariables['icon-color'] = Color::colorToCSS($this->themeSettings->couleurI); - $this->lessVariables['menu-overlay'] = Color::colorToCSS($this->themeSettings->popupVideoOverlay); - - $this->log('CSS 10'); - // Chapters - $this->lessVariables['menu-chapters-columns-count'] = max(1, min(6, $this->fluidbookSettings->chaptersColumns)); - $this->lessVariables['menu-chapters-columns-width'] = $this->fluidbookSettings->chaptersColMaxWidth; - $this->lessVariables['menu-chapters-font-size'] = $this->fluidbookSettings->chaptersFontSize; - - foreach ($this->getFluidbook()->getChapters() as $chapter) { - if ($chapter['color'] == '') { - continue; - } - $color = trim($chapter['color'], '#'); - $lessContents .= '.mview.c_' . $color . '{.menu-color(' . Color::colorToCSS($color) . ');}'; - } - - // Archives -// if ($this->>getFluidbook()->parametres->externalArchivesBack) { -// $this->vdir->copy($this->wdir . '/' . $this->>getFluidbook()->parametres->externalArchivesBack, 'data/images/' . $this->>getFluidbook()->parametres->externalArchivesBack); -// $res[] = '.mview.archives{background-image:url("../images/' . $this->>getFluidbook()->parametres->externalArchivesBack . '");}'; -// } - $this->log('CSS 11'); - # Index - $thumbw = $this->fluidbookSettings->mobileNavigationType === 'portrait' ? 200 : 100; - $this->lessVariables['thumb-width'] = $thumbw . 'px'; - $ratio = $this->width / $this->height; - $thumbh = round($thumbw / $ratio); - $this->config->thumbWidth = $thumbw; - $this->config->thumbHeight = $thumbh; - - $this->lessVariables['thumb-height'] = $thumbh . 'px'; - - #tooltip - $this->lessVariables['tooltip-background'] = Color::colorToCSS($this->themeSettings->tooltipBackColor); - $this->lessVariables['tooltip-color'] = Color::colorToCSS($this->themeSettings->tooltipTextColor); - $this->lessVariables['tooltip-font-size'] = $this->themeSettings->tooltipTextSize == 100 ? 14 : $this->themeSettings->tooltipTextSize; - $this->lessVariables['tooltip-padding'] = $this->themeSettings->tooltipPadding ?: 20; - $this->log('CSS 12'); - #Videos - if ($this->fluidbookSettings->bigPlayImage) { - $this->lessVariables['video-bigplay-image'] = '~"../data/links/' . $this->fluidbookSettings->bigPlayImage . '"'; - $this->vdir->copy($this->wdir . '/' . $this->fluidbookSettings->bigPlayImage, 'data/links/' . $this->fluidbookSettings->bigPlayImage); - } else { - $this->lessVariables['video-bigplay-image'] = '~"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMTMuNCAxMTMuNCI+PHN0eWxlPi5zdDB7b3BhY2l0eTowLjg7fSAuc3Qxe2ZpbGw6I0ZGRkZGRjt9PC9zdHlsZT48cGF0aCBjbGFzcz0ic3QwIiBkPSJNMTEwLjUgMTEzLjRIMi45Yy0xLjYgMC0yLjktMS4zLTIuOS0yLjlWMi45QzAgMS4zIDEuMyAwIDIuOSAwaDEwNy42YzEuNiAwIDIuOSAxLjMgMi45IDIuOXYxMDcuNmMwIDEuNi0xLjMgMi45LTIuOSAyLjl6Ii8+PHBhdGggY2xhc3M9InN0MSIgZD0iTTQ1LjggMzcuOGwzMS41IDE3LjljLjguNS44IDEuNiAwIDIuMUw0NS44IDc1LjZjLS44LjUtMS44LS4xLTEuOC0xVjM4LjhjMC0uOSAxLTEuNSAxLjgtMXoiLz48L3N2Zz4="'; - } - - #fonts - foreach ($this->cssfont as $hash => $item) { - $res[] = '@font-face{font-family: "' . $hash . '";src:url("../../data/fonts/' . $hash . '.woff") format("woff");}'; - } - - if ($this->fluidbookSettings->textPopupStylesheet) { - $res[] = file_get_contents($this->wdir . '/' . $this->fluidbookSettings->textPopupStylesheet); - } - $this->log('CSS 13'); - $this->_writeLess($this->lessVariables, $lessContents); - $this->stylesheets[] = 'data/style/style.css'; - $this->vdir->file_put_contents('data/style/style.css', implode("\n", $res)); - $this->log('Write CSS'); - } - - protected function _writeLess($variables, $lessContents = '') - { - if ($this->widget) { - $this->lessFiles[] = 'widget'; - } - foreach ($this->specialCSS as $s) { - $this->lessFiles[] = 'special/' . $s; - } - - $tmp = Files::tmpdir(); - - $from = $this->assets . '/style/*'; - `cp -r $from $tmp`; - - $bookVariables = array(); - foreach ($variables as $k => $v) { - $bookVariables[] = '@' . trim($k) . ':' . $v . ';'; - } - file_put_contents($tmp . '/book-variables.less', implode("\n", $bookVariables)); - file_put_contents($tmp . '/additional.less', $lessContents); - - foreach ($this->lessFiles as $f) { - $source_less = $this->assets . '/style/' . $f . '.less'; - $destination_less = $tmp . '/' . $f . '.less'; - $destination_css = $tmp . '/' . $f . '.css'; - - if (!file_exists($source_less)) { - die($source_less); - continue; - } - - $dir = dirname($destination_css); - - if (file_exists($dir) && !is_dir($dir)) { - unlink($dir); - } - // LESS file might be in a subfolder, so create if it doesn't exist - if (!file_exists($dir)) { - mkdir($dir, 0777, true); - } - - // Less files must be copied to temporary directory so they'll - // have access to the variables generated in book-variables.less - copy($source_less, $destination_less); - $less = new CommandLine('lessc'); - $less->setArg(null, $destination_less); - $less->setArg(null, $destination_css); - $less->execute(); - if (!file_exists($destination_css)) { - die($less->output); - continue; - } - $this->vdir->copy($destination_css, 'style/' . $f . '.css'); - if ($f != 'widget') { - $this->stylesheets[] = 'style/' . $f . '.css'; - } - } - } - - protected function _cssBackground() - { - $body = '#background, #splash {'; - - switch ($this->themeSettings->repeat) { - case FluidbookTheme::REPEAT: - $body .= 'background-repeat:repeat;'; - break; - case FluidbookTheme::NONE: - $body .= 'background-repeat:no-repeat;'; - break; - case FluidbookTheme::RATIO: - $body .= 'background-repeat:no-repeat;'; - $body .= 'background-size:cover;'; - break; - case FluidbookTheme::STRETCH: - $body .= 'background-repeat:no-repeat;'; - $body .= 'background-size:100% 100%;'; - break; - } - - if ($backgroundImage = $this->themeAsset('backgroundImage')) { - - $dbi = Image::getimagesize($backgroundImage->getPathname()); - $this->config->backgroundImageDimensions = array('width' => $dbi[0], 'height' => $dbi[1]); - - - $this->vdir->copy($backgroundImage->getPathname(), 'data/images/' . $backgroundImage->getFilename()); - $body .= 'background-image:url(../images/' . $backgroundImage->getFilename() . ');'; - $body .= 'background-position:'; - - switch ($this->themeSettings->backgroundVAlign) { - case FluidbookTheme::TOP: - $body .= 'top'; - break; - case FluidbookTheme::MIDDLE: - $body .= 'center'; - break; - case FluidbookTheme::BOTTOM: - $body .= 'bottom'; - break; - } - - $body .= ' '; - - switch ($this->themeSettings->backgroundHAlign) { - case FluidbookTheme::LEFT: - $body .= 'left'; - break; - case FluidbookTheme::CENTER: - $body .= 'center'; - break; - case FluidbookTheme::RIGHT: - $body .= 'right'; - break; - } - $body .= ';'; - } - - $body .= '}'; - - - if ($this->_themeBoolean($this->themeSettings->displayBackgroundDuringLoading)) { - $body .= '#background, #splash { - background-color:' . Color::colorToCSS($this->themeSettings->backgroundColor) . ' !important; - }'; - } else { - $body .= '#background { - visibility: hidden; - opacity: 0; - background-color:' . Color::colorToCSS($this->themeSettings->backgroundColor) . ' !important; - }'; - - $body .= '#splash { - background-color:' . Color::colorToCSS($this->themeSettings->loadingBackColor) . ' !important; - background-image: none; - }'; - } - - return $body; - } - - public static function writeCSSUA($property, $value) - { - $res = array(); - foreach (self::$uaPrefixes as $prefix) { - $res[] = $prefix . $property . ':' . $value; - } - return implode(';', $res); - } - - protected function base62($val) - { - $chars = '0123456789abcdefghijklmnopqrstuvwxyz'; - $base = strlen($chars); - $str = ''; - do { - $i = $val % $base; - $str = $chars[$i] . $str; - $val = ($val - $i) / $base; - } while ($val > 0); - return $str; - } - - public function copyLinkDir($source, $dest) - { - $this->vdir->copyDirectory($source, $dest); - } - - public function simpleCopyLinkFile($source, $dest) - { - if (stripos($source, '.svg') !== false) { - $source = $this->_fixSVG($source); - } - - $this->vdir->copy($source, $dest); - } - - protected function _fixSVG($source) - { - $fixed = str_replace('.svg', '.f.svg', $source); - if (file_exists($fixed) && filemtime($fixed) >= filemtime($source)) { - return $fixed; - } - if (file_exists($fixed) && is_link($fixed)) { - unlink($fixed); - } - $svg = simplexml_load_string(file_get_contents($source)); - $attr = $svg->attributes(); - if (isset($attr['width'], $attr['height'])) { - copy($source, $fixed); - return $fixed; - } - - $dim = Image::getimagesize($source); - $svg->addAttribute('preserveAspectRatio', 'none'); - $svg->addAttribute('width', $dim[0]); - $svg->addAttribute('height', $dim[1]); - file_put_contents($fixed, $svg->asXML()); - - return $fixed; - } - - public function addVideoJs() - { - $locale = $this->getFluidbook()->lang; - $map = ['pt' => 'pt-PT', 'pt-br' => 'pt-BR', 'zh' => 'zh-CN', 'es-pr' => 'es']; - if (isset($map[$locale])) { - $locale = $map[$locale]; - } - - $this->addJsLib('videojs', ['js/libs/videojs/video.min.js', 'js/libs/videojs/lang/' . $locale . '.js']); - $this->addLess('videojs/videojs'); - } - - public function addParallax() - { - $this->addJsLib('parallax', ['js/libs/fluidbook/fluidbook.parallax.js']); - } - - public function addLottie($animationData, $params, $hash) - { - if (isset($this->_lottieIDByHash[$hash])) { - return $this->_lottieIDByHash[$hash]; - } - - $this->addJsLib('lottie', 'js/libs/lottie.min.js'); - - if (!isset($this->config->lottieAnimations)) { - $this->config->lottieAnimations = []; - } - - $id = count($this->config->lottieAnimations); - $this->config->lottieAnimations[] = [$params, $animationData]; - $this->_lottieIDByHash[$hash] = $id; - return $id; - } - - public function addFont($fontFile) - { - $f = $this->wdir . '/' . $fontFile; - $e = explode('.', $f); - $ext = array_pop($e); - $hash = 'fb_' . substr(md5($fontFile), 0, 10); - if (!isset($this->cssfont[$hash])) { - $final = $hash . '.woff'; - $dest = $this->wdir . '/' . $final; - if (!file_exists($dest) || filemtime($dest) < filemtime($f)) { - $script = resource_path('tools/fonts/convertrn.pe'); - if (!is_executable($script)) { - chmod($script, 755); - } - $fontforge = new CommandLine('fontforge'); - $fontforge->setArg('-script', $script); - $fontforge->setArg(null, $f); - $fontforge->setArg(null, $dest); - $fontforge->execute(); - //$fontforge->debug(); - } - $this->vdir->copy($dest, 'data/fonts/' . $hash . '.woff'); - $fontline = new CommandLine('font-line'); - $fontline->setArg(null, 'report'); - $fontline->setArg(null, $f); - $fontline->execute(); - //$fontline->debug(); - $report = explode("\n", $fontline->getOutput()); - - foreach ($report as $item) { - $item = trim($item); - if (!stristr($item, ':')) { - continue; - } - list($k, $v) = explode(':', $item, 2); - $v = trim($v); - if ($k == '[head] Units per Em') { - $fontHeight = $v; - } - if ($k == '[OS/2] CapHeight') { - $fontCapHeight = $v; - } - if ($k == '[OS/2] TypoAscender') { - $ascender = abs($v); - } - if ($k == '[OS/2] TypoDescender') { - $descender = abs($v); - } - } - $capHeight = 1; - if (isset($fontCapHeight) && isset($fontHeight)) { - $capHeight = $fontCapHeight / $fontHeight; - } - $font = ['family' => $hash, 'capHeight' => $capHeight, 'ascender' => $ascender / $fontHeight, 'descender' => $descender / $fontHeight]; - $this->cssfont[$hash] = $font; - } - return $this->cssfont[$hash]; - } - - public function addJsLib($name, $files) - { - if (!isset($this->jsLibs[$name])) { - $this->jsLibs[$name] = []; - } - if (!is_array($files)) { - $files = [$files]; - } - $diff = array_diff($files, $this->jsLibs[$name]); - if (count($diff)) { - $this->jsLibs[$name] = array_merge($this->jsLibs[$name], $diff); - } - } - - public static function encodeWebVideos($file, $dir = null, $async = true, $force = false, $format = 'all') - { - $file = new SplFileInfo($file); - if (is_null($dir)) { - $dir = $file->getPath(); - } - - $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 . '/' . $file->getBasename('.' . $file->getExtension()); - $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 CommandLine('/application/scripts/webvideo', $log); - $webvideo->setArg(null, $file); - $webvideo->setArg(null, $dir); - $webvideo->execute(); - } - - - public function copyLinkFile($source, $dest, $video = false) - { - if ($video && $this->fluidbookSettings->mobileVideosPath != '') { - - } - $origDir = $this->wdir; - $types = $this->getVideosFormats(); - if ($video) { - self::encodeWebVideos($origDir . $source, null, true); - $e = explode('.', $source); - array_pop($e); - $base = implode('.', $e); - $source = array(); - foreach ($types as $type) { - $source[] = $base . '.' . $type; - } - } - - if (!is_array($source)) { - $source = array($source); - } - - foreach ($source as $so) { - $s = $origDir . $so; - if (file_exists($s)) { - $d = $dest . '/' . $so; - $this->simpleCopyLinkFile($s, $d, false); - } - } - } - - public function __destruct() - { - - } - - public function unzipFile($file, $moveAssets = false, $baseDir = null, $junkPaths = false) - { - $fdir = is_null($baseDir) ? 'data/links/' . str_replace('.', '_', $file) : $baseDir; - - $zipPath = $this->wdir . '/' . $file; - $dir = protected_path('fluidbookpublication/cache/unzip') . '/' . Files::hashFileAttributes($zipPath) . '_' . ($moveAssets ? '1' : '0') . '_' . ($junkPaths ? '1' : '0'); - - if (!file_exists($dir)) { - mkdir($dir, 0777, true); - Zip::extract($zipPath, $dir, $junkPaths); - if ($moveAssets) { - `mv $dir/Assets/* $dir`; - rmdir($dir . '/Assets'); - } - } - - return array('dir' => $dir, 'fdir' => $fdir); - } - - public function getConfigZIP($d) - { - $res = array('type' => 'zip', 'width' => 0, 'height' => 0); - if (file_exists($d . '/index.html')) { - $doc = new DOMDocument(); - @$doc->loadHTMLFile($d . '/index.html'); - $xpath = new DOMXPath($doc); - $c = $xpath->query("//canvas"); - foreach ($c as $canvas) { - /* @var $canvas DOMElement */ - $res['width'] = intval((string)$canvas->getAttribute('width')); - $res['height'] = intval((string)$canvas->getAttribute('height')); - } - - $m = $xpath->query('//meta[@name="width"]'); - foreach ($m as $meta) { - $res['width'] = intval((string)$meta->getAttribute('content')); - } - - $m = $xpath->query('//meta[@name="height"]'); - foreach ($m as $meta) { - $res['height'] = intval((string)$meta->getAttribute('content')); - } - - $r = array('html' => 'index.html', 'inject' => array(), 'injectcss' => array(), 'injectjs' => array()); - } else { - $r = array('html' => false, 'inject' => array(file_get_contents($d . '/init.js')), 'injectcss' => array('multimedia.css'), 'injectjs' => array('multimedia.js')); - } - return array_merge($res, $r); - } - - public function addFontKit($font) - { - if ($font === 'sans-serif') { - return; - } - if ($font === 'Open Sans') { - $font = 'OpenSans'; - } - - $path = 'style/fonts/' . $font; - $css = $path . '/font.css'; - if (in_array($css, $this->stylesheets)) { - return; - } - $this->stylesheets[] = $css; - $this->vdir->copyDirectory($this->assets . '/' . $path, $path); - return $path . '/font.css'; - } - - - public function SimpleXMLElement_innerXML($xml) - { - $innerXML = ''; - foreach (dom_import_simplexml($xml)->childNodes as $child) { - $innerXML .= $child->ownerDocument->saveXML($child); - } - return $innerXML; - } - - public function writeXMLArticles() - { - $f = $this->fluidbookSettings->articlesFile; - - $this->lessVariables['articles-title-color'] = '#000000'; - $this->lessVariables['articles-font'] = 'Open Sans'; - - if (!$f || !file_exists($this->wdir . '/' . $f) || !is_file($this->wdir . '/' . $f)) { - return; - } - $f = $this->wdir . '/' . $f; - $mapFonts = ['OpenSans' => 'Open Sans']; - $this->addLess('articles'); - if ($this->fluidbookSettings->articlesStyle !== 'default') { - $this->lessVariables['articles-styles'] = $this->fluidbookSettings->articlesStyle; - } - $this->lessVariables['articles-font'] = $mapFonts[$this->fluidbookSettings->articlesFont] ?? $this->fluidbookSettings->articlesFont; - $fontPath = $this->addFontKit($this->fluidbookSettings->articlesFont); - - $list = $this->config->articlesList ?? []; - - - $this->lessVariables['articles-title-color'] = '#565657'; - - - $svg = ' - - '; - - - $x = simplexml_load_string(file_get_contents($f)); - foreach ($x->xpath('/articles/article') as $k => $a) { - $dir = isset($a['dir']) ? (string)$a['dir'] : null; - $url = (string)$a['url']; - $id = (string)$a['id']; - $color = (string)$a['color']; - if (!$color) { - $color = '#000'; - } - - $specificStyles = '## h3, ## figure figcaption{background-color:' . $color . '}'; - $specificStyles .= '## .chapo, ## blockquote, ## a{color:' . $color . ';}'; - - $inner = '
'; - $inner .= '
'; - if ($this->fluidbookSettings->articlesShare && $this->fluidbookSettings->share) { - $inner .= ''; - } - $inner .= ''; - $inner .= '
'; - - $inner .= '
'; - - $title = ''; - $lead = ''; - $image = ''; - - $first = true; - - foreach ($a->children() as $child) { - if ($first) { - $first = false; - if ($child->getName() !== 'category') { - $inner .= '

 

'; - } - } - $inner .= $this->_articleToHTML($child, $title, $lead, $image, $dir); - } - $inner .= '
'; - - if (!$title) { - $title = 'Article sans titre ' . $k; - } - - if (!$id) { - $id = Text::str2URL($title); - } - - if (!$url) { - $url = $id . '.html'; - } - - $inner = str_replace(array('$id', '$url'), array($id, $url), $inner); - - $article = ['id' => $id, - 'url' => $url, - 'color' => $color, - 'contents' => '', - 'type' => 'xml']; - - $article['contents'] = $inner; - $content = ''; - $content .= ''; - $content .= ''; - $content .= ''; - $content .= ''; - $content .= ''; - $content .= $svg; - $content .= $inner; - $content .= ''; - $article['print'] = $content; - $list[] = $article; - - $this->addSEOArticle('#/article/' . $article['url'], $title, $lead, $image, $article['id'], $article['url'], $inner); - } - - if (isset($list)) { - $this->config->articlesList = $list; - } - } - - public function findArticleById($id) - { - foreach ($this->config->articlesList as $item) { - if ($item['id'] === $id) { - return $item; - } - } - return null; - } - - public function updateArticleById($id, $article) - { - foreach ($this->config->articlesList as $k => $item) { - if ($item['id'] === $id) { - $this->config->articlesList[$k] = $article; - break; - } - } - } - - public function writeArticles() - { - $list = $this->config->articlesList ?? []; - - $nb = count($list); - - usort($list, function ($a, $b) { - if ($a['page'] == $b['page']) { - $ea = explode('-', $a['id']); - $eb = explode('-', $b['id']); - if (is_numeric($ea[0]) && is_numeric($eb[0])) { - return $ea[0] - $eb[0]; - } - return strcmp($a['id'], $b['id']); - } - return $a['page'] - $b['page']; - }); - - foreach ($list as $k => $item) { - $nextIndex = ($k + 1) % $nb; - $prevIndex = ($k - 1 + $nb) % $nb; - $list[$k]['prev'] = $list[$prevIndex]['url']; - $list[$k]['next'] = $list[$nextIndex]['url']; - } - - - $idlist = []; - foreach ($list as $item) { - $idlist[$item['id']] = $item; - } - - $this->config->articlesList = $idlist; - } - - /** - * @param $child SimpleXMLElement - * @param $title - * @param $lead - * @param $image - * @return string|void - */ - protected function _articleToHTML($child, &$title, &$lead, &$image, $dir = null) - { - $markupMap = ['category' => 'h3', - 'subtitle' => 'h2', - 'legend' => 'figcaption', - 'title' => 'h1', - 'lead' => 'div.chapo', - 'paragraph' => 'p', - 'note' => 'div.note', - 'quote' => 'blockquote', - 'signature' => 'div.author', - 'intertitle' => 'h2.inter', - 'bigfont' => 'h2.bigfont', - 'separator' => 'hr', - 'link' => 'a']; - - $attrsmap = ['a' => ['link' => 'href']]; - - $dirattr = ''; - if (isset($child['dir'])) { - $d = (string)$child['dir']; - if ($d !== $dir) { - $dirattr = ' dir="' . $d . '"'; - $dir = $d; - } - } - - $res = ''; - $tag = $child->getName(); - if ($tag === 'encadre') { - $res .= ''; - foreach ($child->children() as $sub) { - $res .= $this->_articleToHTML($sub, $a1, $a2, $a3, $dir); - } - $res .= ''; - } else if ($tag === 'youtube') { - $res .= '
'; - } else if ($tag === 'image') { - $srcattrs = ['href', 'src', 'file']; - $file = ''; - foreach ($srcattrs as $srcattr) { - if (isset($child[$srcattr])) { - $file = (string)$child[$srcattr]; - break; - } - } - if ($image === '') { - $image = 'articles/' . $file; - } - $filepath = $this->wdir . '/articles/' . $file; - $this->vdir->copy($filepath, 'data/articles/' . $file); - $legend = (string)$child; - $caption = $legend ? '
' . $legend . '
' : ''; - if (file_exists($filepath)) { - $dim = getimagesize($filepath); - } else { - $dim = [0 => 1024, 1 => 10]; - } - $res .= '' . $legend . '' . $caption . ''; - } else { - $c = trim($this->SimpleXMLElement_innerXML($child)); - if (!$c) { - return; - } - if ($title === '' && $tag === 'title') { - $title = $c; - } - if ($lead === '' && $tag === 'lead') { - $lead = $c; - } - $m = $markupMap[$tag] ?? $tag; - $e = explode('.', $m); - $markup = $e[0]; - $attrs = $dirattr; - if (count($e) === 2) { - $attrs .= ' class="' . $e[1] . '"'; - } - if ($m === 'a') { - $attrs .= ' target="_blank"'; - } - foreach ($child->attributes() as $name => $v) { - $n = $attrsmap[$m][$name] ?? $name; - $attrs .= ' ' . $n . '="' . htmlspecialchars($v) . '"'; - } - $res .= '<' . $markup . $attrs . '>' . $c . ''; - } - return $res; - } - - public static function getPhonegapVersion($v = 'latest') - { - if ($v != 'latest') { - return $v; - } - - $versions = self::getPhonegapVersions(); - return array_pop($versions); - } - - public static function getPhonegapVersions() - { - $versions = array(); - $phonegap_dir = resource_path('fluidbookpublication/phonegap'); - - if (is_dir($phonegap_dir)) { - $dr = opendir($phonegap_dir); - while ($file = readdir($dr)) { - if ($file == '.' || $file == '..' || $file == 'plugins') { - continue; - } - $versions[] = $file; - } - usort($versions, 'version_compare'); - } - - return $versions; - } - - public static function getSourcesPath($version, $dir = null) - { - $res = self::getFluidbookPlayerBaseDirectory(); - if (null !== $dir) { - $res .= $dir . '/'; - } - if ($version === 'stable') { - $res .= 'branches/master'; - } else if ($version === 'dev') { - $res .= 'local/master'; - } else { - list($branch, $location) = explode('|', $version); - $res .= ($location === 'git' ? 'branches' : $location) . '/' . $branch; - } - return $res; - } - - public static function getCompiledSourcesPath($version) - { - return self::getSourcesPath($version, 'compiled'); - } - - public function source_path($path = ''): string - { - $res = $this->assets; - if ($path) { - $res .= '/' . $path; - } - return $res; - } - - public function page_path($page, $path = ''): string - { - return ''; - } - - public function getPagePDFSource($page): string - { - return $this->getFluidbook()->getPDFSplitSource($page); - } - - public function getWidthForLinks() { - return $this->getWidth() / $this->getLinkScale(); - } - - public function getHeightForLinks() { - return $this->getHeight() / $this->getLinkScale(); - } -} diff --git a/app/Jobs/FluidbookImagesPreprocess.php b/app/Jobs/FluidbookImagesPreprocess.php index f598a7732..cbddf5267 100644 --- a/app/Jobs/FluidbookImagesPreprocess.php +++ b/app/Jobs/FluidbookImagesPreprocess.php @@ -2,6 +2,7 @@ namespace App\Jobs; +use App\Fluidbook\Compiler; use App\Models\FluidbookPublication; use Cubist\Util\ArrayUtil; @@ -28,7 +29,7 @@ class FluidbookImagesPreprocess extends Base { $this->book = FluidbookPublication::find($this->book_id); $settings = $this->book->getSettings(); - $resolutions = FluidbookCompiler::getBookResolutions($this->book); + $resolutions = Compiler::getBookResolutions($this->book); $rasterizePages = ArrayUtil::parseRange($settings->rasterizePages); $vectorPages = array_diff(ArrayUtil::parseRange($settings->vectorPages), $rasterizePages); diff --git a/app/SubForms/Link/Base.php b/app/SubForms/Link/Base.php index 00f82fcf9..f01ecface 100644 --- a/app/SubForms/Link/Base.php +++ b/app/SubForms/Link/Base.php @@ -6,10 +6,11 @@ use App\Fields\FluidbookLinkEditor\Depth; use App\Fields\FluidbookLinkEditor\LinkType; use App\Fields\FluidbookLinkEditor\MultimediaIntegration; use App\Fields\FluidbookLinkEditor\RolloverAnimation; -use App\Jobs\FluidbookCompiler; +use App\Fluidbook\Compiler; +use App\Fluidbook\Link\Link; use App\Models\FluidbookPublication; -use Cubist\Backpack\Magic\Fields\CheckboxBasic; use Cubist\Backpack\Magic\Fields\Checkbox; +use Cubist\Backpack\Magic\Fields\CheckboxBasic; use Cubist\Backpack\Magic\Fields\FieldGroupEnd; use Cubist\Backpack\Magic\Fields\FieldGroupStart; use Cubist\Backpack\Magic\Fields\FilesOrURL; @@ -20,8 +21,6 @@ use Cubist\Backpack\Magic\Fields\SelectFromArray; use Cubist\Backpack\Magic\Fields\Text; use Cubist\Backpack\Magic\Fields\Textarea; use Cubist\Backpack\Magic\Form; -use Fluidbook\Tools\Compiler\DummyCompiler; -use App\Fluidbook\Link\Link; // __('!! Editeur de liens') class Base extends Form @@ -356,7 +355,7 @@ class Base extends Form $id = 1; $base = ['top' => 1, 'left' => 1, 'width' => 1, 'height' => 1, 'to' => '', 'inline' => 'inline', 'page' => 2, 'target' => '_blank', 'extra' => '', 'interactive' => false]; - $compiler = new FluidbookCompiler($fluidbook); + $compiler = new Compiler($fluidbook); /** @var Link $instances */ $instances = [];