|
|
|
<?php
|
|
|
|
|
|
|
|
namespace Drupal\islandora;
|
|
|
|
|
|
|
|
use Drupal\context\ContextManager;
|
|
|
|
use Drupal\Core\Entity\ContentEntityInterface;
|
|
|
|
use Drupal\Core\Entity\EntityFieldManager;
|
|
|
|
use Drupal\Core\Entity\EntityTypeManager;
|
|
|
|
use Drupal\Core\Entity\Query\QueryException;
|
|
|
|
use Drupal\Core\Entity\Query\QueryFactory;
|
|
|
|
use Drupal\Core\Entity\Query\QueryInterface;
|
|
|
|
use Drupal\Core\Site\Settings;
|
|
|
|
use Drupal\file\FileInterface;
|
|
|
|
use Drupal\flysystem\FlysystemFactory;
|
|
|
|
use Drupal\islandora\ContextProvider\NodeContextProvider;
|
|
|
|
use Drupal\islandora\ContextProvider\MediaContextProvider;
|
|
|
|
use Drupal\islandora\ContextProvider\FileContextProvider;
|
|
|
|
use Drupal\islandora\ContextProvider\TermContextProvider;
|
|
|
|
use Drupal\media\MediaInterface;
|
|
|
|
use Drupal\node\NodeInterface;
|
|
|
|
use Drupal\taxonomy\TermInterface;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Utility functions for figuring out when to fire derivative reactions.
|
|
|
|
*/
|
|
|
|
class IslandoraUtils {
|
|
|
|
|
|
|
|
const EXTERNAL_URI_FIELD = 'field_external_uri';
|
|
|
|
const MEDIA_OF_FIELD = 'field_media_of';
|
|
|
|
const MEDIA_USAGE_FIELD = 'field_media_use';
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The entity type manager.
|
|
|
|
*
|
|
|
|
* @var \Drupal\Core\Entity\EntityTypeManager
|
|
|
|
*/
|
|
|
|
protected $entityTypeManager;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The entity field manager.
|
|
|
|
*
|
|
|
|
* @var \Drupal\Core\Entity\EntityFieldManager
|
|
|
|
*/
|
|
|
|
protected $entityFieldManager;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Entity query.
|
|
|
|
*
|
|
|
|
* @var \Drupal\Core\Entity\Query\QueryFactory
|
|
|
|
*/
|
|
|
|
protected $entityQuery;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Context manager.
|
|
|
|
*
|
|
|
|
* @var \Drupal\context\ContextManager
|
|
|
|
*/
|
|
|
|
protected $contextManager;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Flysystem factory.
|
|
|
|
*
|
|
|
|
* @var \Drupal\flysystem\FlysystemFactory
|
|
|
|
*/
|
|
|
|
protected $flysystemFactory;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Constructor.
|
|
|
|
*
|
|
|
|
* @param \Drupal\Core\Entity\EntityTypeManager $entity_type_manager
|
|
|
|
* The entity type manager.
|
|
|
|
* @param \Drupal\Core\Entity\EntityFieldManager $entity_field_manager
|
|
|
|
* The entity field manager.
|
|
|
|
* @param \Drupal\Core\Entity\Query\QueryFactory $entity_query
|
|
|
|
* Entity query.
|
|
|
|
* @param \Drupal\context\ContextManager $context_manager
|
|
|
|
* Context manager.
|
|
|
|
* @param \Drupal\flysystem\FlysystemFactory $flysystem_factory
|
|
|
|
* Flysystem factory.
|
|
|
|
*/
|
|
|
|
public function __construct(
|
|
|
|
EntityTypeManager $entity_type_manager,
|
|
|
|
EntityFieldManager $entity_field_manager,
|
|
|
|
QueryFactory $entity_query,
|
|
|
|
ContextManager $context_manager,
|
|
|
|
FlysystemFactory $flysystem_factory
|
|
|
|
) {
|
|
|
|
$this->entityTypeManager = $entity_type_manager;
|
|
|
|
$this->entityFieldManager = $entity_field_manager;
|
|
|
|
$this->entityQuery = $entity_query;
|
|
|
|
$this->contextManager = $context_manager;
|
|
|
|
$this->flysystemFactory = $flysystem_factory;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Gets nodes that a media belongs to.
|
|
|
|
*
|
|
|
|
* @param \Drupal\media\MediaInterface $media
|
|
|
|
* The Media whose node you are searching for.
|
|
|
|
*
|
|
|
|
* @return \Drupal\node\NodeInterface
|
|
|
|
* Parent node.
|
|
|
|
*
|
|
|
|
* @throws \Drupal\Core\TypedData\Exception\MissingDataException
|
|
|
|
* Method $field->first() throws if data structure is unset and no item can
|
|
|
|
* be created.
|
|
|
|
*/
|
|
|
|
public function getParentNode(MediaInterface $media) {
|
|
|
|
if (!$media->hasField(self::MEDIA_OF_FIELD)) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
$field = $media->get(self::MEDIA_OF_FIELD);
|
|
|
|
if ($field->isEmpty()) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
$parent = $field->first()
|
|
|
|
->get('entity')
|
|
|
|
->getTarget();
|
|
|
|
if (!is_null($parent)) {
|
|
|
|
return $parent->getValue();
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Gets media that belong to a node.
|
|
|
|
*
|
|
|
|
* @param \Drupal\node\NodeInterface $node
|
|
|
|
* The parent node.
|
|
|
|
*
|
|
|
|
* @return \Drupal\media\MediaInterface[]
|
|
|
|
* The children Media.
|
|
|
|
*
|
|
|
|
* @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
|
|
|
|
* Calling getStorage() throws if the entity type doesn't exist.
|
|
|
|
* @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
|
|
|
|
* Calling getStorage() throws if the storage handler couldn't be loaded.
|
|
|
|
*/
|
|
|
|
public function getMedia(NodeInterface $node) {
|
|
|
|
if (!$this->entityTypeManager->getStorage('field_storage_config')->load('media.' . self::MEDIA_OF_FIELD)) {
|
|
|
|
return [];
|
|
|
|
}
|
|
|
|
$mids = $this->entityQuery->get('media')->condition(self::MEDIA_OF_FIELD, $node->id())->execute();
|
|
|
|
if (empty($mids)) {
|
|
|
|
return [];
|
|
|
|
}
|
|
|
|
return $this->entityTypeManager->getStorage('media')->loadMultiple($mids);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Gets media that belong to a node with the specified term.
|
|
|
|
*
|
|
|
|
* @param \Drupal\node\NodeInterface $node
|
|
|
|
* The parent node.
|
|
|
|
* @param \Drupal\taxonomy\TermInterface $term
|
|
|
|
* Taxonomy term.
|
|
|
|
*
|
|
|
|
* @return \Drupal\media\MediaInterface
|
|
|
|
* The child Media.
|
|
|
|
*
|
|
|
|
* @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
|
|
|
|
* Calling getStorage() throws if the entity type doesn't exist.
|
|
|
|
* @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
|
|
|
|
* Calling getStorage() throws if the storage handler couldn't be loaded.
|
|
|
|
*/
|
|
|
|
public function getMediaWithTerm(NodeInterface $node, TermInterface $term) {
|
|
|
|
$mids = $this->getMediaReferencingNodeAndTerm($node, $term);
|
|
|
|
if (empty($mids)) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
return $this->entityTypeManager->getStorage('media')->load(reset($mids));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Gets Media that reference a File.
|
|
|
|
*
|
|
|
|
* @param int $fid
|
|
|
|
* File id.
|
|
|
|
*
|
|
|
|
* @return \Drupal\media\MediaInterface[]
|
|
|
|
* Array of media.
|
|
|
|
*
|
|
|
|
* @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
|
|
|
|
* Calling getStorage() throws if the entity type doesn't exist.
|
|
|
|
* @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
|
|
|
|
* Calling getStorage() throws if the storage handler couldn't be loaded.
|
|
|
|
*/
|
|
|
|
public function getReferencingMedia($fid) {
|
|
|
|
// Get media fields that reference files.
|
|
|
|
$fields = $this->getReferencingFields('media', 'file');
|
|
|
|
|
|
|
|
// Process field names, stripping off 'media.' and appending 'target_id'.
|
|
|
|
$conditions = array_map(
|
|
|
|
function ($field) {
|
|
|
|
return ltrim($field, 'media.') . '.target_id';
|
|
|
|
},
|
|
|
|
$fields
|
|
|
|
);
|
|
|
|
|
|
|
|
// Query for media that reference this file.
|
|
|
|
$query = $this->entityQuery->get('media', 'OR');
|
|
|
|
foreach ($conditions as $condition) {
|
|
|
|
$query->condition($condition, $fid);
|
|
|
|
}
|
|
|
|
|
|
|
|
return $this->entityTypeManager->getStorage('media')->loadMultiple($query->execute());
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Gets the taxonomy term associated with an external uri.
|
|
|
|
*
|
|
|
|
* @param string $uri
|
|
|
|
* External uri.
|
|
|
|
*
|
|
|
|
* @return \Drupal\taxonomy\TermInterface|null
|
|
|
|
* Term or NULL if not found.
|
|
|
|
*
|
|
|
|
* @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
|
|
|
|
* Calling getStorage() throws if the entity type doesn't exist.
|
|
|
|
* @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
|
|
|
|
* Calling getStorage() throws if the storage handler couldn't be loaded.
|
|
|
|
*/
|
|
|
|
public function getTermForUri($uri) {
|
|
|
|
$results = $this->entityQuery->get('taxonomy_term')
|
|
|
|
->condition(self::EXTERNAL_URI_FIELD . '.uri', $uri)
|
|
|
|
->execute();
|
|
|
|
if (empty($results)) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
return $this->entityTypeManager->getStorage('taxonomy_term')->load(reset($results));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Gets the taxonomy term associated with an external uri.
|
|
|
|
*
|
|
|
|
* @param \Drupal\taxonomy\TermInterface $term
|
|
|
|
* Taxonomy term.
|
|
|
|
*
|
|
|
|
* @return string|null
|
|
|
|
* URI or NULL if not found.
|
|
|
|
*
|
|
|
|
* @throws \Drupal\Core\TypedData\Exception\MissingDataException
|
|
|
|
* Method $field->first() throws if data structure is unset and no item can
|
|
|
|
* be created.
|
|
|
|
*/
|
|
|
|
public function getUriForTerm(TermInterface $term) {
|
|
|
|
if ($term && $term->hasField(self::EXTERNAL_URI_FIELD)) {
|
|
|
|
$field = $term->get(self::EXTERNAL_URI_FIELD);
|
|
|
|
if (!$field->isEmpty()) {
|
|
|
|
$link = $field->first()->getValue();
|
|
|
|
return $link['uri'];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Executes context reactions for a Node.
|
|
|
|
*
|
|
|
|
* @param string $reaction_type
|
|
|
|
* Reaction type.
|
|
|
|
* @param \Drupal\node\NodeInterface $node
|
|
|
|
* Node to evaluate contexts and pass to reaction.
|
|
|
|
*/
|
|
|
|
public function executeNodeReactions($reaction_type, NodeInterface $node) {
|
|
|
|
$provider = new NodeContextProvider($node);
|
|
|
|
$provided = $provider->getRuntimeContexts([]);
|
|
|
|
$this->contextManager->evaluateContexts($provided);
|
|
|
|
|
|
|
|
// Fire off index reactions.
|
|
|
|
foreach ($this->contextManager->getActiveReactions($reaction_type) as $reaction) {
|
|
|
|
$reaction->execute($node);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Executes context reactions for a Media.
|
|
|
|
*
|
|
|
|
* @param string $reaction_type
|
|
|
|
* Reaction type.
|
|
|
|
* @param \Drupal\media\MediaInterface $media
|
|
|
|
* Media to evaluate contexts and pass to reaction.
|
|
|
|
*/
|
|
|
|
public function executeMediaReactions($reaction_type, MediaInterface $media) {
|
|
|
|
$provider = new MediaContextProvider($media);
|
|
|
|
$provided = $provider->getRuntimeContexts([]);
|
|
|
|
$this->contextManager->evaluateContexts($provided);
|
|
|
|
|
|
|
|
// Fire off index reactions.
|
|
|
|
foreach ($this->contextManager->getActiveReactions($reaction_type) as $reaction) {
|
|
|
|
$reaction->execute($media);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Executes context reactions for a File.
|
|
|
|
*
|
|
|
|
* @param string $reaction_type
|
|
|
|
* Reaction type.
|
|
|
|
* @param \Drupal\file\FileInterface $file
|
|
|
|
* File to evaluate contexts and pass to reaction.
|
|
|
|
*/
|
|
|
|
public function executeFileReactions($reaction_type, FileInterface $file) {
|
|
|
|
$provider = new FileContextProvider($file);
|
|
|
|
$provided = $provider->getRuntimeContexts([]);
|
|
|
|
$this->contextManager->evaluateContexts($provided);
|
|
|
|
|
|
|
|
// Fire off index reactions.
|
|
|
|
foreach ($this->contextManager->getActiveReactions($reaction_type) as $reaction) {
|
|
|
|
$reaction->execute($file);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Executes context reactions for a File.
|
|
|
|
*
|
|
|
|
* @param string $reaction_type
|
|
|
|
* Reaction type.
|
|
|
|
* @param \Drupal\taxonomy\TermInterface $term
|
|
|
|
* Term to evaluate contexts and pass to reaction.
|
|
|
|
*/
|
|
|
|
public function executeTermReactions($reaction_type, TermInterface $term) {
|
|
|
|
$provider = new TermContextProvider($term);
|
|
|
|
$provided = $provider->getRuntimeContexts([]);
|
|
|
|
$this->contextManager->evaluateContexts($provided);
|
|
|
|
|
|
|
|
// Fire off index reactions.
|
|
|
|
foreach ($this->contextManager->getActiveReactions($reaction_type) as $reaction) {
|
|
|
|
$reaction->execute($term);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Executes derivative reactions for a Media and Node.
|
|
|
|
*
|
|
|
|
* @param string $reaction_type
|
|
|
|
* Reaction type.
|
|
|
|
* @param \Drupal\node\NodeInterface $node
|
|
|
|
* Node to pass to reaction.
|
|
|
|
* @param \Drupal\media\MediaInterface $media
|
|
|
|
* Media to evaluate contexts.
|
|
|
|
*/
|
|
|
|
public function executeDerivativeReactions($reaction_type, NodeInterface $node, MediaInterface $media) {
|
|
|
|
$provider = new MediaContextProvider($media);
|
|
|
|
$provided = $provider->getRuntimeContexts([]);
|
|
|
|
$this->contextManager->evaluateContexts($provided);
|
|
|
|
|
|
|
|
// Fire off index reactions.
|
|
|
|
foreach ($this->contextManager->getActiveReactions($reaction_type) as $reaction) {
|
|
|
|
$reaction->execute($node);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Evaluates if fields have changed between two instances of a ContentEntity.
|
|
|
|
*
|
|
|
|
* @param \Drupal\Core\Entity\ContentEntityInterface $entity
|
|
|
|
* The updated entity.
|
|
|
|
* @param \Drupal\Core\Entity\ContentEntityInterface $original
|
|
|
|
* The original entity.
|
|
|
|
*
|
|
|
|
* @return bool
|
|
|
|
* TRUE if the fields have changed.
|
|
|
|
*/
|
|
|
|
public function haveFieldsChanged(ContentEntityInterface $entity, ContentEntityInterface $original) {
|
|
|
|
|
|
|
|
$field_definitions = $this->entityFieldManager->getFieldDefinitions($entity->getEntityTypeId(), $entity->bundle());
|
|
|
|
|
|
|
|
$ignore_list = ['vid' => 1, 'changed' => 1, 'path' => 1];
|
|
|
|
$field_definitions = array_diff_key($field_definitions, $ignore_list);
|
|
|
|
|
|
|
|
foreach ($field_definitions as $field_name => $field_definition) {
|
|
|
|
$langcodes = array_keys($entity->getTranslationLanguages());
|
|
|
|
|
|
|
|
if ($langcodes !== array_keys($original->getTranslationLanguages())) {
|
|
|
|
// If the list of langcodes has changed, we need to save.
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
foreach ($langcodes as $langcode) {
|
|
|
|
$items = $entity
|
|
|
|
->getTranslation($langcode)
|
|
|
|
->get($field_name)
|
|
|
|
->filterEmptyItems();
|
|
|
|
$original_items = $original
|
|
|
|
->getTranslation($langcode)
|
|
|
|
->get($field_name)
|
|
|
|
->filterEmptyItems();
|
|
|
|
|
|
|
|
// If the field items are not equal, we need to save.
|
|
|
|
if (!$items->equals($original_items)) {
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns a list of all available filesystem schemes.
|
|
|
|
*
|
|
|
|
* @return String[]
|
|
|
|
* List of all available filesystem schemes.
|
|
|
|
*/
|
|
|
|
public function getFilesystemSchemes() {
|
|
|
|
$schemes = ['public'];
|
|
|
|
if (!empty(Settings::get('file_private_path'))) {
|
|
|
|
$schemes[] = 'private';
|
|
|
|
}
|
|
|
|
return array_merge($schemes, $this->flysystemFactory->getSchemes());
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get array of media ids that have fields that reference $node and $term.
|
|
|
|
*
|
|
|
|
* @param \Drupal\node\NodeInterface $node
|
|
|
|
* The node to reference.
|
|
|
|
* @param \Drupal\taxonomy\TermInterface $term
|
|
|
|
* The term to reference.
|
|
|
|
*
|
|
|
|
* @return array|int|null
|
|
|
|
* Array of media IDs or NULL.
|
|
|
|
*/
|
|
|
|
public function getMediaReferencingNodeAndTerm(NodeInterface $node, TermInterface $term) {
|
|
|
|
$term_fields = $this->getReferencingFields('media', 'taxonomy_term');
|
|
|
|
if (count($term_fields) <= 0) {
|
|
|
|
\Drupal::logger("No media fields reference a taxonomy term");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
$node_fields = $this->getReferencingFields('media', 'node');
|
|
|
|
if (count($node_fields) <= 0) {
|
|
|
|
\Drupal::logger("No media fields reference a node.");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
$remove_entity = function (&$o) {
|
|
|
|
$o = substr($o, strpos($o, '.') + 1);
|
|
|
|
};
|
|
|
|
array_walk($term_fields, $remove_entity);
|
|
|
|
array_walk($node_fields, $remove_entity);
|
|
|
|
|
|
|
|
$query = $this->entityQuery->get('media');
|
|
|
|
$taxon_condition = $this->getEntityQueryOrCondition($query, $term_fields, $term->id());
|
|
|
|
$query->condition($taxon_condition);
|
|
|
|
$node_condition = $this->getEntityQueryOrCondition($query, $node_fields, $node->id());
|
|
|
|
$query->condition($node_condition);
|
|
|
|
// Does the tags field exist?
|
|
|
|
try {
|
|
|
|
$mids = $query->execute();
|
|
|
|
}
|
|
|
|
catch (QueryException $e) {
|
|
|
|
$mids = [];
|
|
|
|
}
|
|
|
|
return $mids;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the fields on an entity of $entity_type that reference a $target_type.
|
|
|
|
*
|
|
|
|
* @param string $entity_type
|
|
|
|
* Type of entity to search for.
|
|
|
|
* @param string $target_type
|
|
|
|
* Type of entity the field references.
|
|
|
|
*
|
|
|
|
* @return array
|
|
|
|
* Array of fields.
|
|
|
|
*/
|
|
|
|
public function getReferencingFields($entity_type, $target_type) {
|
|
|
|
$fields = $this->entityQuery->get('field_storage_config')
|
|
|
|
->condition('entity_type', $entity_type)
|
|
|
|
->condition('settings.target_type', $target_type)
|
|
|
|
->execute();
|
|
|
|
if (!is_array($fields)) {
|
|
|
|
$fields = [$fields];
|
|
|
|
}
|
|
|
|
return $fields;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Make an OR condition for an array of fields and a value.
|
|
|
|
*
|
|
|
|
* @param \Drupal\Core\Entity\Query\QueryInterface $query
|
|
|
|
* The QueryInterface for the query.
|
|
|
|
* @param array $fields
|
|
|
|
* The array of field names.
|
|
|
|
* @param string $value
|
|
|
|
* The value to search the fields for.
|
|
|
|
*
|
|
|
|
* @return \Drupal\Core\Entity\Query\ConditionInterface
|
|
|
|
* The OR condition to add to your query.
|
|
|
|
*/
|
|
|
|
private function getEntityQueryOrCondition(QueryInterface $query, array $fields, $value) {
|
|
|
|
$condition = $query->orConditionGroup();
|
|
|
|
foreach ($fields as $field) {
|
|
|
|
$condition->condition($field, $value);
|
|
|
|
}
|
|
|
|
return $condition;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|