View source
<?php
namespace Drupal\background_image\Controller;
use Drupal\background_image\BackgroundImageInterface;
use Drupal\background_image\BackgroundImageManagerInterface;
use Drupal\Core\File\Exception\FileException;
use Drupal\Core\File\Exception\FileWriteException;
use Drupal\Core\File\FileSystemInterface;
use Drupal\Core\Image\ImageFactory;
use Drupal\Core\Lock\LockBackendInterface;
use Drupal\Core\StreamWrapper\StreamWrapperManagerInterface;
use Drupal\Core\Theme\ThemeManagerInterface;
use Drupal\image\Entity\ImageStyle;
use Drupal\system\FileDownloadController;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Symfony\Component\HttpKernel\Exception\ServiceUnavailableHttpException;
class BackgroundImageCssController extends FileDownloadController {
protected $backgroundImageManager;
protected $breakpointManager;
protected $cssTemplates;
protected $fileSystem;
protected $gzip = FALSE;
protected $imageFactory;
protected $lock;
protected $logger;
protected $themeManager;
protected $twig;
public function __construct(StreamWrapperManagerInterface $streamWrapperManager, BackgroundImageManagerInterface $background_image_manager, FileSystemInterface $fileSystem, ImageFactory $image_factory, LockBackendInterface $lock, ThemeManagerInterface $theme_manager, \Twig_Environment $twig) {
parent::__construct($streamWrapperManager);
$this->backgroundImageManager = $background_image_manager;
$this->fileSystem = $fileSystem;
$this->gzip = extension_loaded('zlib') && \Drupal::config('system.performance')
->get('css.gzip');
$this->imageFactory = $image_factory;
$this->lock = $lock;
$this->logger = $this
->getLogger('background_image');
$this->themeManager = $theme_manager;
$this->twig = $twig;
if ($this
->moduleHandler()
->moduleExists('responsive_image')) {
$this->breakpointManager = \Drupal::service('breakpoint.manager');
}
}
public static function create(ContainerInterface $container) {
return new static($container
->get('stream_wrapper_manager'), $container
->get('background_image.manager'), $container
->get('file_system'), $container
->get('image.factory'), $container
->get('lock'), $container
->get('theme.manager'), $container
->get('twig'));
}
public function buildCss(BackgroundImageInterface $background_image, $uri) {
if (!$background_image
->getImageFile()) {
$this->logger
->error('Background image does not have a valid image file: background_image:@id', [
'@id' => $background_image
->id(),
]);
return FALSE;
}
$directory = $this->fileSystem
->dirname($uri);
if (!$this->fileSystem
->prepareDirectory($directory, FileSystemInterface::CREATE_DIRECTORY | FileSystemInterface::MODIFY_PERMISSIONS)) {
$this->logger
->error('Failed to create background image directory: %directory', [
'%directory' => $directory,
]);
return FALSE;
}
$variables = [
'base_class' => $this->backgroundImageManager
->getBaseClass(),
'background_image_class' => $background_image
->getCssClass(),
'settings' => $background_image
->getSettings()
->get(),
'preload_url' => $background_image
->getImageUrl($this->backgroundImageManager
->getPreloadImageStyle()),
'fallback_url' => $background_image
->getImageUrl($this->backgroundImageManager
->getFallbackImageStyle()),
'media_queries' => $this
->buildMediaQueries($background_image),
];
$cssTemplate = $this
->getCssTemplate();
$this
->moduleHandler()
->alter('background_image_css_template', $variables, $cssTemplate, $background_image);
$this->themeManager
->alter('background_image_css_template', $variables, $cssTemplate, $background_image);
try {
$data = $this->twig
->loadTemplate($cssTemplate)
->render($variables);
if (preg_match('/\\.min\\.css$/', $uri) && ($css_minifier = $this->backgroundImageManager
->getCssMinifier())) {
$data = $css_minifier
->optimize($data, [], []);
$css_minifier
->addLicense($data, preg_replace('/\\.min\\.css$/', '.css', file_create_url($uri)));
}
if (!$this
->dump($data, $uri)) {
return FALSE;
}
} catch (\Exception $e) {
$previous_exception = $e
->getPrevious();
$this->logger
->error($previous_exception ? $previous_exception
->getMessage() : $e
->getMessage());
return FALSE;
}
return TRUE;
}
protected function buildMediaQueries(BackgroundImageInterface $background_image) {
$responsive_image_style = $this->backgroundImageManager
->getResponsiveImageStyle();
if (!$this->breakpointManager || !$responsive_image_style) {
return [];
}
$mediaQueries = [];
$breakpoints = $this->breakpointManager
->getBreakpointsByGroup($responsive_image_style
->getBreakpointGroup());
$keyed_image_style_mappings = $responsive_image_style
->getKeyedImageStyleMappings();
$retinaRules = $this->backgroundImageManager
->getRetinaRules();
$i = 0;
foreach ($breakpoints as $breakpoint_id => $breakpoint) {
if (isset($keyed_image_style_mappings[$breakpoint_id])) {
$mediaQuery = trim($breakpoint
->getMediaQuery());
foreach ($keyed_image_style_mappings[$breakpoint_id] as $multiplier => $image_style_mapping) {
if ($image_style_mapping['image_mapping_type'] !== 'image_style') {
continue;
}
$key = intval(mb_substr($multiplier, 0, -1) * 100) + $i++;
$image_style = $image_style_mapping['image_mapping'];
if ($multiplier === "2x") {
$rules = [];
foreach ($retinaRules as $retinaRule) {
$rules[] = trim($retinaRule) . ' and ' . trim(preg_replace('/^\\s*(only )?(all|print|screen)\\s?(and)?/', '', $mediaQuery));
}
$mediaQueries[$key] = [
'image_style' => $image_style,
'multiplier' => $multiplier,
'query' => implode(',', $rules),
'url' => $background_image
->getImageUrl($image_style),
];
}
else {
$mediaQueries[$key] = [
'image_style' => $image_style,
'multiplier' => $multiplier,
'query' => $mediaQuery,
'url' => $background_image
->getImageUrl($image_style),
];
}
}
}
}
ksort($mediaQueries);
return $mediaQueries;
}
protected function dump($data, $uri) {
$this->fileSystem
->prepareDirectory($path, FileSystemInterface::CREATE_DIRECTORY);
try {
$this->fileSystem
->saveData($data, $uri, FileSystemInterface::EXISTS_REPLACE);
if ($this->gzip) {
$this->fileSystem
->saveData(gzencode($data, 9, FORCE_GZIP), $uri . '.gz', FileSystemInterface::EXISTS_REPLACE);
}
} catch (FileWriteException $e) {
$this->logger
->error($this
->t('Unable to create save the CSS file: @uri', [
'@uri' => $uri,
]));
return FALSE;
} catch (FileException $e) {
return FALSE;
}
return $uri;
}
public static function imageStyleUrl($style_name, $path) {
if ($style_name == '_empty image_') {
return 'data:image/gif;base64,R0lGODlhAQABAIABAP///wAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==';
}
$entity = ImageStyle::load($style_name);
if ($entity instanceof ImageStyle) {
return file_url_transform_relative($entity
->buildUrl($path));
}
return file_url_transform_relative(file_create_url($path));
}
public function deliver(Request $request, BackgroundImageInterface $background_image, $scheme, $file = NULL) {
$valid = !empty($background_image) && $this->streamWrapperManager
->isValidScheme($scheme) && $background_image
->access('view');
if (!$valid) {
throw new AccessDeniedHttpException();
}
$uri = "{$scheme}://background_image/css/{$background_image->id()}/{$scheme}/{$file}";
$headers = [];
if ($scheme == 'private') {
$headers = $this
->moduleHandler()
->invokeAll('file_download', [
$uri,
]);
if (in_array(-1, $headers) || empty($headers)) {
throw new AccessDeniedHttpException();
}
}
if (!file_exists($uri)) {
$lock_name = 'background_image_css_deliver:' . $background_image
->id() . ':' . $background_image
->getImageHash();
$lock_acquired = $this->lock
->acquire($lock_name);
if (!$lock_acquired) {
throw new ServiceUnavailableHttpException(3, $this
->t('Background Image CSS generation in progress. Try again shortly.'));
}
}
$success = file_exists($uri) || $this
->buildCss($background_image, $uri);
if (!empty($lock_acquired)) {
$this->lock
->release($lock_name);
}
if ($success) {
$headers += [
'Content-Type' => 'text/css',
'Content-Length' => filesize($uri),
];
return new BinaryFileResponse($uri, 200, $headers, $scheme !== 'private');
}
else {
$this->logger
->notice('Unable to generate the background image CSS located at %path.', [
'%path' => $uri,
]);
return new Response($this
->t('Error generating image.'), 500);
}
}
protected function getCssTemplate() {
if (!isset($this->cssTemplates)) {
$cache = \Drupal::cache()
->get('background_image_css_templates');
$this->cssTemplates = $cache && is_array($cache->data) ? $cache->data : [];
}
$activeTheme = $this->themeManager
->getActiveTheme();
$activeThemeName = $activeTheme
->getName();
if (!isset($this->cssTemplates[$activeThemeName])) {
$templatePaths = [
$activeTheme
->getPath() . '/templates',
];
foreach ($activeTheme
->getBaseThemeExtensions() as $name => $extension) {
$templatePaths[$name] = $extension
->getPath() . '/templates';
}
$mask = '/' . preg_quote('background_image.css.twig', '/') . '$/';
foreach (array_unique($templatePaths) as $path) {
try {
if (is_dir($path) && ($file = current($this->fileSystem
->scanDirectory($path, $mask)))) {
$cssTemplate = str_replace(\Drupal::root() . '/', '', $file->uri);
break;
}
} catch (FileException $e) {
}
}
if (!isset($cssTemplate)) {
$cssTemplate = drupal_get_path('module', 'background_image') . '/templates/background_image.css.twig';
}
$this->cssTemplates[$activeThemeName] = $cssTemplate;
\Drupal::cache()
->set('background_image_css_templates', $this->cssTemplates);
}
return $this->cssTemplates[$activeThemeName];
}
}