3 namespace Drupal\imagemagick;
5 use Drupal\Component\Utility\Unicode;
6 use Drupal\Core\Cache\Cache;
7 use Drupal\Core\Cache\CacheBackendInterface;
8 use Drupal\Core\Config\ConfigFactoryInterface;
9 use Drupal\Core\Config\Schema\SchemaCheckTrait;
10 use Drupal\Core\Config\TypedConfigManagerInterface;
11 use Drupal\Core\StringTranslation\StringTranslationTrait;
12 // @todo change if extension mapping service gets in, see #2311679
13 use Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesserInterface;
16 * Provides the ImageMagick format mapper.
18 class ImagemagickFormatMapper implements ImagemagickFormatMapperInterface {
21 use StringTranslationTrait;
26 * @var \Drupal\Core\Cache\CacheBackendInterface
31 * The MIME type guessing service.
32 * @todo change if extension mapping service gets in, see #2311679
34 * @var \Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesserInterface
36 protected $mimeTypeMapper;
39 * The config factory service.
41 * @var \Drupal\Core\Config\ConfigFactoryInterface
43 protected $configFactory;
46 * The typed config service.
48 * @var \Drupal\Core\Config\TypedConfigManagerInterface
50 protected $typedConfig;
53 * Constructs an ImagemagickFormatmapper object.
55 * @param \Drupal\Core\Cache\CacheBackendInterface $cache_service
57 * @param \Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesserInterface $mime_type_mapper
58 * The MIME type mapping service.
59 * @todo change if extension mapping service gets in, see #2311679
60 * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
62 * @param \Drupal\Core\Config\TypedConfigManagerInterface $typed_config
63 * The typed config service.
65 public function __construct(CacheBackendInterface $cache_service, MimeTypeGuesserInterface $mime_type_mapper, ConfigFactoryInterface $config_factory, TypedConfigManagerInterface $typed_config) {
66 $this->cache = $cache_service;
67 // @todo change if extension mapping service gets in, see #2311679
68 $this->mimeTypeMapper = $mime_type_mapper;
69 $this->configFactory = $config_factory;
70 $this->typedConfig = $typed_config;
76 public function validateMap(array $map) {
79 // Get current config object and change the format map.
80 $data = $this->configFactory->get('imagemagick.settings')->get();
81 $data['image_formats'] = $map;
83 // Validates against schema.
84 $schema_errors = $this->checkConfigSchema($this->typedConfig, 'imagemagick.settings', $data);
85 if ($schema_errors !== TRUE) {
86 foreach ($schema_errors as $key => $value) {
87 list($object, $path) = explode(':', $key);
88 $components = explode('.', $path);
89 if ($components[0] === 'image_formats') {
90 if (isset($components[2])) {
91 $errors[$components[1]]['variables'][$components[2]][] = $value;
94 $errors[$components[1]]['format'][] = $value;
101 foreach ($map as $key => $value) {
102 if (Unicode::strtoupper($key) != $key) {
103 // Formats must be typed in uppercase.
104 $errors[$key]['format'][] = $this->t("The format (@key) must be entered in all uppercase characters.", ['@key' => $key])->render();
106 if (!isset($value['mime_type'])) {
107 // Formats must have a MIME type mapped.
108 $errors[$key]['format'][] = $this->t("Missing mime_type variable.")->render();
110 elseif (!in_array($value['mime_type'], $this->mimeTypeMapper->getMimeTypes())) {
111 // MIME type must exist.
112 $errors[$key]['variables']['mime_type'][] = $this->t("MIME type (@mime_type) not found.", ['@mime_type' => $value['mime_type']])->render();
122 public function isFormatEnabled($format) {
123 $format = Unicode::strtoupper($format);
124 return $format ? isset($this->resolveEnabledFormats()[$format]) : FALSE;
130 public function getMimeTypeFromFormat($format) {
131 $format = Unicode::strtoupper($format);
132 if ($this->isFormatEnabled($format)) {
133 return $this->resolveEnabledFormats()[$format];
141 public function getFormatFromExtension($extension) {
142 $extension = Unicode::strtolower($extension);
143 $enabled_extensions = $this->resolveEnabledExtensions();
144 return $extension ? (isset($enabled_extensions[$extension]) ? $enabled_extensions[$extension] : NULL) : NULL;
150 public function getEnabledFormats() {
151 return array_keys($this->resolveEnabledFormats());
157 public function getEnabledExtensions() {
158 return array_keys($this->resolveEnabledExtensions());
162 * Returns the enabled image formats, processing the config map.
164 * Results are cached for subsequent access. Saving the config will
165 * invalidate the cache.
168 * An associative array with ImageMagick formats as keys and their MIME
171 protected function resolveEnabledFormats() {
172 if ($cache = $this->cache->get("imagemagick:enabled_formats")) {
173 $enabled_image_formats = $cache->data;
176 $config = $this->configFactory->get('imagemagick.settings');
177 $image_formats = $config->get('image_formats');
178 $enabled_image_formats = [];
179 foreach ($image_formats as $format => $data) {
180 if (!isset($data['enabled']) || (isset($data['enabled']) && $data['enabled'])) {
181 if (isset($data['mime_type']) && in_array($data['mime_type'], $this->mimeTypeMapper->getMimeTypes())) {
182 $enabled_image_formats[$format] = $data['mime_type'];
186 ksort($enabled_image_formats);
187 $this->cache->set("imagemagick:enabled_formats", $enabled_image_formats, Cache::PERMANENT, $config->getCacheTags());
189 return $enabled_image_formats;
194 * Returns the enabled image file extensions, processing the config map.
196 * Results are cached for subsequent access. Saving the config will
197 * invalidate the cache.
200 * An associative array with file extensions as keys and their ImageMagick
203 protected function resolveEnabledExtensions() {
204 if ($cache = $this->cache->get("imagemagick:enabled_extensions")) {
205 $extensions = $cache->data;
208 // Get configured image formats.
209 $image_formats = $this->configFactory->get('imagemagick.settings')->get('image_formats');
211 // Get only enabled formats.
212 $enabled_image_formats = array_keys($this->resolveEnabledFormats());
215 foreach ($enabled_image_formats as $format) {
216 if (isset($image_formats[$format]) && is_array($image_formats[$format])) {
217 $image_formats[$format] += [
220 'exclude_extensions' => NULL,
225 // Scans the enabled formats to determine enabled file extensions and
226 // their mapping to the internal Image/GraphicsMagick format.
228 $excluded_extensions = [];
229 foreach ($enabled_image_formats as $format) {
230 $format_extensions = $this->mimeTypeMapper->getExtensionsForMimeType($image_formats[$format]['mime_type']);
231 $weight_checked_extensions = [];
232 foreach ($format_extensions as $ext) {
233 if (!isset($extensions[$ext])) {
234 $weight_checked_extensions[$ext] = $format;
237 // Extension is already present in the array, lower weight format
239 if ($image_formats[$format]['weight'] < $image_formats[$extensions[$ext]]['weight']) {
240 $weight_checked_extensions[$ext] = $format;
244 $extensions = array_merge($extensions, $weight_checked_extensions);
245 // Accumulate excluded extensions.
246 if ($image_formats[$format]['exclude_extensions']) {
247 $exclude_extensions_string = Unicode::strtolower(preg_replace('/\s+/', '', $image_formats[$format]['exclude_extensions']));
248 $excluded_extensions = array_merge($excluded_extensions, array_intersect($format_extensions, explode(',', $exclude_extensions_string)));
252 // Remove the excluded extensions.
253 $excluded_extensions = array_unique($excluded_extensions);
254 $excluded_extensions = array_combine($excluded_extensions, $excluded_extensions);
255 $extensions = array_diff_key($extensions, $excluded_extensions);
258 $this->cache->set("imagemagick:enabled_extensions", $extensions, Cache::PERMANENT, $this->configFactory->get('imagemagick.settings')->getCacheTags());