![]() Server : Apache System : Linux server2.corals.io 4.18.0-348.2.1.el8_5.x86_64 #1 SMP Mon Nov 15 09:17:08 EST 2021 x86_64 User : corals ( 1002) PHP Version : 7.4.33 Disable Function : exec,passthru,shell_exec,system Directory : /home/corals/mautic.corals.io/app/bundles/CoreBundle/Helper/ |
<?php namespace Mautic\CoreBundle\Helper; use MatthiasMullie\Minify; use Symfony\Component\Finder\Finder; class AssetGenerationHelper { // Temporary array of libraries to load from node_modules before we switch // to Symfony Encore. This is the first step to load libraries from NPM. private const NODE_MODULES = [ 'mousetrap/mousetrap.js', // Needed for keyboard shortcuts 'jquery/dist/jquery.js', // Needed for everything. It's the underlying framework. 'js-cookie/src/js.cookie.js', // Needed for cookies. 'bootstrap/dist/js/bootstrap.js', // Needed for the UI components like bodal boxes. 'jquery-form/src/jquery.form.js', // Needed for ajax forms with file attachments. 'moment/min/moment.min.js', // Needed for date/time formatting. 'jquery.caret/dist/jquery.caret.js', // Needed for the text editor Twitter-like mentions (tokens). 'codemirror/lib/codemirror.js', // Needed for the legacy code-mode editor. 'codemirror/addon/hint/show-hint.js', // Needed for the legacy code-mode editor. 'codemirror/mode/xml/xml.js', // Needed for the legacy code-mode editor. 'codemirror/mode/javascript/javascript.js', // Needed for the legacy code-mode editor. 'codemirror/mode/htmlmixed/htmlmixed.js', // Needed for the legacy code-mode editor. 'codemirror/mode/css/css.js', // Needed for the legacy code-mode editor. 'jquery.cookie/jquery.cookie.js', // A simple, lightweight jQuery plugin for reading, writing and deleting cookies. 'jsplumb/dist/js/jsplumb.js', // Needed for the campaign builder. 'typeahead.js/dist/typeahead.bundle.js', // Needed for the Twitter-like mentions (tokens). 'jquery-datetimepicker/build/jquery.datetimepicker.full.js', // Needed for the date/time UI selector. 'shufflejs/dist/shuffle.js', // Needed for the plugin list page. '@claviska/jquery-minicolors/jquery.minicolors.js', // Needed for the color picker. 'dropzone/dist/dropzone.js', // Needed for the file upload in the asset detail page. 'multiselect/js/jquery.multi-select.js', // Needed for the multiselect UI component. 'chart.js/dist/Chart.js', // Needed for the charts. 'chosen-js/chosen.jquery.js', 'at.js/dist/js/jquery.atwho.js', 'jvectormap-next/jquery-jvectormap.js', 'modernizr/modernizr-mautic-dist.js', 'jquery.quicksearch/src/jquery.quicksearch.js', 'jquery-ui/ui/version.js', 'jquery-ui/ui/widget.js', 'jquery-ui/ui/plugin.js', 'jquery-ui/ui/position.js', 'jquery-ui/ui/data.js', 'jquery-ui/ui/disable-selection.js', 'jquery-ui/ui/focusable.js', 'jquery-ui/ui/form-reset-mixin.js', 'jquery-ui/ui/jquery-patch.js', 'jquery-ui/ui/keycode.js', 'jquery-ui/ui/labels.js', 'jquery-ui/ui/scroll-parent.js', 'jquery-ui/ui/tabbable.js', 'jquery-ui/ui/unique-id.js', 'jquery-ui/ui/effect.js', 'jquery-ui/ui/safe-blur.js', // needed for the legacy builder 'jquery-ui/ui/widgets/mouse.js', 'jquery-ui/ui/widgets/draggable.js', 'jquery-ui/ui/widgets/droppable.js', 'jquery-ui/ui/widgets/selectable.js', 'jquery-ui/ui/widgets/sortable.js', 'jquery-ui/ui/vendor/jquery-color/jquery.color.js', 'jquery-ui/ui/effects/effect-drop.js', 'jquery-ui/ui/effects/effect-fade.js', 'jquery-ui/ui/effects/effect-size.js', 'jquery-ui/ui/effects/effect-slide.js', 'jquery-ui/ui/effects/effect-transfer.js', 'jquery-ui/ui/safe-active-element.js', // needed for ElFinder 'jquery-ui/ui/widgets/button.js', // needed for ElFinder 'jquery-ui/ui/widgets/resizable.js', // needed for ElFinder 'jquery-ui/ui/widgets/slider.js', // needed for ElFinder 'jquery-ui/ui/widgets/controlgroup.js', // needed for ElFinder 'jquery-ui-touch-punch/jquery.ui.touch-punch.js', // Needed for touch devices, and needs to be added after the jquery-ui components ]; private string $version; public function __construct( private BundleHelper $bundleHelper, private PathsHelper $pathsHelper, CoreParametersHelper $coreParametersHelper, AppVersion $appVersion ) { $this->version = substr(hash('sha1', $coreParametersHelper->get('secret_key').$appVersion->getVersion()), 0, 8); } /** * Generates and returns assets. * * @param bool $forceRegeneration * * @return array */ public function getAssets($forceRegeneration = false) { static $assets = []; if (empty($assets)) { $loadAll = true; $env = ($forceRegeneration) ? 'prod' : MAUTIC_ENV; $rootPath = $this->pathsHelper->getSystemPath('assets_root'); $assetsPath = $this->pathsHelper->getSystemPath('media'); $assetsFullPath = "$rootPath/$assetsPath"; if ('prod' == $env) { $loadAll = false; // by default, loading should not be required // check for libraries and app files and generate them if they don't exist if in prod environment $prodFiles = [ 'css/libraries.css', 'css/app.css', 'js/libraries.js', 'js/app.js', ]; foreach ($prodFiles as $file) { if (!file_exists("$assetsFullPath/$file")) { $loadAll = true; // it's missing so compile it break; } } } if ($loadAll || $forceRegeneration) { if ('prod' == $env) { ini_set('max_execution_time', '300'); $inProgressFile = "$assetsFullPath/generation_in_progress.txt"; if (!$forceRegeneration) { while (file_exists($inProgressFile)) { // dummy loop to prevent conflicts if one process is actively regenerating assets } } file_put_contents($inProgressFile, date('r')); } foreach (self::NODE_MODULES as $path) { $relPath = "node_modules/{$path}"; $fullPath = "{$this->pathsHelper->getVendorRootPath()}/{$relPath}"; $ext = pathinfo($relPath, PATHINFO_EXTENSION); $details = [ 'fullPath' => $fullPath, 'relPath' => $relPath, ]; if ('prod' == $env) { $assets[$ext]['libraries'][$relPath] = $details; } else { $assets[$ext][$relPath] = $details; } } $modifiedLast = []; // get a list of all core asset files $bundles = $this->bundleHelper->getMauticBundles(); $fileTypes = ['css', 'js']; foreach ($bundles as $bundle) { foreach ($fileTypes as $ft) { if (!isset($modifiedLast[$ft])) { $modifiedLast[$ft] = []; } $dir = "{$bundle['directory']}/Assets/$ft"; if (file_exists($dir)) { $modifiedLast[$ft] = array_merge($modifiedLast[$ft], $this->findAssets($dir, $ft, $env, $assets)); } } } $modifiedLast = array_merge($modifiedLast, $this->findOverrides($env, $assets)); // combine the files into their corresponding name and put in the root media folder if ('prod' == $env) { $checkPaths = [ $assetsFullPath, "$assetsFullPath/css", "$assetsFullPath/js", ]; array_walk($checkPaths, function ($path): void { if (!file_exists($path)) { mkdir($path); } }); foreach ($assets as $type => $groups) { foreach ($groups as $group => $files) { $assetFile = "$assetsFullPath/$type/$group.$type"; // only refresh if a change has occurred $modified = ($forceRegeneration || !file_exists($assetFile)) ? true : filemtime($assetFile) < $modifiedLast[$type][$group]; if ($modified) { if (file_exists($assetFile)) { // delete it unlink($assetFile); } if ('css' == $type) { $minifier = new Minify\CSS(...array_column($files, 'fullPath')); $minifier->minify($assetFile); } else { $minifier = new Minify\JS(...array_column($files, 'fullPath')); $minifier->minify($assetFile); } } } } unlink($inProgressFile); } } if ('prod' == $env) { // return prod generated assets $assets = [ 'css' => [ "{$assetsPath}/css/libraries.css?v{$this->version}", "{$assetsPath}/css/app.css?v{$this->version}", ], 'js' => [ "{$assetsPath}/js/libraries.js?v{$this->version}", "{$assetsPath}/js/app.js?v{$this->version}", ], ]; } else { foreach ($assets as &$typeAssets) { $typeAssets = array_keys($typeAssets); } } } return $assets; } /** * Finds directory assets. * * @param string $dir * @param string $ext * @param string $env * @param array $assets */ protected function findAssets($dir, $ext, $env, &$assets): array { $rootPath = str_replace('\\', '/', $this->pathsHelper->getSystemPath('assets_root').'/'); $directories = new Finder(); $directories->directories()->exclude('*less')->depth('0')->ignoreDotFiles(true)->in($dir); $modifiedLast = []; if (count($directories)) { foreach ($directories as $directory) { $group = $directory->getBasename(); // Only auto load directories app or libraries if (!in_array($group, ['app', 'libraries'])) { continue; } $files = new Finder(); $thisDirectory = str_replace('\\', '/', $directory->getRealPath()); $files->files()->depth('0')->name('*.'.$ext)->in($thisDirectory); $sort = fn (\SplFileInfo $a, \SplFileInfo $b): int => strnatcmp($a->getRealpath(), $b->getRealpath()); $files->sort($sort); foreach ($files as $file) { $fullPath = $file->getPathname(); $relPath = str_replace($rootPath, '', $file->getPathname()); if (str_starts_with($relPath, '/')) { $relPath = substr($relPath, 1); } $details = [ 'fullPath' => $fullPath, 'relPath' => $relPath, ]; if ('prod' == $env) { $lastModified = filemtime($fullPath); if (!isset($modifiedLast[$group]) || $lastModified > $modifiedLast[$group]) { $modifiedLast[$group] = $lastModified; } $assets[$ext][$group][$relPath] = $details; } else { $assets[$ext][$relPath] = $details; } } unset($files); } } unset($directories); $files = new Finder(); $files->files()->depth('0')->ignoreDotFiles(true)->name('*.'.$ext)->in($dir); $sort = fn (\SplFileInfo $a, \SplFileInfo $b): int => strnatcmp($a->getRealpath(), $b->getRealpath()); $files->sort($sort); foreach ($files as $file) { $fullPath = str_replace('\\', '/', $file->getPathname()); $relPath = str_replace($rootPath, '', $fullPath); $details = [ 'fullPath' => $fullPath, 'relPath' => $relPath, ]; if ('prod' == $env) { $lastModified = filemtime($fullPath); if (!isset($modifiedLast['app']) || $lastModified > $modifiedLast['app']) { $modifiedLast['app'] = $lastModified; } $assets[$ext]['app'][$relPath] = $details; } else { $assets[$ext][$relPath] = $details; } } unset($files); return $modifiedLast; } /** * Find asset overrides in the template. */ protected function findOverrides($env, &$assets): array { $rootPath = $this->pathsHelper->getSystemPath('assets_root'); $currentTheme = $this->pathsHelper->getSystemPath('current_theme'); $modifiedLast = []; $types = ['css', 'js']; $overrideFiles = [ 'libraries' => 'libraries_custom', 'app' => 'app_custom', ]; foreach ($types as $ext) { foreach ($overrideFiles as $group => $of) { if (file_exists("$rootPath/$currentTheme/$ext/$of.$ext")) { $fullPath = "$rootPath/$currentTheme/$ext/$of.$ext"; $relPath = "$currentTheme/$ext/$of.$ext"; $details = [ 'fullPath' => $fullPath, 'relPath' => $relPath, ]; if ('prod' == $env) { $lastModified = filemtime($fullPath); if (!isset($modifiedLast[$ext][$group]) || $lastModified > $modifiedLast[$ext][$group]) { $modifiedLast[$ext][$group] = $lastModified; } $assets[$ext][$group][$relPath] = $details; } else { $assets[$ext][$relPath] = $details; } } } } return $modifiedLast; } }