config_css-8.x-1.0-alpha1/config_css.module
config_css.module
<?php
/**
* @file
* Provides a config entity containing CSS rules and visibility conditions to
* include the stylesheet on specific pages.
*/
use Drupal\Component\Plugin\Exception\ContextException;
use Drupal\Component\Plugin\Exception\MissingValueContextException;
use Drupal\config_css\ConditionAccessResolver;
use Drupal\config_css\Entity\ConfigCss;
use Drupal\Core\Asset\AttachedAssetsInterface;
use Drupal\Core\Cache\CacheableDependencyInterface;
use Drupal\Core\File\Exception\FileWriteException;
use Drupal\Core\File\FileSystem;
use Drupal\Core\Entity\EntityInterface;
/**
* Implements hook_page_attachments().
*/
function config_css_page_attachments(array &$attachments) {
$activeTheme = \Drupal::theme()->getActiveTheme();
$contextHandler = \Drupal::service('context.handler');
$contextRepository = \Drupal::service('context.repository');
// Load out all stylesheet entities.
$stylesheets = \Drupal::entityTypeManager()->getStorage('config_css')->loadMultiple();
// We always add the config_css list cache tag as any change to a config CSS entity could result
// in it showing up somewhere.
$cache = [
'context' => [],
'tags' => ['config:config_css_list'],
];
foreach ($stylesheets as $stylesheet) {
$visible = FALSE;
$missing_values = FALSE;
if ($stylesheet->get('theme') == $activeTheme->getName()) {
$visibilityConditions = $stylesheet->getVisibilityConditions();
$validConditions = [];
foreach ($visibilityConditions as $condition) {
$contexts = $contextRepository->getRuntimeContexts(array_values($condition->getContextMapping()));
try {
$contextHandler->applyContextMapping($condition, $contexts);
if ($condition instanceof CacheableDependencyInterface) {
$cache['context'] = array_merge($cache['context'], $condition->getCacheContexts());
$cache['tags'] = array_merge($cache['tags'], $condition->getCacheTags());
}
$validConditions[] = $condition;
}
catch (MissingValueContextException $e) {
// If there is no value, don't display.
$visible = FALSE;
$missing_values = TRUE;
break;
}
catch (ContextException $e) {
// No-op, we just skip this condition.
}
}
// If we didn't have any missing values, resolve all the conditions.
if (!$missing_values) {
$conditionAccessResolver = new ConditionAccessResolver();
if ($conditionAccessResolver->resolveConditions($validConditions, 'and')) {
$visible = TRUE;
}
}
}
// If the stylesheet is visible, attach it.
if ($visible) {
$attachments['#attached']['library'][] = 'config_css/' . $stylesheet->id();
$cache['tags'] = array_merge($cache['tags'], $stylesheet->getCacheTags(), $stylesheet->getEntityType()->getListCacheTags());
$cache['context'] = array_merge($cache['context'], $stylesheet->getCacheContexts());
}
}
// Always add cache metadata as any change to a config CSS entity requires invalidating all the headers.
$attachments['#cache'] = $cache;
}
/**
* Implements hook_library_info_build().
*
* Add a library for each stylesheet entry.
*/
function config_css_library_info_build() {
$libraries = [];
$stylesheets = \Drupal::entityTypeManager()->getStorage('config_css')->loadMultiple();
foreach ($stylesheets as $stylesheet) {
config_css_write_stylesheet($stylesheet);
$libraries[$stylesheet->id()] = [
'css' => [
'base' => [
$stylesheet->getStylesheetUrl() => [],
],
],
];
}
return $libraries;
}
/**
* Implements hook_css_alter().
*
* Drupal will force the group to CSS_AGGREGATE_DEFAULT in LibraryDiscoveryParser->buildByExtension(), so we have to alter
* our own CSS to set it.
*/
function config_css_css_alter(&$css, AttachedAssetsInterface $assets) {
$stylesheets = \Drupal::entityTypeManager()->getStorage('config_css')->loadMultiple();
foreach ($stylesheets as $stylesheet) {
$path = ltrim($stylesheet->getStylesheetUrl(), '/');
if (isset($css[$path])) {
$css[$path]['group'] = CSS_AGGREGATE_THEME;
$css[$path]['weight'] = 1000 + $stylesheet->get('weight');
}
}
}
/**
* Implements hook_entity_update().
*/
function config_css_entity_update(EntityInterface $entity) {
if ($entity instanceof ConfigCss) {
_drupal_flush_css_js();
\Drupal::service('cache.css_config_asset_resolver')->invalidateAll();
\Drupal::service('asset.css.collection_optimizer')->deleteAll();
\Drupal::service('library.discovery')->clearCachedDefinitions();
}
}
/**
* Implements hook_entity_insert().
*/
function config_css_entity_insert(EntityInterface $entity) {
config_css_entity_update($entity);
}
/**
* Implements hook_entity_delete().
*/
function config_css_entity_delete(EntityInterface $entity) {
config_css_entity_update($entity);
}
/**
* Write the stylesheet contents for a config CSS entity to the filesystem.
*
* @param \Drupal\config_css\Entity\ConfigCss $entity
* The configuration stylesheet entity to write to the filesystem.
*/
function config_css_write_stylesheet(ConfigCss $entity) {
$config = \Drupal::config('config_css.settings');
$path = rtrim($config->get('css_store_path'), '/');
$fileSystem = \Drupal::service('file_system');
if ($fileSystem->prepareDirectory($path, FileSystem::CREATE_DIRECTORY | FileSystem::MODIFY_PERMISSIONS)) {
$fileSystem->saveData($entity->get('stylesheet'), $path . '/' . $entity->id() . '.css', FileSystem::EXISTS_REPLACE);
}
else {
throw new FileWriteException(t('CSS store directory %directory could not be prepared.', ['%directory' => $path]));
}
}
