path_relative_to_web_root = ConfigQuery::read('asset_dir_from_web_root', 'assets'); $this->assetsManager = $assetsManager; } /** * Generate an asset URL * * @param string $source a module code, or SmartyParser::TEMPLATE_ASSETS_KEY * @param string $file the file path, relative to a template base directory (e.g. assets/css/style.css) * @param string $type the asset type, either 'css' or ' * @param ParserInterface $parserInterface the current template parser * @param array $filters the filters to pass to the asset manager * @param bool $debug the debug mode * @param string $declaredAssetsDirectory if not null, this is the assets directory declared in the {declare_assets} function of a template. * @param mixed $sourceTemplateName A template name, of false. If provided, the assets will be searched in this template directory instead of the current one. * @return mixed */ public function resolveAssetURL($source, $file, $type, ParserInterface $parserInterface, $filters = [], $debug = false, $declaredAssetsDirectory = null, $sourceTemplateName = false) { $url = ""; // Normalize path separator $file = $this->fixPathSeparator($file); $fileRoot = $this->resolveAssetSourcePath($source, $sourceTemplateName, $file, $parserInterface); if (null !== $fileRoot) { $templateDefinition = $parserInterface->getTemplateDefinition($sourceTemplateName); $url = $this->assetsManager->processAsset( $fileRoot . DS . $file, $fileRoot, THELIA_WEB_DIR . $this->path_relative_to_web_root, $templateDefinition->getPath(), $source, // $this->getBaseWebAssetDirectory($source, $declaredAssetsDirectory), URL::getInstance()->absoluteUrl($this->path_relative_to_web_root, null, URL::PATH_TO_FILE /* path only */), $type, $filters, $debug ); } else { Tlog::getInstance()->addError("Asset $file (type $type) was not found."); } return $url; } /** * Return an asset source file path. * * A system of fallback enables file overriding. It will look for the template : * - in the current template in directory /modules/{module code}/ * - in the module in the current template if it exists * - in the module in the default template * * @param string $source a module code, or or SmartyParser::TEMPLATE_ASSETS_KEY * @param string $templateName a template name, or false to use the current template * @param string $fileName the filename * @param ParserInterface $parserInterface the current template parser * * @return mixed the path to directory containing the file, or null if the file doesn't exists. */ public function resolveAssetSourcePath($source, $templateName, $fileName, ParserInterface $parserInterface) { $filePath = null; $templateDefinition = $parserInterface->getTemplateDefinition(false); // Get all possible directories to search $paths = $this->getPossibleAssetSources( $parserInterface->getTemplateDirectories($templateDefinition->getType()), $templateName ?: $templateDefinition->getName(), $source ); // Normalize path separator if required (e.g., / becomes \ on windows) $fileName = $this->fixPathSeparator($fileName); /* Absolute paths are not allowed. This may be a mistake, such as '/assets/...' instead of 'assets/...'. Forgive it. */ $fileName = ltrim($fileName, DS); /* Navigating in the server's directory tree is not allowed :) */ if (preg_match('!\.\.\\'.DS.'!', $fileName)) { // This time, we will not forgive. throw new \InvalidArgumentException("Relative paths are not allowed in assets names."); } // Find the first occurrence of the file in the directories lists foreach ($paths as $path) { if ($this->filesExist($path, $fileName)) { // Got it ! $filePath = $path; break; } } return $filePath; } /** * Be sure that the pat separator of a pathname is always the platform path separator. * * @param string $path the iput path * @return string the fixed path */ protected function fixPathSeparator($path) { if (DS != '/') { $path = str_replace('/', DS, $path); } return $path; } /** * Check if a file(s) exists in a directory * * @param string $dir the directory path * @param string $file the file path. It can contain wildcard. eg: /path/*.css * @return bool true if file(s) */ protected function filesExist($dir, $file) { if (!file_exists($dir)) { return false; } $full_path = rtrim($dir, DS) . DS . ltrim($file, DS); try { $files = glob($full_path); $files_found = ! empty($files); } catch (\Exception $ex) { Tlog::getInstance()->addError($ex->getMessage()); $files_found = false; } return $files_found; } /** * Get all possible directories from which the asset can be found. * It returns an array of directories ordered by priority. * * @param array $directories all directories source available for the template type * @param string $template the name of the template * @param string $source the module code or SmartyParser::TEMPLATE_ASSETS_KEY * @return array possible directories */ protected function getPossibleAssetSources($directories, $template, $source) { $paths = []; if (SmartyParser::TEMPLATE_ASSETS_KEY !== $source) { // We're in a module. // First look into the current template in the right scope : frontOffice, backOffice, ... // template should be overridden in : {template_path}/modules/{module_code}/{template_name} if (isset($directories[$template][SmartyParser::TEMPLATE_ASSETS_KEY])) { $paths[] = $directories[$template][SmartyParser::TEMPLATE_ASSETS_KEY] . DS . self::MODULE_OVERRIDE_DIRECTORY_NAME . DS . $source; } // then in the implementation for the current template used in the module directory if (isset($directories[$template][$source])) { $paths[] = $directories[$template][$source]; } // then in the default theme in the module itself if (isset($directories[self::DEFAULT_TEMPLATE_NAME][$source])) { $paths[] = $directories[self::DEFAULT_TEMPLATE_NAME][$source]; } } else { $paths[] = $directories[$template][SmartyParser::TEMPLATE_ASSETS_KEY]; } return $paths; } }