Initial commit
This commit is contained in:
@@ -0,0 +1,311 @@
|
||||
<?php
|
||||
/*************************************************************************************/
|
||||
/* This file is part of the Thelia package. */
|
||||
/* */
|
||||
/* Copyright (c) OpenStudio */
|
||||
/* email : dev@thelia.net */
|
||||
/* web : http://www.thelia.net */
|
||||
/* */
|
||||
/* For the full copyright and license information, please view the LICENSE.txt */
|
||||
/* file that was distributed with this source code. */
|
||||
/*************************************************************************************/
|
||||
|
||||
namespace TheliaSmarty\Template\Assets;
|
||||
|
||||
use Thelia\Core\Template\Assets\AssetManagerInterface;
|
||||
use Thelia\Core\Template\Assets\AssetResolverInterface;
|
||||
use Thelia\Core\Template\ParserInterface;
|
||||
use TheliaSmarty\Template\SmartyParser;
|
||||
use Thelia\Core\Template\TemplateDefinition;
|
||||
use Thelia\Log\Tlog;
|
||||
use Thelia\Model\ConfigQuery;
|
||||
use Thelia\Tools\URL;
|
||||
|
||||
class SmartyAssetsResolver implements AssetResolverInterface
|
||||
{
|
||||
protected $path_relative_to_web_root;
|
||||
|
||||
protected $assetsManager;
|
||||
|
||||
protected $cdnBaseUrl;
|
||||
|
||||
/**
|
||||
* Creates a new SmartyAssetsManager instance
|
||||
*
|
||||
* @param AssetManagerInterface $assetsManager an asset manager instance
|
||||
*/
|
||||
public function __construct(AssetManagerInterface $assetsManager)
|
||||
{
|
||||
$this->path_relative_to_web_root = ConfigQuery::read('asset_dir_from_web_root', 'assets');
|
||||
|
||||
$this->assetsManager = $assetsManager;
|
||||
|
||||
$this->cdnBaseUrl = ConfigQuery::read('cdn.assets-base-url', null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $url the fully qualified CDN URL that will be used to create doucments URL.
|
||||
*/
|
||||
public function setCdnBaseUrl($url)
|
||||
{
|
||||
$this->cdnBaseUrl = $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate an asset URL
|
||||
*
|
||||
* @param string $source a module code, or ParserInterface::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);
|
||||
|
||||
$templateDefinition = $parserInterface->getTemplateDefinition($sourceTemplateName);
|
||||
|
||||
$fileRoot = $this->resolveAssetSourcePathAndTemplate($source, $sourceTemplateName, $file, $parserInterface, $templateDefinition);
|
||||
|
||||
if (null !== $fileRoot) {
|
||||
$url = $this->assetsManager->processAsset(
|
||||
$fileRoot . DS . $file,
|
||||
$fileRoot,
|
||||
THELIA_WEB_DIR . $this->path_relative_to_web_root,
|
||||
$templateDefinition->getPath(),
|
||||
$source,
|
||||
URL::getInstance()->absoluteUrl($this->path_relative_to_web_root, null, URL::PATH_TO_FILE /* path only */, $this->cdnBaseUrl),
|
||||
$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 ParserInterface::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)
|
||||
{
|
||||
$tpl = $parserInterface->getTemplateDefinition(false);
|
||||
|
||||
return $this->resolveAssetSourcePathAndTemplate(
|
||||
$source,
|
||||
$templateName,
|
||||
$fileName,
|
||||
$parserInterface,
|
||||
$tpl
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an asset source file path, and get the original template where it was found
|
||||
*
|
||||
* A system of fallback enables file overriding. It will look for the template :
|
||||
* - in the current template in directory /modules/{module code}/
|
||||
* - in the parent templates (if any) 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 ParserInterface::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
|
||||
* @param TemplateDefinition &$templateDefinition the template where to start search.
|
||||
* This parameter will contain the template where the asset was found.
|
||||
*
|
||||
* @return mixed the path to directory containing the file, or null if the file doesn't exists.
|
||||
*/
|
||||
public function resolveAssetSourcePathAndTemplate(
|
||||
$source,
|
||||
$templateName,
|
||||
$fileName,
|
||||
ParserInterface $parserInterface,
|
||||
TemplateDefinition &$templateDefinition
|
||||
) {
|
||||
// A simple cache for the path list, to gain some performances
|
||||
static $cache = [];
|
||||
|
||||
// The path are categorized, and will be checked in the following order. (see getPossibleAssetSources)
|
||||
// - template : the template directory
|
||||
// - module_override : the module override directory (template/modules/module_name/...)
|
||||
// - module_directory : the current template in the module directory
|
||||
// - the default template in the module directory.
|
||||
static $pathOrigin = ['template', 'module_override', 'module_directory', 'default_fallback'];
|
||||
|
||||
$templateName = $templateName ?: $templateDefinition->getName();
|
||||
|
||||
$templateDirectories = $parserInterface->getTemplateDirectories($templateDefinition->getType());
|
||||
|
||||
$hash = "$source|$templateName";
|
||||
|
||||
if (! isset($cache[$hash])) {
|
||||
// Build a list of all template names, starting with current template
|
||||
$templateList = [$templateName => $templateDefinition];
|
||||
|
||||
/** @var TemplateDefinition $tplDef */
|
||||
foreach ($templateDefinition->getParentList() as $tplDef) {
|
||||
$templateList[$tplDef->getName()] = $tplDef;
|
||||
}
|
||||
|
||||
$pathList = [];
|
||||
|
||||
// Get all possible directories to search, including the parent templates ones.
|
||||
// The current template is checked firts, then the parent ones.
|
||||
|
||||
/** @var TemplateDefinition $templateDef */
|
||||
foreach ($templateList as $tplName => $dummy) {
|
||||
$this->getPossibleAssetSources(
|
||||
$templateDirectories,
|
||||
$tplName,
|
||||
$source,
|
||||
$pathList
|
||||
);
|
||||
}
|
||||
|
||||
$cache[$hash] = [ $pathList, $templateList ];
|
||||
} else {
|
||||
list($pathList, $templateList) = $cache[$hash];
|
||||
}
|
||||
|
||||
// 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 directory list.
|
||||
foreach ($pathOrigin as $origin) {
|
||||
if (isset($pathList[$origin])) {
|
||||
foreach ($pathList[$origin] as $pathInfo) {
|
||||
list($tplName, $path) = $pathInfo;
|
||||
|
||||
if ($this->filesExist($path, $fileName)) {
|
||||
// Got it ! Save the template where the asset was found and return !
|
||||
$templateDefinition = $templateList[$tplName];
|
||||
|
||||
return $path;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Not found !
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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 $templateName the name of the template
|
||||
* @param string $source the module code or ParserInterface::TEMPLATE_ASSETS_KEY
|
||||
* @param array $pathList the pathList that will be updated
|
||||
*/
|
||||
protected function getPossibleAssetSources($directories, $templateName, $source, &$pathList)
|
||||
{
|
||||
if ($source !== ParserInterface::TEMPLATE_ASSETS_KEY) {
|
||||
// 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[$templateName][ParserInterface::TEMPLATE_ASSETS_KEY])) {
|
||||
$pathList['module_override'][] = [
|
||||
$templateName,
|
||||
$directories[$templateName][ParserInterface::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[$templateName][$source])) {
|
||||
$pathList['module_directory'][] = [ $templateName, $directories[$templateName][$source] ];
|
||||
}
|
||||
|
||||
// then in the default theme in the module itself
|
||||
if (isset($directories[self::DEFAULT_TEMPLATE_NAME][$source])) {
|
||||
$pathList['default_fallback'][] = [ $templateName, $directories[self::DEFAULT_TEMPLATE_NAME][$source] ];
|
||||
}
|
||||
} else {
|
||||
$pathList['template'][] = [ $templateName, $directories[$templateName][$source] ];
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user