Skip to content
Snippets Groups Projects

Issue #3333776: Fatal error when trying to display inline a SVG image that does not exist and with stage_file_proxy module enabled

Merged Issue #3333776: Fatal error when trying to display inline a SVG image that does not exist and with stage_file_proxy module enabled
Files
5
@@ -2,6 +2,7 @@
namespace Drupal\svg_image_field\Plugin\Field\FieldFormatter;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldItemListInterface;
@@ -46,6 +47,13 @@ class SvgImageFieldFormatter extends FormatterBase implements ContainerFactoryPl
*/
protected $fileUrlGenerator;
/**
* The config factory.
*
* @var \Drupal\Core\Config\ConfigFactoryInterface
*/
protected $configFactory;
/**
* {@inheritdoc}
*/
@@ -232,9 +240,8 @@ class SvgImageFieldFormatter extends FormatterBase implements ContainerFactoryPl
continue;
}
$uri = $item->entity->getFileUri();
$stageFileProxy = $this->moduleHandler->moduleExists('stage_file_proxy');
if (file_exists($uri) === FALSE && !$stageFileProxy) {
$this->logger->error('The specified file %file could not be displayed by image formatter due file not exists.', ['%file' => $uri]);
if (!is_file($uri) && !$this->isStageFileProxyConfigured()) {
$this->logger->error('File %file could not be displayed by image formatter because it does not exist on server.', ['%file' => $uri]);
continue;
}
if ($this->getSetting('enable_alt')) {
@@ -247,36 +254,6 @@ class SvgImageFieldFormatter extends FormatterBase implements ContainerFactoryPl
if ($this->getSetting('enable_title') && !empty($item->title)) {
$attributes['title'] = $item->title;
}
$svg_data = NULL;
if ($this->getSetting('inline')) {
$svg_file = file_get_contents($uri);
$dom = new \DOMDocument();
libxml_use_internal_errors(TRUE);
if (!empty($svg_file)) {
$dom->loadXML($svg_file);
}
if ($this->getSetting('force_fill')) {
$dom->documentElement->setAttribute('fill', 'currentColor');
}
if (isset($dom->documentElement)) {
if ($this->getSetting('apply_dimensions')) {
$dom->documentElement->setAttribute('height', $attributes['height']);
$dom->documentElement->setAttribute('width', $attributes['width']);
}
$svg_data = $dom->saveXML($dom->documentElement);
}
else {
$svg_data = $dom->saveXML();
}
if ($this->getSetting('sanitize')) {
$svgSanitizer = new Sanitizer();
if ($this->getSetting('sanitize_remote')) {
$svgSanitizer->removeRemoteReferences(TRUE);
}
$svg_data = $svgSanitizer->sanitize($svg_data);
}
}
$cache_contexts = [];
if ($image_link_setting == 'file') {
@@ -289,23 +266,96 @@ class SvgImageFieldFormatter extends FormatterBase implements ContainerFactoryPl
$cache_contexts[] = 'url.site';
}
$elements[$delta] = [
$element = [
'#theme' => 'svg_image_field_formatter',
'#inline' => $this->getSetting('inline') ? TRUE : FALSE,
'#attributes' => $attributes,
'#uri' => $this->getSetting('inline') ? NULL : $uri,
'#svg_data' => $svg_data,
'#link_url' => $url,
'#cache' => [
'tags' => $item->entity->getCacheTags(),
'contexts' => $cache_contexts,
],
];
// Set properties based on if the SVG is displayed inline or not.
if ($this->getSetting('inline') && $svg_data = $this->loadInlineSvgData($uri, $attributes)) {
$element['#inline'] = TRUE;
$element['#svg_data'] = $svg_data;
}
else {
$element['#inline'] = FALSE;
$element['#uri'] = $uri;
}
$elements[$delta] = $element;
}
return $elements;
}
/**
* Determine if the Stage File Proxy module is enabled and configured.
*
* @return bool
* True if Stage File Proxy module is enabled and configured.
*/
protected function isStageFileProxyConfigured() {
return (
$this->moduleHandler->moduleExists('stage_file_proxy') &&
$this->configFactory->get('stage_file_proxy.settings')->get('origin')
);
}
/**
* Load SVG file data for inline display.
*
* @param string $uri
* The uri to the SVG file.
* @param array $attributes
* Attributes to set for the SVG html tag.
*
* @return string|null
* The loaded SVG XML, or null if the file is missing or empty.
*/
protected function loadInlineSvgData(string $uri, array $attributes): ?string {
// If the file is missing or has empty contents, temporarily disable
// inline SVG and instead render an <img> tag containing the URL to the
// SVG in the src attribute. This approach allows Stage File Proxy to
// intercept the request and obtain the original SVG file from the origin
// server. And as long as the Stage File Proxy 'hotlink' option is not
// enabled, subsequent page loads will render the SVG inline again.
if (!is_file($uri) || !$svg_file = @file_get_contents($uri)) {
$this->logger->warning('Inline file %file is missing or empty. Inline display will be disabled and an image tag used instead.', ['%file' => $uri]);
return NULL;
}
$dom = new \DOMDocument();
libxml_use_internal_errors(TRUE);
$dom->loadXML($svg_file);
if ($this->getSetting('force_fill')) {
$dom->documentElement->setAttribute('fill', 'currentColor');
}
if (isset($dom->documentElement)) {
if ($this->getSetting('apply_dimensions')) {
$dom->documentElement->setAttribute('height', $attributes['height']);
$dom->documentElement->setAttribute('width', $attributes['width']);
}
$svg_data = $dom->saveXML($dom->documentElement);
}
else {
$svg_data = $dom->saveXML();
}
if ($this->getSetting('sanitize')) {
$svgSanitizer = new Sanitizer();
if ($this->getSetting('sanitize_remote')) {
$svgSanitizer->removeRemoteReferences(TRUE);
}
$svg_data = $svgSanitizer->sanitize($svg_data);
}
return $svg_data;
}
/**
* Constructs a SvgImageFieldFormatter object.
*
@@ -329,6 +379,8 @@ class SvgImageFieldFormatter extends FormatterBase implements ContainerFactoryPl
* The module handler service.
* @param \Drupal\Core\File\FileUrlGeneratorInterface $file_url_generator
* The file URL generator.
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
* The config factory.
*/
public function __construct(
$plugin_id,
@@ -340,11 +392,13 @@ class SvgImageFieldFormatter extends FormatterBase implements ContainerFactoryPl
array $third_party_settings,
LoggerChannelFactoryInterface $logger,
ModuleHandlerInterface $module_handler,
FileUrlGeneratorInterface $file_url_generator
FileUrlGeneratorInterface $file_url_generator,
ConfigFactoryInterface $config_factory,
) {
$this->logger = $logger->get('svg_image_field');
$this->moduleHandler = $module_handler;
$this->fileUrlGenerator = $file_url_generator;
$this->configFactory = $config_factory;
parent::__construct($plugin_id, $plugin_definition, $field_definition,
$settings, $label, $view_mode, $third_party_settings);
}
@@ -368,7 +422,8 @@ class SvgImageFieldFormatter extends FormatterBase implements ContainerFactoryPl
$configuration['third_party_settings'],
$container->get('logger.factory'),
$container->get('module_handler'),
$container->get('file_url_generator')
$container->get('file_url_generator'),
$container->get('config.factory'),
);
}
Loading