Updated all the contrib modules to their latest versions.
[yaffs-website] / web / modules / contrib / file_mdm / src / Plugin / FileMetadata / FileMetadataPluginBase.php
diff --git a/web/modules/contrib/file_mdm/src/Plugin/FileMetadata/FileMetadataPluginBase.php b/web/modules/contrib/file_mdm/src/Plugin/FileMetadata/FileMetadataPluginBase.php
new file mode 100644 (file)
index 0000000..fd2a0dd
--- /dev/null
@@ -0,0 +1,538 @@
+<?php
+
+namespace Drupal\file_mdm\Plugin\FileMetadata;
+
+use Drupal\Core\Cache\Cache;
+use Drupal\Core\Cache\CacheBackendInterface;
+use Drupal\Core\Config\ConfigFactoryInterface;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Plugin\PluginBase;
+use Drupal\file_mdm\FileMetadataException;
+use Drupal\file_mdm\FileMetadataInterface;
+use Drupal\file_mdm\Plugin\FileMetadataPluginInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Abstract implementation of a base File Metadata plugin.
+ */
+abstract class FileMetadataPluginBase extends PluginBase implements FileMetadataPluginInterface {
+
+  /**
+   * The cache service.
+   *
+   * @var \Drupal\Core\Cache\CacheBackendInterface
+   */
+  protected $cache;
+
+  /**
+   * The config factory.
+   *
+   * @var \Drupal\Core\Config\ConfigFactoryInterface
+   */
+  protected $configFactory;
+
+  /**
+   * The URI of the file.
+   *
+   * @var string
+   */
+  protected $uri;
+
+  /**
+   * The local filesystem path to the file.
+   *
+   * This is used to allow accessing local copies of files stored remotely, to
+   * minimise remote calls and allow functions that cannot access remote stream
+   * wrappers to operate locally.
+   *
+   * @var string
+   */
+  protected $localTempPath;
+
+  /**
+   * The hash used to reference the URI.
+   *
+   * @var string
+   */
+  protected $hash;
+
+  /**
+   * The metadata of the file.
+   *
+   * @var mixed
+   */
+  protected $metadata = NULL;
+
+  /**
+   * The metadata loading status.
+   *
+   * @var int
+   */
+  protected $isMetadataLoaded = FileMetadataInterface::NOT_LOADED;
+
+  /**
+   * Track if metadata has been changed from version on file.
+   *
+   * @var bool
+   */
+  protected $hasMetadataChangedFromFileVersion = FALSE;
+
+  /**
+   * Track if file metadata on cache needs update.
+   *
+   * @var bool
+   */
+  protected $hasMetadataChangedFromCacheVersion = FALSE;
+
+  /**
+   * Constructs a FileMetadataPluginBase plugin.
+   *
+   * @param array $configuration
+   *   A configuration array containing information about the plugin instance.
+   * @param string $plugin_id
+   *   The plugin_id for the plugin instance.
+   * @param array $plugin_definition
+   *   The plugin implementation definition.
+   * @param \Drupal\Core\Cache\CacheBackendInterface $cache_service
+   *   The cache service.
+   * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
+   *   The config factory.
+   */
+  public function __construct(array $configuration, $plugin_id, array $plugin_definition, CacheBackendInterface $cache_service, ConfigFactoryInterface $config_factory) {
+    parent::__construct($configuration, $plugin_id, $plugin_definition);
+    $this->cache = $cache_service;
+    $this->configFactory = $config_factory;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
+    return new static(
+      $configuration,
+      $plugin_id,
+      $plugin_definition,
+      $container->get('cache.file_mdm'),
+      $container->get('config.factory')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function defaultConfiguration() {
+    return [
+      'cache' => [
+        'override' => FALSE,
+        'settings' => [
+          'enabled' => TRUE,
+          'expiration' => 172800,
+          'disallowed_paths' => [],
+        ],
+      ],
+    ];
+  }
+
+  /**
+   * Gets the configuration object for this plugin.
+   *
+   * @param bool $editable
+   *   If TRUE returns the editable configuration object.
+   *
+   * @return \Drupal\Core\Config\ImmutableConfig|\Drupal\Core\Config\Config
+   *   The ImmutableConfig of the Config object for this plugin.
+   */
+  protected function getConfigObject($editable = FALSE) {
+    $plugin_definition = $this->getPluginDefinition();
+    $config_name = $plugin_definition['provider'] . '.file_metadata_plugin.' . $plugin_definition['id'];
+    return $editable ? $this->configFactory->getEditable($config_name) : $this->configFactory->get($config_name);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
+    $form['override'] = [
+      '#type' => 'checkbox',
+      '#title' => $this->t('Override main caching settings'),
+      '#default_value' => $this->configuration['cache']['override'],
+    ];
+    $form['cache_details'] = [
+      '#type' => 'details',
+      '#open' => TRUE,
+      '#collapsible' => FALSE,
+      '#title' => $this->t('Metadata caching'),
+      '#tree' => TRUE,
+      '#states' => [
+        'visible' => [
+          ':input[name="' . $this->getPluginId() . '[override]"]' => ['checked' => TRUE],
+        ],
+      ],
+    ];
+    $form['cache_details']['settings'] = [
+      '#type' => 'file_mdm_caching',
+      '#default_value' => $this->configuration['cache']['settings'],
+    ];
+
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function validateConfigurationForm(array &$form, FormStateInterface $form_state) {
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
+    // @codingStandardsIgnoreStart
+    $this->configuration['cache']['override'] = (bool) $form_state->getValue([$this->getPluginId(), 'override']);
+    $this->configuration['cache']['settings'] = $form_state->getValue([$this->getPluginId(), 'cache_details', 'settings']);
+    // @codingStandardsIgnoreEnd
+
+    $config = $this->getConfigObject(TRUE);
+    $config->set('configuration', $this->configuration);
+    if ($config->getOriginal('configuration') != $config->get('configuration')) {
+      $config->save();
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setUri($uri) {
+    if (!$uri) {
+      throw new FileMetadataException('Missing $uri argument', $this->getPluginId(), __FUNCTION__);
+    }
+    $this->uri = $uri;
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getUri() {
+    return $this->uri;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setLocalTempPath($temp_path) {
+    $this->localTempPath = $temp_path;
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getLocalTempPath() {
+    return $this->localTempPath;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setHash($hash) {
+    if (!$hash) {
+      throw new FileMetadataException('Missing $hash argument', $this->getPluginId(), __FUNCTION__);
+    }
+    $this->hash = $hash;
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function isMetadataLoaded() {
+    return $this->isMetadataLoaded;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function loadMetadata($metadata) {
+    $this->metadata = $metadata;
+    $this->hasMetadataChangedFromFileVersion = TRUE;
+    $this->hasMetadataChangedFromCacheVersion = TRUE;
+    $this->deleteCachedMetadata();
+    if ($this->metadata === NULL) {
+      $this->isMetadataLoaded = FileMetadataInterface::NOT_LOADED;
+    }
+    else {
+      $this->isMetadataLoaded = FileMetadataInterface::LOADED_BY_CODE;
+      $this->saveMetadataToCache();
+    }
+    return (bool) $this->metadata;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function loadMetadataFromFile() {
+    if (!file_exists($this->getLocalTempPath())) {
+      // File does not exists.
+      throw new FileMetadataException("File at '{$this->getLocalTempPath()}' does not exist", $this->getPluginId(), __FUNCTION__);
+    }
+    $this->hasMetadataChangedFromFileVersion = FALSE;
+    if (($this->metadata = $this->doGetMetadataFromFile()) === NULL) {
+      $this->isMetadataLoaded = FileMetadataInterface::NOT_LOADED;
+      $this->deleteCachedMetadata();
+    }
+    else {
+      $this->isMetadataLoaded = FileMetadataInterface::LOADED_FROM_FILE;
+      $this->saveMetadataToCache();
+    }
+    return (bool) $this->metadata;
+  }
+
+  /**
+   * Gets file metadata from the file at URI/local path.
+   *
+   * @return mixed
+   *   The metadata retrieved from the file.
+   *
+   * @throws \Drupal\file_mdm\FileMetadataException
+   *   In case there were significant errors reading from file.
+   */
+  abstract protected function doGetMetadataFromFile();
+
+  /**
+   * {@inheritdoc}
+   */
+  public function loadMetadataFromCache() {
+    $plugin_id = $this->getPluginId();
+    $this->hasMetadataChangedFromFileVersion = FALSE;
+    $this->hasMetadataChangedFromCacheVersion = FALSE;
+    if ($this->isUriFileMetadataCacheable() !== FALSE && ($cache = $this->cache->get("hash:{$plugin_id}:{$this->hash}"))) {
+      $this->metadata = $cache->data;
+      $this->isMetadataLoaded = FileMetadataInterface::LOADED_FROM_CACHE;
+    }
+    else {
+      $this->metadata = NULL;
+      $this->isMetadataLoaded = FileMetadataInterface::NOT_LOADED;
+    }
+    return (bool) $this->metadata;
+  }
+
+  /**
+   * Checks if file metadata should be cached.
+   *
+   * @return array|bool
+   *   The caching settings array retrieved from configuration if file metadata
+   *   is cacheable, FALSE otherwise.
+   */
+  protected function isUriFileMetadataCacheable() {
+    // Check plugin settings first, if they override general settings.
+    if ($this->configuration['cache']['override']) {
+      $settings = $this->configuration['cache']['settings'];
+      if (!$settings['enabled']) {
+        return FALSE;
+      }
+    }
+
+    // Use general settings if they are not overridden by plugin.
+    if (!isset($settings)) {
+      $settings = $this->configFactory->get('file_mdm.settings')->get('metadata_cache');
+      if (!$settings['enabled']) {
+        return FALSE;
+      }
+    }
+
+    // URIs without valid scheme, and temporary:// URIs are not cached.
+    if (!file_valid_uri($this->getUri()) || file_uri_scheme($this->getUri()) === 'temporary') {
+      return FALSE;
+    }
+
+    // URIs falling into disallowed paths are not cached.
+    foreach ($settings['disallowed_paths'] as $pattern) {
+      $p = "#^" . strtr(preg_quote($pattern, '#'), ['\*' => '.*', '\?' => '.']) . "$#i";
+      if (preg_match($p, $this->getUri())) {
+        return FALSE;
+      }
+    }
+
+    return $settings;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getMetadata($key = NULL) {
+    if (!$this->getUri()) {
+      throw new FileMetadataException("No URI specified", $this->getPluginId(), __FUNCTION__);
+    }
+    if (!$this->hash) {
+      throw new FileMetadataException("No hash specified", $this->getPluginId(), __FUNCTION__);
+    }
+    if ($this->metadata === NULL) {
+      // Metadata has not been loaded yet. Try loading it from cache first.
+      $this->loadMetadataFromCache();
+    }
+    if ($this->metadata === NULL && $this->isMetadataLoaded !== FileMetadataInterface::LOADED_FROM_FILE) {
+      // Metadata has not been loaded yet. Try loading it from file if URI is
+      // defined and a read attempt was not made yet.
+      $this->loadMetadataFromFile();
+    }
+    return $this->doGetMetadata($key);
+  }
+
+  /**
+   * Gets a metadata element.
+   *
+   * @param mixed|null $key
+   *   A key to determine the metadata element to be returned. If NULL, the
+   *   entire metadata will be returned.
+   *
+   * @return mixed|null
+   *   The value of the element specified by $key. If $key is NULL, the entire
+   *   metadata. If no metadata is available, return NULL.
+   */
+  abstract protected function doGetMetadata($key = NULL);
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setMetadata($key, $value) {
+    if ($key === NULL) {
+      throw new FileMetadataException("No metadata key specified for file at '{$this->getUri()}'", $this->getPluginId(), __FUNCTION__);
+    }
+    if (!$this->metadata && !$this->getMetadata()) {
+      throw new FileMetadataException("No metadata loaded for file at '{$this->getUri()}'", $this->getPluginId(), __FUNCTION__);
+    }
+    if ($this->doSetMetadata($key, $value)) {
+      $this->hasMetadataChangedFromFileVersion = TRUE;
+      if ($this->isMetadataLoaded === FileMetadataInterface::LOADED_FROM_CACHE) {
+        $this->hasMetadataChangedFromCacheVersion = TRUE;
+      }
+      return TRUE;
+    }
+    return FALSE;
+  }
+
+  /**
+   * Sets a metadata element.
+   *
+   * @param mixed $key
+   *   A key to determine the metadata element to be changed.
+   * @param mixed $value
+   *   The value to change the metadata element to.
+   *
+   * @return bool
+   *   TRUE if metadata was changed successfully, FALSE otherwise.
+   */
+  abstract protected function doSetMetadata($key, $value);
+
+  /**
+   * {@inheritdoc}
+   */
+  public function removeMetadata($key) {
+    if ($key === NULL) {
+      throw new FileMetadataException("No metadata key specified for file at '{$this->getUri()}'", $this->getPluginId(), __FUNCTION__);
+    }
+    if (!$this->metadata && !$this->getMetadata()) {
+      throw new FileMetadataException("No metadata loaded for file at '{$this->getUri()}'", $this->getPluginId(), __FUNCTION__);
+    }
+    if ($this->doRemoveMetadata($key)) {
+      $this->hasMetadataChangedFromFileVersion = TRUE;
+      if ($this->isMetadataLoaded === FileMetadataInterface::LOADED_FROM_CACHE) {
+        $this->hasMetadataChangedFromCacheVersion = TRUE;
+      }
+      return TRUE;
+    }
+    return FALSE;
+  }
+
+  /**
+   * Removes a metadata element.
+   *
+   * @param mixed $key
+   *   A key to determine the metadata element to be removed.
+   *
+   * @return bool
+   *   TRUE if metadata was removed successfully, FALSE otherwise.
+   */
+  abstract protected function doRemoveMetadata($key);
+
+  /**
+   * {@inheritdoc}
+   */
+  public function isSaveToFileSupported() {
+    return FALSE;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function saveMetadataToFile() {
+    if (!$this->isSaveToFileSupported()) {
+      throw new FileMetadataException('Write metadata to file is not supported', $this->getPluginId(), __FUNCTION__);
+    }
+    if ($this->metadata === NULL) {
+      return FALSE;
+    }
+    if ($this->hasMetadataChangedFromFileVersion) {
+      // Clears cache so that next time metadata will be fetched from file.
+      $this->deleteCachedMetadata();
+      return $this->doSaveMetadataToFile();
+    }
+    return FALSE;
+  }
+
+  /**
+   * Saves metadata to file at URI.
+   *
+   * @return bool
+   *   TRUE if metadata was saved successfully, FALSE otherwise.
+   */
+  protected function doSaveMetadataToFile() {
+    return FALSE;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function saveMetadataToCache(array $tags = []) {
+    if ($this->metadata === NULL) {
+      return FALSE;
+    }
+    if (($cache_settings = $this->isUriFileMetadataCacheable()) === FALSE) {
+      return FALSE;
+    }
+    if ($this->isMetadataLoaded !== FileMetadataInterface::LOADED_FROM_CACHE || ($this->isMetadataLoaded === FileMetadataInterface::LOADED_FROM_CACHE && $this->hasMetadataChangedFromCacheVersion)) {
+      $tags = Cache::mergeTags($tags, $this->getConfigObject()->getCacheTags());
+      $tags = Cache::mergeTags($tags, $this->configFactory->get('file_mdm.settings')->getCacheTags());
+      $expire = $cache_settings['expiration'] === -1 ? Cache::PERMANENT : time() + $cache_settings['expiration'];
+      $this->cache->set("hash:{$this->getPluginId()}:{$this->hash}", $this->getMetadataToCache(), $expire, $tags);
+      $this->hasMetadataChangedFromCacheVersion = FALSE;
+      return TRUE;
+    }
+    return FALSE;
+  }
+
+  /**
+   * Gets metadata to save to cache.
+   *
+   * @return mixed
+   *   The metadata to be cached.
+   */
+  protected function getMetadataToCache() {
+    return $this->metadata;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function deleteCachedMetadata() {
+    if ($this->isUriFileMetadataCacheable() === FALSE) {
+      return FALSE;
+    }
+    $plugin_id = $this->getPluginId();
+    $this->cache->delete("hash:{$plugin_id}:{$this->hash}");
+    $this->hasMetadataChangedFromCacheVersion = FALSE;
+    return TRUE;
+  }
+
+}