Initial commit
This commit is contained in:
680
typo3conf/ext/vhs/Classes/Service/AssetService.php
Normal file
680
typo3conf/ext/vhs/Classes/Service/AssetService.php
Normal file
@@ -0,0 +1,680 @@
|
||||
<?php
|
||||
namespace FluidTYPO3\Vhs\Service;
|
||||
|
||||
/*
|
||||
* This file is part of the FluidTYPO3/Vhs project under GPLv2 or later.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.md file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
use FluidTYPO3\Vhs\Asset;
|
||||
use FluidTYPO3\Vhs\ViewHelpers\Asset\AssetInterface;
|
||||
use TYPO3\CMS\Core\SingletonInterface;
|
||||
use TYPO3\CMS\Core\Utility\ArrayUtility;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface;
|
||||
use TYPO3\CMS\Extbase\Object\ObjectManager;
|
||||
use TYPO3\CMS\Extbase\Object\ObjectManagerInterface;
|
||||
use TYPO3\CMS\Extbase\Utility\DebuggerUtility;
|
||||
|
||||
/**
|
||||
* Asset Handling Service
|
||||
*
|
||||
* Inject this Service in your class to access VHS Asset
|
||||
* features - include assets etc.
|
||||
*
|
||||
* @author Claus Due <claus@namelesscoder.net>
|
||||
* @package Vhs
|
||||
* @subpackage Service
|
||||
*/
|
||||
class AssetService implements SingletonInterface {
|
||||
|
||||
/**
|
||||
* @var boolean
|
||||
*/
|
||||
protected static $typoScriptAssetsBuilt = FALSE;
|
||||
|
||||
/**
|
||||
* @var \TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface
|
||||
*/
|
||||
protected $configurationManager;
|
||||
|
||||
/**
|
||||
* @var \TYPO3\CMS\Extbase\Object\ObjectManagerInterface
|
||||
*/
|
||||
protected $objectManager;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $settingsCache = NULL;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $cachedDependencies = array();
|
||||
|
||||
/**
|
||||
* @var boolean
|
||||
*/
|
||||
private static $cacheCleared = FALSE;
|
||||
|
||||
/**
|
||||
* @param \TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface $configurationManager
|
||||
* @return void
|
||||
*/
|
||||
public function injectConfigurationManager(ConfigurationManagerInterface $configurationManager) {
|
||||
$this->configurationManager = $configurationManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \TYPO3\CMS\Extbase\Object\ObjectManagerInterface $objectManager
|
||||
* @return void
|
||||
*/
|
||||
public function injectObjectManager(ObjectManagerInterface $objectManager) {
|
||||
$this->objectManager = $objectManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param object $caller
|
||||
* @param boolean $shouldUsePageCache
|
||||
* @return boolean
|
||||
*/
|
||||
public function usePageCache($caller, $shouldUsePageCache) {
|
||||
$this->buildAll(array(), $caller);
|
||||
return $shouldUsePageCache;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $parameters
|
||||
* @param object $caller
|
||||
* @param boolean $cached If TRUE, treats this inclusion as happening in a cached context
|
||||
* @return void
|
||||
*/
|
||||
public function buildAll(array $parameters, $caller, $cached = TRUE) {
|
||||
if (FALSE === $this->objectManager instanceof ObjectManager) {
|
||||
$this->objectManager = GeneralUtility::makeInstance('TYPO3\\CMS\\Extbase\\Object\\ObjectManager');
|
||||
$this->configurationManager = $this->objectManager->get('TYPO3\\CMS\\Extbase\\Configuration\\ConfigurationManagerInterface');
|
||||
}
|
||||
$settings = $this->getSettings();
|
||||
$cached = (boolean) $cached;
|
||||
$buildTypoScriptAssets = (FALSE === self::$typoScriptAssetsBuilt && (TRUE === $cached || TRUE === (boolean) $GLOBALS['TSFE']->no_cache));
|
||||
if (TRUE === $buildTypoScriptAssets && TRUE === isset($settings['asset']) && TRUE === is_array($settings['asset'])) {
|
||||
foreach ($settings['asset'] as $name => $typoScriptAsset) {
|
||||
if (FALSE === isset($GLOBALS['VhsAssets'][$name]) && TRUE === is_array($typoScriptAsset)) {
|
||||
if (FALSE === isset($typoScriptAsset['name'])) {
|
||||
$typoScriptAsset['name'] = $name;
|
||||
}
|
||||
Asset::createFromSettings($typoScriptAsset);
|
||||
}
|
||||
}
|
||||
self::$typoScriptAssetsBuilt = TRUE;
|
||||
}
|
||||
if (FALSE === isset($GLOBALS['VhsAssets']) || FALSE === is_array($GLOBALS['VhsAssets'])) {
|
||||
return;
|
||||
}
|
||||
$assets = $GLOBALS['VhsAssets'];
|
||||
$assets = $this->sortAssetsByDependency($assets);
|
||||
$assets = $this->manipulateAssetsByTypoScriptSettings($assets);
|
||||
$buildDebugRequested = (isset($settings['asset']['debugBuild']) && $settings['asset']['debugBuild'] > 0);
|
||||
$assetDebugRequested = (isset($settings['asset']['debug']) && $settings['asset']['debug'] > 0);
|
||||
$useDebugUtility = (isset($settings['asset']['useDebugUtility']) && $settings['asset']['useDebugUtility'] > 0) || FALSE === isset($settings['asset']['useDebugUtility']);
|
||||
if (TRUE === ($buildDebugRequested || $assetDebugRequested)) {
|
||||
if (TRUE === $useDebugUtility) {
|
||||
DebuggerUtility::var_dump($assets);
|
||||
} else {
|
||||
echo var_export($assets, TRUE);
|
||||
}
|
||||
}
|
||||
$this->placeAssetsInHeaderAndFooter($assets, $cached);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $parameters
|
||||
* @param object $caller
|
||||
* @return void
|
||||
*/
|
||||
public function buildAllUncached(array $parameters, $caller) {
|
||||
$content = $caller->content;
|
||||
$matches = array();
|
||||
preg_match_all('/\<\![\-]+\ VhsAssetsDependenciesLoaded ([^ ]+) [\-]+\>/i', $content, $matches);
|
||||
foreach ($matches[1] as $key => $match) {
|
||||
$extractedDependencies = explode(',', $matches[1][$key]);
|
||||
self::$cachedDependencies = array_merge(self::$cachedDependencies, $extractedDependencies);
|
||||
$content = str_replace($matches[0][$key], '', $content);
|
||||
}
|
||||
$caller->content = $content;
|
||||
$this->buildAll($parameters, $caller, FALSE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the settings used by this particular Asset
|
||||
* during inclusion. Public access allows later inspection
|
||||
* of the TypoScript values which were applied to the Asset.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getSettings() {
|
||||
if (NULL === self::$settingsCache) {
|
||||
$allTypoScript = $this->configurationManager->getConfiguration(ConfigurationManagerInterface::CONFIGURATION_TYPE_FULL_TYPOSCRIPT);
|
||||
$settingsExist = isset($allTypoScript['plugin.']['tx_vhs.']['settings.']);
|
||||
if (FALSE === $settingsExist) {
|
||||
// no settings exist, but don't allow a NULL value. This prevents cache clobbering.
|
||||
self::$settingsCache = array();
|
||||
} else {
|
||||
self::$settingsCache = GeneralUtility::removeDotsFromTS($allTypoScript['plugin.']['tx_vhs.']['settings.']);
|
||||
}
|
||||
}
|
||||
$settings = (array) self::$settingsCache;
|
||||
return $settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param AssetInterface[] $assets
|
||||
* @param boolean $cached
|
||||
* @return void
|
||||
*/
|
||||
protected function placeAssetsInHeaderAndFooter($assets, $cached) {
|
||||
$settings = $this->getSettings();
|
||||
$header = array();
|
||||
$footer = array();
|
||||
$footerRelocationEnabled = (TRUE === isset($settings['enableFooterRelocation']) && $settings['relocateToFooter'] > 0) || FALSE === isset($settings['enableFooterRelocation']);
|
||||
foreach ($assets as $name => $asset) {
|
||||
$variables = $asset->getVariables();
|
||||
if (0 < count($variables)) {
|
||||
$name .= '-' . md5(serialize($variables));
|
||||
}
|
||||
if (TRUE === ($this->assertAssetAllowedInFooter($asset) && $footerRelocationEnabled)) {
|
||||
$footer[$name] = $asset;
|
||||
} else {
|
||||
$header[$name] = $asset;
|
||||
}
|
||||
}
|
||||
if (FALSE === $cached) {
|
||||
$uncachedSuffix = 'Uncached';
|
||||
} else {
|
||||
$uncachedSuffix = '';
|
||||
$dependenciesString = '<!-- VhsAssetsDependenciesLoaded ' . implode(',', array_keys($assets)) . ' -->';
|
||||
$this->insertAssetsAtMarker('DependenciesLoaded', $dependenciesString);
|
||||
}
|
||||
$this->insertAssetsAtMarker('Header' . $uncachedSuffix, $header);
|
||||
$this->insertAssetsAtMarker('Footer' . $uncachedSuffix, $footer);
|
||||
$GLOBALS['VhsAssets'] = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $markerName
|
||||
* @param mixed $assets
|
||||
* @return void
|
||||
*/
|
||||
protected function insertAssetsAtMarker($markerName, $assets) {
|
||||
$assetMarker = '<!-- VhsAssets' . $markerName . ' -->';
|
||||
if (FALSE === strpos($GLOBALS['TSFE']->content, $assetMarker)) {
|
||||
$inFooter = (boolean) (FALSE !== strpos($markerName, 'Footer'));
|
||||
$tag = TRUE === $inFooter ? '</body>' : '</head>';
|
||||
$GLOBALS['TSFE']->content = str_replace($tag, $assetMarker . LF . $tag, $GLOBALS['TSFE']->content);
|
||||
}
|
||||
if (TRUE === is_array($assets)) {
|
||||
$chunk = $this->buildAssetsChunk($assets);
|
||||
} else {
|
||||
$chunk = $assets;
|
||||
}
|
||||
$GLOBALS['TSFE']->content = str_replace($assetMarker, $chunk, $GLOBALS['TSFE']->content);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $assets
|
||||
* @throws \RuntimeException
|
||||
* @return string
|
||||
*/
|
||||
protected function buildAssetsChunk($assets) {
|
||||
$spool = array();
|
||||
foreach ($assets as $name => $asset) {
|
||||
$assetSettings = $this->extractAssetSettings($asset);
|
||||
$type = $assetSettings['type'];
|
||||
if (FALSE === isset($spool[$type])) {
|
||||
$spool[$type] = array();
|
||||
}
|
||||
$spool[$type][$name] = $asset;
|
||||
}
|
||||
$chunks = array();
|
||||
foreach ($spool as $type => $spooledAssets) {
|
||||
$chunk = array();
|
||||
/** @var AssetInterface[] $spooledAssets */
|
||||
foreach ($spooledAssets as $name => $asset) {
|
||||
$assetSettings = $this->extractAssetSettings($asset);
|
||||
$standalone = (boolean) $assetSettings['standalone'];
|
||||
$external = (boolean) $assetSettings['external'];
|
||||
$rewrite = (boolean) $assetSettings['rewrite'];
|
||||
$path = $assetSettings['path'];
|
||||
if (FALSE === $standalone) {
|
||||
$chunk[$name] = $asset;
|
||||
} else {
|
||||
if (0 < count($chunk)) {
|
||||
$mergedFileTag = $this->writeCachedMergedFileAndReturnTag($chunk, $type);
|
||||
array_push($chunks, $mergedFileTag);
|
||||
$chunk = array();
|
||||
}
|
||||
if (TRUE === empty($path)) {
|
||||
$assetContent = $this->extractAssetContent($asset);
|
||||
array_push($chunks, $this->generateTagForAssetType($type, $assetContent));
|
||||
} else {
|
||||
if (TRUE === $external) {
|
||||
array_push($chunks, $this->generateTagForAssetType($type, NULL, $path));
|
||||
} else {
|
||||
if (TRUE === $rewrite) {
|
||||
array_push($chunks, $this->writeCachedMergedFileAndReturnTag(array($name => $asset), $type));
|
||||
} else {
|
||||
$path = substr($path, strlen(PATH_site));
|
||||
$path = $this->prefixPath($path);
|
||||
array_push($chunks, $this->generateTagForAssetType($type, NULL, $path));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (0 < count($chunk)) {
|
||||
$mergedFileTag = $this->writeCachedMergedFileAndReturnTag($chunk, $type);
|
||||
array_push($chunks, $mergedFileTag);
|
||||
}
|
||||
}
|
||||
return implode(LF, $chunks);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param AssetInterface[] $assets
|
||||
* @param string $type
|
||||
* @return string
|
||||
*/
|
||||
protected function writeCachedMergedFileAndReturnTag($assets, $type) {
|
||||
$source = '';
|
||||
$assetName = implode('-', array_keys($assets));
|
||||
if (TRUE === isset($GLOBALS['TSFE']->tmpl->setup['plugin.']['tx_vhs.']['assets.']['mergedAssetsUseHashedFilename'])) {
|
||||
if (TRUE === (boolean) $GLOBALS['TSFE']->tmpl->setup['plugin.']['tx_vhs.']['assets.']['mergedAssetsUseHashedFilename']) {
|
||||
$assetName = md5($assetName);
|
||||
}
|
||||
}
|
||||
$fileRelativePathAndFilename = 'typo3temp/vhs-assets-' . $assetName . '.' . $type;
|
||||
$fileAbsolutePathAndFilename = GeneralUtility::getFileAbsFileName($fileRelativePathAndFilename);
|
||||
if (
|
||||
FALSE === file_exists($fileAbsolutePathAndFilename)
|
||||
|| 0 === filemtime($fileAbsolutePathAndFilename)
|
||||
|| TRUE === isset($GLOBALS['BE_USER'])
|
||||
|| TRUE === (boolean) $GLOBALS['TSFE']->no_cache
|
||||
|| TRUE === (boolean) $GLOBALS['TSFE']->page['no_cache']
|
||||
) {
|
||||
foreach ($assets as $name => $asset) {
|
||||
$assetSettings = $this->extractAssetSettings($asset);
|
||||
if (TRUE === (isset($assetSettings['namedChunks']) && 0 < $assetSettings['namedChunks']) || FALSE === isset($assetSettings['namedChunks'])) {
|
||||
$source .= '/* ' . $name . ' */' . LF;
|
||||
}
|
||||
$source .= $this->extractAssetContent($asset) . LF;
|
||||
// Put a return carriage between assets preventing broken content.
|
||||
$source .= "\n";
|
||||
}
|
||||
$this->writeFile($fileAbsolutePathAndFilename, $source);
|
||||
}
|
||||
if (FALSE === empty($GLOBALS['TYPO3_CONF_VARS']['FE']['versionNumberInFilename'])) {
|
||||
$timestampMode = $GLOBALS['TYPO3_CONF_VARS']['FE']['versionNumberInFilename'];
|
||||
if (TRUE === file_exists($fileRelativePathAndFilename)) {
|
||||
$lastModificationTime = filemtime($fileRelativePathAndFilename);
|
||||
if ('querystring' === $timestampMode) {
|
||||
$fileRelativePathAndFilename .= '?' . $lastModificationTime;
|
||||
} elseif ('embed' === $timestampMode) {
|
||||
$fileRelativePathAndFilename = substr_replace($fileRelativePathAndFilename, '.' . $lastModificationTime, strrpos($fileRelativePathAndFilename, '.'), 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
$fileRelativePathAndFilename = $this->prefixPath($fileRelativePathAndFilename);
|
||||
return $this->generateTagForAssetType($type, NULL, $fileRelativePathAndFilename);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $type
|
||||
* @param string $content
|
||||
* @param string $file
|
||||
* @throws \RuntimeException
|
||||
* @return string
|
||||
*/
|
||||
protected function generateTagForAssetType($type, $content, $file = NULL) {
|
||||
/** @var \TYPO3\CMS\Fluid\Core\ViewHelper\TagBuilder $tagBuilder */
|
||||
$tagBuilder = $this->objectManager->get('TYPO3\\CMS\\Fluid\\Core\\ViewHelper\\TagBuilder');
|
||||
if (NULL === $file && TRUE === empty($content)) {
|
||||
$content = '<!-- Empty tag content -->';
|
||||
}
|
||||
switch ($type) {
|
||||
case 'js':
|
||||
$tagBuilder->setTagName('script');
|
||||
$tagBuilder->forceClosingTag(TRUE);
|
||||
$tagBuilder->addAttribute('type', 'text/javascript');
|
||||
if (NULL === $file) {
|
||||
$tagBuilder->setContent($content);
|
||||
} else {
|
||||
$tagBuilder->addAttribute('src', $file);
|
||||
}
|
||||
break;
|
||||
case 'css':
|
||||
if (NULL === $file) {
|
||||
$tagBuilder->setTagName('style');
|
||||
$tagBuilder->forceClosingTag(TRUE);
|
||||
$tagBuilder->addAttribute('type', 'text/css');
|
||||
$tagBuilder->setContent($content);
|
||||
} else {
|
||||
$tagBuilder->forceClosingTag(FALSE);
|
||||
$tagBuilder->setTagName('link');
|
||||
$tagBuilder->addAttribute('rel', 'stylesheet');
|
||||
$tagBuilder->addAttribute('href', $file);
|
||||
}
|
||||
break;
|
||||
case 'meta':
|
||||
$tagBuilder->forceClosingTag(FALSE);
|
||||
$tagBuilder->setTagName('meta');
|
||||
break;
|
||||
default:
|
||||
if (NULL === $file) {
|
||||
return $content;
|
||||
} else {
|
||||
throw new \RuntimeException('Attempt to include file based asset with unknown type ("' . $type . '")', 1358645219);
|
||||
}
|
||||
break;
|
||||
}
|
||||
return $tagBuilder->render();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $assets
|
||||
* @return array
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
protected function manipulateAssetsByTypoScriptSettings($assets) {
|
||||
$settings = $this->getSettings();
|
||||
if (FALSE === (isset($settings['asset']) || isset($settings['assetGroup']))) {
|
||||
return $assets;
|
||||
}
|
||||
$filtered = array();
|
||||
/** @var \FluidTYPO3\Vhs\Asset $asset */
|
||||
foreach ($assets as $name => $asset) {
|
||||
$assetSettings = $this->extractAssetSettings($asset);
|
||||
$groupName = $assetSettings['group'];
|
||||
$removed = (boolean) (TRUE === isset($assetSettings['removed']) ? $assetSettings['removed'] : FALSE);
|
||||
if (TRUE === $removed) {
|
||||
continue;
|
||||
}
|
||||
$localSettings = (array) $assetSettings;
|
||||
if (TRUE === isset($settings['asset'])) {
|
||||
$localSettings = $this->mergeArrays($localSettings, (array) $settings['asset']);
|
||||
}
|
||||
if (TRUE === isset($settings['asset'][$name])) {
|
||||
$localSettings = $this->mergeArrays($localSettings, (array) $settings['asset'][$name]);
|
||||
}
|
||||
if (TRUE === isset($settings['assetGroup'][$groupName])) {
|
||||
$localSettings = $this->mergeArrays($localSettings, (array) $settings['assetGroup'][$groupName]);
|
||||
}
|
||||
if (TRUE === $asset instanceof AssetInterface) {
|
||||
$asset->setSettings($localSettings);
|
||||
$filtered[$name] = $asset;
|
||||
} else {
|
||||
$filtered[$name] = $assetSettings;
|
||||
}
|
||||
}
|
||||
return $filtered;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param AssetInterface[] $assets
|
||||
* @throws \RuntimeException
|
||||
* @return AssetInterface[]
|
||||
*/
|
||||
protected function sortAssetsByDependency($assets) {
|
||||
$placed = array();
|
||||
$assetNames = (0 < count($assets)) ? array_combine(array_keys($assets), array_keys($assets)) : array();
|
||||
while ($asset = array_shift($assets)) {
|
||||
$postpone = FALSE;
|
||||
/** @var AssetInterface $asset */
|
||||
$assetSettings = $this->extractAssetSettings($asset);
|
||||
$name = array_shift($assetNames);
|
||||
$dependencies = $assetSettings['dependencies'];
|
||||
if (FALSE === is_array($dependencies)) {
|
||||
$dependencies = GeneralUtility::trimExplode(',', $assetSettings['dependencies'], TRUE);
|
||||
}
|
||||
foreach ($dependencies as $dependency) {
|
||||
if (
|
||||
TRUE === array_key_exists($dependency, $assets)
|
||||
&& FALSE === isset($placed[$dependency])
|
||||
&& FALSE === in_array($dependency, self::$cachedDependencies)
|
||||
) {
|
||||
// shove the Asset back to the end of the queue, the dependency has
|
||||
// not yet been encountered and moving this item to the back of the
|
||||
// queue ensures it will be encountered before re-encountering this
|
||||
// specific Asset
|
||||
if (0 === count($assets)) {
|
||||
throw new \RuntimeException('Asset "' . $name . '" depends on "' . $dependency . '" but "' . $dependency . '" was not found', 1358603979);
|
||||
}
|
||||
$assets[$name] = $asset;
|
||||
$assetNames[$name] = $name;
|
||||
$postpone = TRUE;
|
||||
}
|
||||
}
|
||||
if (FALSE === $postpone) {
|
||||
$placed[$name] = $asset;
|
||||
}
|
||||
}
|
||||
return $placed;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $asset
|
||||
* @return string
|
||||
*/
|
||||
protected function renderAssetAsFluidTemplate($asset) {
|
||||
$settings = $this->extractAssetSettings($asset);
|
||||
$variables = (TRUE === (isset($settings['variables']) && is_array($settings['variables'])) ? $settings['variables'] : array());
|
||||
$contents = $this->buildAsset($asset);
|
||||
$variables = GeneralUtility::removeDotsFromTS($variables);
|
||||
/** @var \TYPO3\CMS\Fluid\View\StandaloneView $view */
|
||||
$view = $this->objectManager->get('TYPO3\\CMS\\Fluid\\View\\StandaloneView');
|
||||
$view->setTemplateSource($contents);
|
||||
$view->assignMultiple($variables);
|
||||
$content = $view->render();
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefix a path according to "absRefPrefix" TS configuration.
|
||||
*
|
||||
* @param string $fileRelativePathAndFilename
|
||||
* @return string
|
||||
*/
|
||||
protected function prefixPath($fileRelativePathAndFilename) {
|
||||
$settings = $this->getSettings();
|
||||
$prefixPath = $settings['prependPath'];
|
||||
if (FALSE === empty($GLOBALS['TSFE']->absRefPrefix) && TRUE === empty($prefixPath)) {
|
||||
$fileRelativePathAndFilename = $GLOBALS['TSFE']->absRefPrefix . $fileRelativePathAndFilename;
|
||||
} elseif (FALSE === empty($prefixPath)) {
|
||||
$fileRelativePathAndFilename = $prefixPath . $fileRelativePathAndFilename;
|
||||
}
|
||||
return $fileRelativePathAndFilename;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fixes the relative paths inside of url() references in CSS files
|
||||
*
|
||||
* @param string $contents Data to process
|
||||
* @param string $originalDirectory Original location of file
|
||||
* @return string Processed data
|
||||
*/
|
||||
protected function detectAndCopyFileReferences($contents, $originalDirectory) {
|
||||
if (FALSE !== stripos($contents, 'url')) {
|
||||
$regex = '/url(\\(\\s*["\']?(?!\\/)([^"\']+)["\']?\\s*\\))/iU';
|
||||
$contents = $this->copyReferencedFilesAndReplacePaths($contents, $regex, $originalDirectory, '(\'|\')');
|
||||
}
|
||||
if (FALSE !== stripos($contents, '@import')) {
|
||||
$regex = '/@import\\s*(["\']?(?!\\/)([^"\']+)["\']?)/i';
|
||||
$contents = $this->copyReferencedFilesAndReplacePaths($contents, $regex, $originalDirectory, '"|"');
|
||||
}
|
||||
return $contents;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds and replaces all URLs by using a given regex
|
||||
*
|
||||
* @param string $contents Data to process
|
||||
* @param string $regex Regex used to find URLs in content
|
||||
* @param string $originalDirectory Original location to CSS file, if file based.
|
||||
* @param string $wrap Wrap around replaced values
|
||||
* @return string Processed data
|
||||
*/
|
||||
protected function copyReferencedFilesAndReplacePaths($contents, $regex, $originalDirectory, $wrap = '|') {
|
||||
$matches = array();
|
||||
$replacements = array();
|
||||
$wrap = explode('|', $wrap);
|
||||
preg_match_all($regex, $contents, $matches);
|
||||
foreach ($matches[2] as $matchCount => $match) {
|
||||
$match = trim($match, '\'" ');
|
||||
if (FALSE === strpos($match, ':') && !preg_match('/url\\s*\\(/i', $match)) {
|
||||
$checksum = md5($originalDirectory . $match);
|
||||
if (0 < preg_match('/([^\?#]+)(.+)?/', $match, $items)) {
|
||||
list(, $path, $suffix) = $items;
|
||||
} else {
|
||||
$path = $match;
|
||||
$suffix = '';
|
||||
}
|
||||
$newPath = basename($path);
|
||||
$extension = pathinfo($newPath, PATHINFO_EXTENSION);
|
||||
$temporaryFileName = 'vhs-assets-css-' . $checksum . '.' . $extension;
|
||||
$temporaryFile = constant('PATH_site') . 'typo3temp/' . $temporaryFileName;
|
||||
$rawPath = GeneralUtility::getFileAbsFileName($originalDirectory . (TRUE === empty($originalDirectory) ? '' : '/')) . $path;
|
||||
$realPath = realpath($rawPath);
|
||||
if (FALSE === $realPath) {
|
||||
GeneralUtility::sysLog('Asset at path "' . $rawPath . '" not found. Processing skipped.', 'vhs', GeneralUtility::SYSLOG_SEVERITY_WARNING);
|
||||
} else {
|
||||
if (FALSE === file_exists($temporaryFile)) {
|
||||
copy($realPath, $temporaryFile);
|
||||
}
|
||||
$replacements[$matches[1][$matchCount]] = $wrap[0] . $temporaryFileName . $suffix . $wrap[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
if (FALSE === empty($replacements)) {
|
||||
$contents = str_replace(array_keys($replacements), array_values($replacements), $contents);
|
||||
}
|
||||
return $contents;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $asset An Asset ViewHelper instance or an array containing an Asset definition
|
||||
* @return boolean
|
||||
*/
|
||||
protected function assertAssetAllowedInFooter($asset) {
|
||||
if (TRUE === $asset instanceof AssetInterface) {
|
||||
return $asset->assertAllowedInFooter();
|
||||
}
|
||||
return (boolean) (TRUE === isset($asset['allowMoveToFooter']) ? $asset['allowMoveToFooter'] : TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $asset An Asset ViewHelper instance or an array containing an Asset definition
|
||||
* @return array
|
||||
*/
|
||||
protected function extractAssetSettings($asset) {
|
||||
if (TRUE === $asset instanceof AssetInterface) {
|
||||
return $asset->getAssetSettings();
|
||||
}
|
||||
return $asset;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $asset An Asset ViewHelper instance or an array containing an Asset definition
|
||||
* @return string
|
||||
*/
|
||||
protected function buildAsset($asset) {
|
||||
if (TRUE === $asset instanceof AssetInterface) {
|
||||
return $asset->build();
|
||||
}
|
||||
if (FALSE === isset($asset['path']) || TRUE === empty($asset['path'])) {
|
||||
return (TRUE === isset($asset['content']) ? $asset['content'] : NULL);
|
||||
}
|
||||
if (TRUE === isset($asset['external']) && TRUE === (boolean) $asset['external']) {
|
||||
$path = $asset['path'];
|
||||
} else {
|
||||
$path = GeneralUtility::getFileAbsFileName($asset['path']);
|
||||
}
|
||||
$content = file_get_contents($path);
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $asset
|
||||
* @throws \RuntimeException
|
||||
* @return string
|
||||
*/
|
||||
protected function extractAssetContent($asset) {
|
||||
$assetSettings = $this->extractAssetSettings($asset);
|
||||
$fileRelativePathAndFilename = $assetSettings['path'];
|
||||
$fileRelativePath = dirname($assetSettings['path']);
|
||||
$absolutePathAndFilename = GeneralUtility::getFileAbsFileName($fileRelativePathAndFilename);
|
||||
$isExternal = TRUE === isset($assetSettings['external']) && TRUE === (boolean) $assetSettings['external'];
|
||||
$isFluidTemplate = TRUE === isset($assetSettings['fluid']) && TRUE === (boolean) $assetSettings['fluid'];
|
||||
if (FALSE === empty($fileRelativePathAndFilename)) {
|
||||
if (FALSE === $isExternal && FALSE === file_exists($absolutePathAndFilename)) {
|
||||
throw new \RuntimeException('Asset "' . $absolutePathAndFilename . '" does not exist.');
|
||||
}
|
||||
if (TRUE === $isFluidTemplate) {
|
||||
$content = $this->renderAssetAsFluidTemplate($asset);
|
||||
} else {
|
||||
$content = $this->buildAsset($asset);
|
||||
}
|
||||
} else {
|
||||
$content = $this->buildAsset($asset);
|
||||
}
|
||||
if (('css' === $assetSettings['type']) && (TRUE === (boolean) $assetSettings['rewrite'])) {
|
||||
$content = $this->detectAndCopyFileReferences($content, $fileRelativePath);
|
||||
}
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $parameters
|
||||
* @return void
|
||||
*/
|
||||
public function clearCacheCommand($parameters) {
|
||||
if (TRUE === self::$cacheCleared) {
|
||||
return;
|
||||
}
|
||||
if ('all' !== $parameters['cacheCmd']) {
|
||||
return;
|
||||
}
|
||||
$assetCacheFiles = glob(GeneralUtility::getFileAbsFileName('typo3temp/vhs-assets-*'));
|
||||
if (FALSE === $assetCacheFiles) {
|
||||
return;
|
||||
}
|
||||
foreach ($assetCacheFiles as $assetCacheFile) {
|
||||
touch($assetCacheFile, 0);
|
||||
}
|
||||
self::$cacheCleared = TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $file
|
||||
* @param string $contents
|
||||
*/
|
||||
protected function writeFile($file, $contents) {
|
||||
file_put_contents($file, $contents);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $array1
|
||||
* @param $array2
|
||||
* @return array
|
||||
*/
|
||||
protected function mergeArrays($array1, $array2) {
|
||||
if (6.2 <= (float) substr(TYPO3_version, 0, 3)) {
|
||||
ArrayUtility::mergeRecursiveWithOverrule($array1, $array2);
|
||||
return $array1;
|
||||
} else {
|
||||
return GeneralUtility::array_merge_recursive_overrule($array1, $array2);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
218
typo3conf/ext/vhs/Classes/Service/PageSelectService.php
Normal file
218
typo3conf/ext/vhs/Classes/Service/PageSelectService.php
Normal file
@@ -0,0 +1,218 @@
|
||||
<?php
|
||||
namespace FluidTYPO3\Vhs\Service;
|
||||
|
||||
/*
|
||||
* This file is part of the FluidTYPO3/Vhs project under GPLv2 or later.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.md file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
use TYPO3\CMS\Core\SingletonInterface;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Frontend\Page\PageRepository;
|
||||
|
||||
/**
|
||||
* Page Select Service
|
||||
*
|
||||
* Wrapper service for \TYPO3\CMS\Frontend\Page\PageRepository including static caches for
|
||||
* menus, rootlines, pages and page overlays to be implemented in
|
||||
* viewhelpers by replacing calls to \TYPO3\CMS\Frontend\Page\PageRepository::getMenu()
|
||||
* and the like.
|
||||
*
|
||||
* @author Björn Fromme <fromme@dreipunktnull.com>, dreipunktnull
|
||||
* @package Vhs
|
||||
* @subpackage Service
|
||||
*/
|
||||
class PageSelectService implements SingletonInterface {
|
||||
|
||||
const DOKTYPE_MOVE_TO_PLACEHOLDER = 0;
|
||||
|
||||
/**
|
||||
* @var PageRepository
|
||||
*/
|
||||
protected static $pageSelect;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected static $cachedPages = array();
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected static $cachedOverlays = array();
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected static $cachedMenus = array();
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected static $cachedRootLines = array();
|
||||
|
||||
/**
|
||||
* Initialize \TYPO3\CMS\Frontend\Page\PageRepository objects
|
||||
*/
|
||||
public function initializeObject() {
|
||||
self::$pageSelect = $this->createPageSelectInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return PageRepository
|
||||
*/
|
||||
private function createPageSelectInstance() {
|
||||
if (TRUE === is_array($GLOBALS['TSFE']->fe_user->user)
|
||||
|| (TRUE === isset($GLOBALS['TSFE']->fe_user->groupData['uid']) && 0 < $GLOBALS['TSFE']->fe_user->groupData['uid'])) {
|
||||
$groups = array(-2, 0);
|
||||
} else {
|
||||
$groups = array(-1, 0);
|
||||
}
|
||||
|
||||
/** @var \TYPO3\CMS\Frontend\Page\PageRepository $pageSelect */
|
||||
$pageSelect = GeneralUtility::makeInstance('TYPO3\\CMS\\Frontend\\Page\\PageRepository');
|
||||
|
||||
if (TRUE === isset($GLOBALS['TSFE']->fe_user->groupData)) {
|
||||
$groups = array_merge($groups, array_values($GLOBALS['TSFE']->fe_user->groupData['uid']));
|
||||
}
|
||||
$clauses = array();
|
||||
foreach ($groups as $group) {
|
||||
$clause = "fe_group = '" . $group . "' OR fe_group LIKE '" .
|
||||
$group . ",%' OR fe_group LIKE '%," . $group . "' OR fe_group LIKE '%," . $group . ",%'";
|
||||
array_push($clauses, $clause);
|
||||
}
|
||||
array_push($clauses, "fe_group = '' OR fe_group = '0'");
|
||||
$pageSelect->where_groupAccess = ' AND (' . implode(' OR ', $clauses) . ')';
|
||||
$pageSelect->versioningPreview = (boolean) 0 < $GLOBALS['BE_USER']->workspace;
|
||||
$pageSelect->versioningWorkspaceId = (integer) $GLOBALS['BE_USER']->workspace;
|
||||
return $pageSelect;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper for \TYPO3\CMS\Frontend\Page\PageRepository::getPage()
|
||||
*
|
||||
* @param integer $pageUid
|
||||
* @return array
|
||||
*/
|
||||
public function getPage($pageUid = NULL) {
|
||||
if (NULL === $pageUid) {
|
||||
$pageUid = $GLOBALS['TSFE']->id;
|
||||
}
|
||||
if (FALSE === isset(self::$cachedPages[$pageUid])) {
|
||||
self::$cachedPages[$pageUid] = self::$pageSelect->getPage($pageUid);
|
||||
}
|
||||
return self::$cachedPages[$pageUid];
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper for \TYPO3\CMS\Frontend\Page\PageRepository::getPageOverlay()
|
||||
*
|
||||
* @param mixed $pageInput
|
||||
* @param integer $languageUid
|
||||
* @return array
|
||||
*/
|
||||
public function getPageOverlay($pageInput, $languageUid = -1) {
|
||||
$key = md5(serialize($pageInput) . $languageUid);
|
||||
if (FALSE === isset(self::$cachedOverlays[$key])) {
|
||||
self::$cachedOverlays[$key] = self::$pageSelect->getPageOverlay($pageInput, $languageUid);
|
||||
}
|
||||
return self::$cachedOverlays[$key];
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper for \TYPO3\CMS\Frontend\Page\PageRepository::getMenu()
|
||||
* Caution: different signature
|
||||
*
|
||||
* @param integer $pageUid
|
||||
* @param array $excludePages
|
||||
* @param string $where
|
||||
* @param boolean $showHiddenInMenu
|
||||
* @param boolean $checkShortcuts
|
||||
* @param array $allowedDoktypeList
|
||||
* @return array
|
||||
*/
|
||||
public function getMenu($pageUid = NULL, $excludePages = array(), $where = '', $showHiddenInMenu = FALSE, $checkShortcuts = FALSE, $allowedDoktypeList = array()) {
|
||||
if (NULL === $pageUid) {
|
||||
$pageUid = $GLOBALS['TSFE']->id;
|
||||
}
|
||||
$addWhere = self::$pageSelect->enableFields('pages', 0);
|
||||
if (0 < count($allowedDoktypeList)) {
|
||||
$addWhere .= ' AND doktype IN (' . implode(',', $allowedDoktypeList) . ')';
|
||||
} else {
|
||||
$addWhere .= ' AND doktype != ' . PageRepository::DOKTYPE_SYSFOLDER;
|
||||
}
|
||||
if (0 < count($excludePages)) {
|
||||
$addWhere .= ' AND uid NOT IN (' . implode(',', $excludePages) . ')';
|
||||
}
|
||||
if (FALSE === (boolean) $showHiddenInMenu) {
|
||||
$addWhere .= ' AND nav_hide=0';
|
||||
}
|
||||
if ('' !== $where) {
|
||||
$addWhere = $where . ' ' . $addWhere;
|
||||
}
|
||||
$key = md5(intval($showHiddenInMenu) . $pageUid . $addWhere . intval($checkShortcuts));
|
||||
if (FALSE === isset(self::$cachedMenus[$key])) {
|
||||
self::$cachedMenus[$key] = self::$pageSelect->getMenu($pageUid, '*', 'sorting', $addWhere, $checkShortcuts);
|
||||
}
|
||||
return self::$cachedMenus[$key];
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper for \TYPO3\CMS\Frontend\Page\PageRepository::getRootLine()
|
||||
*
|
||||
* @param integer $pageUid
|
||||
* @param string $MP
|
||||
* @param boolean $reverse
|
||||
* @return array
|
||||
*/
|
||||
public function getRootLine($pageUid = NULL, $MP = NULL, $reverse = FALSE) {
|
||||
if (NULL === $pageUid) {
|
||||
$pageUid = $GLOBALS['TSFE']->id;
|
||||
}
|
||||
if (NULL === $MP) {
|
||||
$MP = GeneralUtility::_GP('MP');
|
||||
if (TRUE === empty($MP)) {
|
||||
$MP = '';
|
||||
}
|
||||
}
|
||||
$key = md5($pageUid . $MP . (string) $reverse);
|
||||
if (FALSE === isset(self::$cachedRootLines[$key])) {
|
||||
$rootLine = self::$pageSelect->getRootLine($pageUid, $MP);
|
||||
if (TRUE === $reverse) {
|
||||
$rootLine = array_reverse($rootLine);
|
||||
}
|
||||
self::$cachedRootLines[$key] = $rootLine;
|
||||
}
|
||||
return self::$cachedRootLines[$key];
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a page for a specific language should be hidden
|
||||
*
|
||||
* @param integer $pageUid
|
||||
* @param integer $languageUid
|
||||
* @param boolean $normalWhenNoLanguage
|
||||
* @return boolean
|
||||
*/
|
||||
public function hidePageForLanguageUid($pageUid = 0, $languageUid = -1, $normalWhenNoLanguage = TRUE) {
|
||||
if (0 === $pageUid) {
|
||||
$pageUid = $GLOBALS['TSFE']->id;
|
||||
}
|
||||
if (-1 === $languageUid) {
|
||||
$languageUid = $GLOBALS['TSFE']->sys_language_uid;
|
||||
}
|
||||
$page = $this->getPage($pageUid);
|
||||
$l18nCfg = TRUE === isset($page['l18n_cfg']) ? $page['l18n_cfg'] : 0;
|
||||
$hideIfNotTranslated = (boolean) GeneralUtility::hideIfNotTranslated($l18nCfg);
|
||||
$hideIfDefaultLanguage = (boolean) GeneralUtility::hideIfDefaultLanguage($l18nCfg);
|
||||
$pageOverlay = 0 !== $languageUid ? $this->getPageOverlay($pageUid, $languageUid) : array();
|
||||
$translationAvailable = 0 !== count($pageOverlay);
|
||||
return
|
||||
(TRUE === $hideIfNotTranslated && 0 !== $languageUid && FALSE === $translationAvailable) ||
|
||||
(TRUE === $hideIfDefaultLanguage && (0 === $languageUid || FALSE === $translationAvailable)) ||
|
||||
(FALSE === $normalWhenNoLanguage && 0 !== $languageUid && FALSE === $translationAvailable);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user