mustache_templates-8.x-1.0-beta4/src/Summable/SummableScripts.php
src/Summable/SummableScripts.php
<?php
namespace Drupal\mustache\Summable;
use Drupal\Core\Asset\LibraryDiscoveryInterface;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Cache\CacheTagsInvalidatorInterface;
use Drupal\Core\File\FileSystemInterface;
use Drupal\Core\Theme\ActiveTheme;
use Drupal\Core\Theme\ThemeInitializationInterface;
use Drupal\Core\Theme\ThemeManagerInterface;
use Drupal\mustache\Exception\MustacheFileException;
use Drupal\mustache\Exception\MustacheTemplateNotFoundException;
use Drupal\mustache\Helpers\MustacheRenderTemplate;
use Drupal\mustache\MustacheTemplates;
/**
* Class for providing Mustache templates as summable script files.
*/
class SummableScripts implements SummableScriptsInterface {
/**
* Whether the usage of summable script files is enabled or not.
*
* @var bool
*/
protected $enabled;
/**
* The file path where to store generated script files.
*
* @var string
*/
protected $jsPath;
/**
* The cache backend to store summable script information.
*
* @var \Drupal\Core\Cache\CacheBackendInterface
*/
protected $cache;
/**
* The finder of Mustache templates.
*
* @var \Drupal\mustache\MustacheTemplates
*/
protected $templates;
/**
* The library discovery service.
*
* @var \Drupal\Core\Asset\LibraryDiscoveryInterface
*/
protected $libraryDiscovery;
/**
* The theme manager.
*
* @var \Drupal\Core\Theme\ThemeManagerInterface
*/
protected $themeManager;
/**
* The theme initialization service.
*
* @var \Drupal\Core\Theme\ThemeInitializationInterface
*/
protected $themeInitialization;
/**
* The file system service.
*
* @var \Drupal\Core\File\FileSystemInterface
*/
protected $fileSystem;
/**
* A list of libraries for every template as summable script file.
*
* @var array
*/
protected $libraries;
/**
* A list of known template libraries currently being used per theme.
*
* @var array
*/
protected $current;
/**
* SummableScripts constructor.
*
* @param bool $enabled
* Whether the usage of summable script files is enabled or not.
* @param string $js_path
* The file path where to store generated script files.
* @param \Drupal\Core\Cache\CacheBackendInterface $cache
* The cache backend to store summable script information.
* @param \Drupal\mustache\MustacheTemplates $templates
* The finder of Mustache templates.
* @param \Drupal\Core\Asset\LibraryDiscoveryInterface $library_discovery
* The library discovery service.
* @param \Drupal\Core\Theme\ThemeManagerInterface $theme_manager
* The theme manager.
* @param \Drupal\Core\Theme\ThemeInitializationInterface $theme_initialization
* The theme initialization service.
* @param \Drupal\Core\File\FileSystemInterface $file_system
* The file system service.
*/
public function __construct($enabled, $js_path, CacheBackendInterface $cache, MustacheTemplates $templates, LibraryDiscoveryInterface $library_discovery, ThemeManagerInterface $theme_manager, ThemeInitializationInterface $theme_initialization, FileSystemInterface $file_system) {
$this->enabled = $enabled;
$this->jsPath = $js_path;
$this->cache = $cache;
$this->templates = $templates;
$this->libraryDiscovery = $library_discovery;
$this->themeManager = $theme_manager;
$this->themeInitialization = $theme_initialization;
$this->fileSystem = $file_system;
}
/**
* {@inheritdoc}
*/
public function isEnabled() {
return $this->enabled;
}
/**
* {@inheritdoc}
*/
public function getLibraryName($template, ActiveTheme $theme = NULL) {
$library_name = FALSE;
$theme = $theme ?: $this->themeManager->getActiveTheme();
$theme_name = $theme->getName();
$cid = 'mustache:libraries:' . $theme_name;
$regenerate = FALSE;
if (!isset($this->current[$theme_name])) {
if ($cached = $this->cache->get($cid)) {
$this->current[$theme_name] = $cached->data;
}
}
if (isset($this->current[$theme_name][$template])) {
$library_name = $this->current[$theme_name][$template];
}
else {
$regenerate = TRUE;
$candidates = ['template.' . $theme_name . '.' . $template];
foreach ($theme->getBaseThemeExtensions() as $base_theme) {
$candidates[] = 'template.' . $base_theme->getName() . '.' . $template;
}
$candidates[] = 'template.module.' . $template;
foreach ($candidates as $candidate) {
if ($this->libraryDiscovery->getLibraryByName('mustache', $candidate)) {
$library_name = 'mustache/' . $candidate;
break;
}
}
$this->current[$theme_name][$template] = $library_name;
$this->cache->set($cid, $this->current[$theme_name], CacheBackendInterface::CACHE_PERMANENT, ['mustache:libraries']);
}
if (!$library_name) {
throw new MustacheFileException(t('Cannot provide summable script file, because no library has been found for template @template.', ['@template' => $template]));
}
// Make sure the script file has been generated and is available.
if (!$this->generate($library_name, $regenerate)) {
throw new MustacheFileException(t('Failed to generate summable script file for template @template', ['@template' => $template]));
}
return $library_name;
}
/**
* {@inheritdoc}
*/
public function getAllLibraries() {
if (!isset($this->libraries)) {
$cid = 'mustache:libraries';
if ($cached = $this->cache->get($cid)) {
$this->libraries = $cached->data;
}
else {
$libraries = [];
$with_partials = [];
foreach ($this->templates->findAll() as $name => $items) {
$defaults = $this->templates->getElementDefaults($name);
foreach ($items as $item) {
$library_name = 'template.' . $item['provider'] . '.' . $name;
$js_file_uri = $this->buildUri($item['provider'], $name);
$js_file_path = \Drupal::service('file_url_generator')->generateString($js_file_uri);
$libraries[$library_name] = [
'js' => [$js_file_path => []],
'dependencies' => ['mustache/sync.now'],
];
if (!empty($defaults['#partials'])) {
$with_partials[$library_name] = [
'provider' => $item['provider'],
'partials' => $defaults['#partials'],
];
}
}
}
foreach ($with_partials as $library_name => $detail) {
$candidates = [];
if ($detail['provider'] !== 'module') {
$candidates[] = $detail['provider'];
$theme = $this->themeInitialization->getActiveThemeByName($detail['provider']);
$candidates = array_merge($candidates, array_keys($theme->getBaseThemeExtensions()));
}
$candidates[] = 'module';
foreach ($detail['partials'] as $partial_name) {
$partial_defaults = $this->templates->getElementDefaults($partial_name);
if (isset($partial_defaults['#summable']) && empty($partial_defaults['#summable'])) {
continue;
}
foreach ($candidates as $candidate) {
$partial_library_candidate = 'template.' . $candidate . '.' . $partial_name;
if (isset($libraries[$partial_library_candidate])) {
$libraries[$library_name]['dependencies'][] = $partial_library_candidate;
break;
}
}
}
}
$this->cache->set($cid, $libraries, CacheBackendInterface::CACHE_PERMANENT, ['mustache:libraries']);
$this->libraries = $libraries;
}
}
return $this->libraries;
}
/**
* {@inheritdoc}
*/
public function buildUri($provider, $template_name) {
return str_replace('-', '_', $this->jsPath . '/' . $provider . '/' . $template_name . '.js');
}
/**
* {@inheritdoc}
*/
public function generate($library_name, $regenerate = FALSE) {
$parts = explode('.', $library_name);
// First part would always be "template".
array_shift($parts);
// The second part is either "module" or a theme name.
$provider = array_shift($parts);
// The rest is the actual name of the template.
$template_name = implode('.', $parts);
$script_file_uri = $this->buildUri($provider, $template_name);
if (!$regenerate && file_exists($script_file_uri)) {
return TRUE;
}
$template_content = NULL;
if ($provider === 'module') {
$module_templates = $this->templates->getModuleTemplates();
if (isset($module_templates[$template_name]['file'])) {
$template_content = @file_get_contents($module_templates[$template_name]['file']);
}
}
else {
$theme = $this->themeManager->getActiveTheme();
if ($theme->getName() !== $provider) {
$theme = $this->themeInitialization->getActiveThemeByName($provider);
}
$template_content = $this->templates->getContent($template_name, $theme);
}
if (!is_string($template_content)) {
throw new MustacheTemplateNotFoundException(t('Defined template @template for library @library not found.',
['@template' => $template_name, '@library' => $library_name]));
}
$template_encoded = trim(substr(json_encode($template_content, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT), 1, -1));
$build = MustacheRenderTemplate::build('summable')
->usingData([
'name' => $template_name,
'content' => $template_encoded,
])
->toRenderArray();
$script_generated = trim((string) \Drupal::service('renderer')->render($build));
$directory = $this->jsPath . '/' . $provider;
$this->fileSystem->prepareDirectory($directory, FileSystemInterface::CREATE_DIRECTORY);
if ($this->fileSystem->saveData($script_generated, $script_file_uri, FileSystemInterface::EXISTS_REPLACE) === FALSE) {
return FALSE;
}
return TRUE;
}
/**
* {@inheritdoc}
*/
public function clearCaches() {
if ($this->cache instanceof CacheTagsInvalidatorInterface) {
$this->cache->invalidateTags(['mustache:libraries']);
}
else {
$this->cache->invalidateAll();
}
// The library discovery service holds a list of statically cached
// definitions, and since we are adding a new definition here, it
// needs to be reset. The clear method is the only known way to do so.
$this->libraryDiscovery->clearCachedDefinitions();
$this->libraries = NULL;
$this->current = [];
}
/**
* {@inheritdoc}
*/
public function deleteAll() {
return $this->fileSystem->deleteRecursive($this->jsPath);
}
}
