media_library_extend_crowdriff-1.x-dev/src/CrowdriffAssetService.php
src/CrowdriffAssetService.php
<?php
namespace Drupal\media_library_extend_crowdriff;
use Drupal\Component\Render\PlainTextOutput;
use Drupal\Component\Utility\Bytes;
use Drupal\Core\Datetime\DrupalDateTime;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\File\FileSystemInterface;
use Drupal\Core\Queue\QueueFactory;
use Drupal\Core\Utility\Token;
use Drupal\crowdriff_api\CrowdriffService;
use Drupal\entity_usage\EntityUsage;
use Drupal\file\FileRepositoryInterface;
use Drupal\media\Entity\Media;
use Drupal\taxonomy\Entity\Term;
/**
* Crowdriff Asset Service.
*
* @package Drupal\media_library_extend_crowdriff
*/
class CrowdriffAssetService {
const CROWDRIFF_HASHTAGS_VID = 'crowdriff_hashtags';
const CROWDRIFF_KEYWORDS_VID = 'crowdriff_keywords';
const CROWDRIFF_OBJECTS_VID = 'crowdriff_objects';
/**
* The Crowdriff service.
*
* @var \Drupal\crowdriff_api\CrowdriffService
*/
protected $crowdriffService;
/**
* The entity type manager.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;
/**
* The file repository.
*
* @var \Drupal\file\FileRepositoryInterface
*/
protected $fileRepository;
/**
* The token service.
*
* @var \Drupal\Core\Utility\Token
*/
protected $token;
/**
* The file system service.
*
* @var \Drupal\Core\File\FileSystemInterface
*/
protected $fileSystem;
/**
* The queue factory service.
*
* @var \Drupal\Core\Queue\QueueFactory
*/
protected $queueFactory;
/**
* The entity usage service.
*
* @var \Drupal\entity_usage\EntityUsage
*/
protected $entityUsage;
/**
* CrowdriffAssetService constructor.
*
* @param \Drupal\crowdriff_api\CrowdriffService $crowdriff_service
* The crowdriff service.
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* The entity type manager.
* @param \Drupal\file\FileRepositoryInterface $file_repository
* The file repository.
* @param \Drupal\Core\Utility\Token $token
* The token service.
* @param \Drupal\Core\File\FileSystemInterface $file_system
* The file system service.
* @param \Drupal\Core\Queue\QueueFactory $queue_factory
* The queue factory service.
* @param \Drupal\entity_usage\EntityUsage $entity_usage
* The entity usage service.
*/
public function __construct(
CrowdriffService $crowdriff_service,
EntityTypeManagerInterface $entity_type_manager,
FileRepositoryInterface $file_repository,
Token $token,
FileSystemInterface $file_system,
QueueFactory $queue_factory,
EntityUsage $entity_usage
) {
$this->crowdriffService = $crowdriff_service;
$this->entityTypeManager = $entity_type_manager;
$this->fileRepository = $file_repository;
$this->token = $token;
$this->fileSystem = $file_system;
$this->queueFactory = $queue_factory;
$this->entityUsage = $entity_usage;
}
/**
* Get asset source field tied to target bundle.
*
* @param string $bundle
* The target bundle.
*
* @return string
* Returns the source field.
*
* @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
* @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
*/
public function getAssetSourceField(string $bundle): ?string {
$source_field = NULL;
$media_type = $this->entityTypeManager->getStorage('media_type')->load($bundle);
if ($media_type) {
$source_config = $media_type->getSource()->getConfiguration();
$source_field = $source_config['source_field'];
}
return $source_field;
}
/**
* Get asset entity usage.
*
* @param \Drupal\Core\Entity\EntityInterface $entity
* The entity.
* @param bool $nested
* Nest sources or not.
*
* @return array
* Returns array of entity sources/usage data.
*/
public function getAssetUsage(EntityInterface $entity, bool $nested = TRUE): array {
return $this->entityUsage->listSources($entity, $nested);
}
/**
* Adds media items to the asset sync queue for later processing.
*
* Adds all existing active media entities to the queue.
*
* @return int
* The number of items currently in the queue.
*
* @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
* @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
*/
public function refreshAssetQueue(): int {
$queue = $this->queueFactory->get('crowdriff_asset_refresh');
// We only want to re-queue everything when the queue is totally empty. This
// should help minimize the number of duplicate syncs we perform on assets.
if ($queue->numberOfItems() > 0) {
return $queue->numberOfItems();
}
// Get Crowdriff media assets.
$query = $this->entityTypeManager->getStorage('media')->getQuery();
$query->condition('bundle', ['crowdriff_image', 'crowdriff_video'], 'IN');
$media_ids = $query->execute();
foreach ($media_ids as $media_id) {
/** @var \Drupal\media\Entity\Media $media */
$media = $this->entityTypeManager->getStorage('media')->load($media_id);
if ($media->hasField('field_crowdriff_asset_uuid') && !$media->get('field_crowdriff_asset_uuid')->isEmpty()) {
$queue->createItem([
'media_id' => $media_id,
'asset_id' => $media->get('field_crowdriff_asset_uuid')->value,
]);
}
}
return $queue->numberOfItems();
}
/**
* Queue asset for refreshing.
*
* @param int $media_id
* The media entity id.
* @param string $asset_id
* The Crowdriff asset id.
*
* @return int|bool
* Return queue item id or FALSE otherwise.
*/
public function queueAsset($media_id, $asset_id) {
$queue = $this->queueFactory->get('crowdriff_asset_refresh');
return $queue->createItem([
'media_id' => $media_id,
'asset_id' => $asset_id,
]);
}
/**
* Get total number of items queued for asset refresh.
*
* @return int
* Returns total number of items.
*/
public function getTotalAssetQueuedItems(): int {
$queue = $this->queueFactory->get('crowdriff_asset_refresh');
return $queue->numberOfItems();
}
/**
* Get Crowdriff Asset by uuid.
*
* @param string $uuid
* The asset uuid.
*
* @return array
* Returns the asset.
*
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function getAsset(string $uuid): array {
$asset = [];
$data = $this->crowdriffService->getAssetsById($uuid);
if (!empty($data[$uuid])) {
$asset = $data[$uuid];
}
return $asset;
}
/**
* Check to see if asset already exists.
*
* @param string $uuid
* The asset id.
* @param string $bundle
* The target bundle.
*
* @return int|bool
* Returns entity id if asset already exists, FALSE otherwise.
*
* @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
* @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
*/
public function assetExists(string $uuid, string $bundle) {
$assets = $this->entityTypeManager->getStorage('media')->loadByProperties([
'bundle' => $bundle,
'field_crowdriff_asset_uuid' => $uuid,
]);
$asset = reset($assets);
if ($asset instanceof Media) {
return $asset->id();
}
return FALSE;
}
/**
* Check to see if asset exists and is unique.
*
* @param string $uuid
* The asset id.
* @param array $excluded
* The array of excluded assets.
*
* @return bool
* Returns TRUE if unique, FALSE if asset exists.
*
* @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
* @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
*/
public function isAssetUnique(string $uuid, array $excluded = []): bool {
// Get query.
$query = $this->entityTypeManager->getStorage('media')->getQuery();
// Filter results by asset id and bundles.
$query = $query
->condition('field_crowdriff_asset_uuid', $uuid, '=')
->condition('bundle', ['crowdriff_image', 'crowdriff_video'], 'IN');
// Exclude from result set.
if (!empty($excluded)) {
$query = $query->condition('mid', $excluded, 'NOT IN');
}
$count = $query->count()->execute();
if ($count) {
return FALSE;
}
else {
return TRUE;
}
}
/**
* Check asset file size against set max asset size.
*
* @param array $asset
* The asset.
* @param int $max_size
* The max size in MB for an asset.
*
* @return bool
* Returns TRUE if asset file size is valid, FALSE otherwise.
*/
public function checkAssetFilesize(array $asset, int $max_size): bool {
// Check asset file size.
$max_asset_bytes = Bytes::toNumber($max_size . 'MB');
$asset_size_bytes = 0;
// Check for image asset types.
if ($asset['media_type'] == 'image' && !empty($asset['image_original']['size'])) {
$asset_size_bytes = $asset['image_original']['size'];
}
// Check for video asset types.
if ($asset['media_type'] == 'video' && !empty($asset['video_original']['size'])) {
$asset_size_bytes = $asset['video_original']['size'];
}
// Check asset size.
if ($asset_size_bytes && $asset_size_bytes <= $max_asset_bytes) {
return TRUE;
}
return FALSE;
}
/**
* Check to see if term exists by name and vocabulary id.
*
* @param string $name
* The term name.
* @param string $vid
* The vocab id.
*
* @return int|bool
* Returns term id if found, FALSE otherwise.
*
* @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
* @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
*/
public function termExists(string $name, string $vid) {
$results = $this->entityTypeManager->getStorage('taxonomy_term')->loadByProperties([
'name' => $name,
'vid' => $vid,
]);
if ($term = reset($results)) {
return $term->id();
}
return FALSE;
}
/**
* Add/create new taxonomy term.
*
* @param string $name
* The term name.
* @param string $vid
* The vocab id.
*
* @return \Drupal\taxonomy\Entity\Term|bool
* Returns the term object or FALSE otherwise.
*
* @throws \Drupal\Core\Entity\EntityStorageException
*/
public function createTerm(string $name, string $vid) {
$term = Term::create([
'name' => $name,
'vid' => $vid,
]);
if ($term->save()) {
return $term;
}
else {
return FALSE;
}
}
/**
* Gets uri of a directory to store file uploads in.
*
* @param string $bundle
* The target bundle.
*
* @return string
* The uri of a directory for receiving uploads.
*
* @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
* @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
*/
public function getAssetUploadLocation(string $bundle): ?string {
// Determine field mapping from media type.
$media_type = $this->entityTypeManager->getStorage('media_type')->load($bundle);
$source_field_config = $media_type->getSource()->getSourceFieldDefinition($media_type)->getSettings();
$destination = trim($source_field_config['file_directory'], '/');
// Replace tokens. As the tokens might contain HTML we convert it to plain
// text.
$destination = PlainTextOutput::renderFromHtml($this->token->replace($destination, []));
$destination = $source_field_config['uri_scheme'] . '://' . $destination;
if (!$this->fileSystem->prepareDirectory($destination, FileSystemInterface::CREATE_DIRECTORY | FileSystemInterface::MODIFY_PERMISSIONS)) {
return NULL;
}
return $destination;
}
/**
* Download the asset file.
*
* @param array $asset_file
* The asset file information array.
* @param string $asset_id
* The asset id.
* @param string $upload_location
* The upload location/path.
*
* @return false|int
* Returns file id if successful, otherwise FALSE.
*
* @throws \Drupal\Core\Entity\EntityStorageException
*/
public function downloadAssetFile(array $asset_file, string $asset_id, string $upload_location) {
// Get file and it's contents.
if (!empty($asset_file['url'])) {
// Parse file url.
$asset_file_info = pathinfo($asset_file['url']);
// Get filename.
$asset_filename = $asset_id . '.' . $asset_file_info['extension'];
// Get file contents.
if ($asset_file_contents = file_get_contents($asset_file['url'])) {
// Save to filesystem.
if ($file = $this->fileRepository->writeData($asset_file_contents, $upload_location . '/' . $asset_filename, FileSystemInterface::EXISTS_REPLACE)) {
return $file->id();
}
}
}
return FALSE;
}
/**
* Determine if Media entity matches Crowdriff asset or not.
*
* @param array $asset
* The asset.
* @param \Drupal\media\Entity\Media $entity
* The media entity.
*
* @return bool
* Returns TRUE if changes found, FALSE otherwise.
*
* @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
* @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
*/
public function isAssetChanged(array $asset, Media $entity): bool {
$changed = FALSE;
// Asset created.
if (isset($asset['created_at'])) {
if ($entity->hasField('field_crowdriff_asset_created')) {
$dt = DrupalDateTime::createFromTimestamp(strtotime($asset['created_at']));
$dt->setTimezone(new \DateTimeZone('UTC'));
$created_at = $dt->format('Y-m-d\TH:i:s');
if ($entity->get('field_crowdriff_asset_created')->value != $created_at) {
$changed = TRUE;
}
}
if ($entity->hasField('field_crowdriff_asset_created_at')) {
if ($entity->get('field_crowdriff_asset_created_at')->value != $asset['created_at']) {
$changed = TRUE;
}
}
}
// Asset video original url (video_original.url).
if (isset($asset['video_original']['url']) && $entity->hasField('field_crowdriff_asset_video_orig')) {
if ($entity->get('field_crowdriff_asset_video_orig')->value != $asset['video_original']['url']) {
$changed = TRUE;
}
}
// Asset source (source).
if (isset($asset['source']) && $entity->hasField('field_crowdriff_asset_source')) {
if ($entity->get('field_crowdriff_asset_source')->value != $asset['source']) {
$changed = TRUE;
}
}
// Asset image original url (image_original.url).
if (isset($asset['image_original']['url']) && $entity->hasField('field_crowdriff_asset_image_orig')) {
if ($entity->get('field_crowdriff_asset_image_orig')->value != $asset['image_original']['url']) {
$changed = TRUE;
}
}
// Asset social url (native_url).
if (isset($asset['native_url']) && $entity->hasField('field_crowdriff_asset_social_url')) {
if ($entity->get('field_crowdriff_asset_social_url')->value != $asset['native_url']) {
$changed = TRUE;
}
}
// Asset screen name (user.screen_name or user.full_name).
if ($entity->hasField('field_crowdriff_asset_user_name') && isset($asset['user'])) {
$screen_name = '';
// Check screen_name.
if (!empty($asset['user']['screen_name'])) {
$screen_name = $asset['user']['screen_name'];
}
// Check full_name.
if (empty($screen_name) && !empty($asset['user']['full_name'])) {
$screen_name = $asset['user']['full_name'];
}
// Set screen name.
if ($entity->get('field_crowdriff_asset_user_name')->value != $screen_name) {
$changed = TRUE;
}
}
// Asset text/caption (text).
if ($entity->hasField('field_crowdriff_asset_text') && isset($asset['text'])) {
if ($entity->get('field_crowdriff_asset_text')->value != $asset['text']) {
$changed = TRUE;
}
}
// Asset hashtags taxonomy (hashtags).
if (isset($asset['hashtags']) && $entity->hasField('field_crowdriff_asset_hashtags')) {
$hashtags = [];
foreach ($asset['hashtags'] as $hashtag) {
// Check to see if hashtag exists.
$tid = $this->termExists($hashtag, CrowdriffAssetService::CROWDRIFF_HASHTAGS_VID);
if ($tid) {
$hashtags[] = ['target_id' => $tid];
}
}
if ($entity->get('field_crowdriff_asset_hashtags')->getValue() !== $hashtags) {
$changed = TRUE;
}
}
// Asset keywords taxonomy (keywords).
if (isset($asset['keywords']) && $entity->hasField('field_crowdriff_asset_keywords')) {
$keywords = [];
foreach ($asset['keywords'] as $keyword) {
// Check to see if keyword exists.
$tid = $this->termExists($keyword, CrowdriffAssetService::CROWDRIFF_KEYWORDS_VID);
if ($tid) {
$keywords[] = ['target_id' => $tid];
}
}
if ($entity->get('field_crowdriff_asset_keywords')->getValue() !== $keywords) {
$changed = TRUE;
}
}
// Asset objects taxonomy (objects).
if (isset($asset['objects']) && $entity->hasField('field_crowdriff_asset_objects')) {
$objects = [];
foreach ($asset['objects'] as $object) {
// Check to see if object exists.
$tid = $this->termExists($object, CrowdriffAssetService::CROWDRIFF_OBJECTS_VID);
if ($tid) {
$objects[] = ['target_id' => $tid];
}
}
if ($entity->get('field_crowdriff_asset_objects')->getValue() !== $objects) {
$changed = TRUE;
}
}
// Asset status (downloadable).
if ($entity->hasField('field_crowdriff_asset_status') && isset($asset['downloadable'])) {
if ((bool) $entity->get('field_crowdriff_asset_status')->value != (bool) $asset['downloadable']) {
$changed = TRUE;
}
}
return $changed;
}
/**
* Set common fields on a Crowdriff asset.
*
* @param array $asset
* The Crowdriff asset data.
* @param \Drupal\media\Entity\Media $entity
* The media entity.
*
* @return \Drupal\media\Entity\Media
* Returns the modified entity.
*
* @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
* @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
* @throws \Drupal\Core\Entity\EntityStorageException
*/
public function setCommonAssetFields(array $asset, Media $entity): Media {
// Make sure we have asset uuid.
if (empty($asset['uuid'])) {
return $entity;
}
// Set asset uuid (uuid).
if ($entity->hasField('field_crowdriff_asset_uuid') && $entity->get('field_crowdriff_asset_uuid')->isEmpty()) {
$entity->set('field_crowdriff_asset_uuid', $asset['uuid']);
}
// Set asset created (created_at).
if (!empty($asset['created_at'])) {
// Store drupal datetime version of created date.
if ($entity->hasField('field_crowdriff_asset_created')) {
$dt = DrupalDateTime::createFromTimestamp(strtotime($asset['created_at']));
$dt->setTimezone(new \DateTimeZone('UTC'));
if ($created_at = $dt->format('Y-m-d\TH:i:s')) {
$entity->set('field_crowdriff_asset_created', $created_at);
}
}
// Store raw version of created date.
if ($entity->hasField('field_crowdriff_asset_created_at')) {
$entity->set('field_crowdriff_asset_created_at', $asset['created_at']);
}
}
// Set asset video original url (video_original.url).
if (!empty($asset['video_original']['url']) && $entity->hasField('field_crowdriff_asset_video_orig')) {
$entity->set('field_crowdriff_asset_video_orig', $asset['video_original']['url']);
}
// Set asset source (source).
if (!empty($asset['source']) && $entity->hasField('field_crowdriff_asset_source')) {
$entity->set('field_crowdriff_asset_source', $asset['source']);
}
// Set asset image original url (image_original.url).
if (!empty($asset['image_original']['url']) && $entity->hasField('field_crowdriff_asset_image_orig')) {
$entity->set('field_crowdriff_asset_image_orig', $asset['image_original']['url']);
}
// Set asset social url (native_url).
if (!empty($asset['native_url']) && $entity->hasField('field_crowdriff_asset_social_url')) {
$entity->set('field_crowdriff_asset_social_url', $asset['native_url']);
}
// Set asset screen name (user.screen_name or user.full_name).
if ($entity->hasField('field_crowdriff_asset_user_name')) {
$screen_name = '';
// Check screen_name.
if (!empty($asset['user']['screen_name'])) {
$screen_name = $asset['user']['screen_name'];
}
// Check full_name.
if (empty($screen_name) && !empty($asset['user']['full_name'])) {
$screen_name = $asset['user']['full_name'];
}
// Set screen name.
if (!empty($screen_name)) {
$entity->set('field_crowdriff_asset_user_name', $screen_name);
}
}
// Set asset text/caption (text).
if ($entity->hasField('field_crowdriff_asset_text') && !empty($asset['text'])) {
$entity->set('field_crowdriff_asset_text', trim($asset['text']));
}
// Set hashtags taxonomy (hashtags).
if (!empty($asset['hashtags']) && $entity->hasField('field_crowdriff_asset_hashtags')) {
$hashtags = [];
foreach ($asset['hashtags'] as $hashtag) {
// Check to see if hashtag exists.
$tid = $this->termExists($hashtag, CrowdriffAssetService::CROWDRIFF_HASHTAGS_VID);
if (!$tid) {
// Create term if none is found.
/** @var \Drupal\taxonomy\Entity\Term $term */
if ($term = $this->createTerm($hashtag, CrowdriffAssetService::CROWDRIFF_HASHTAGS_VID)) {
$tid = $term->id();
}
}
if ($tid) {
$hashtags[] = ['target_id' => $tid];
}
}
$entity->set('field_crowdriff_asset_hashtags', $hashtags);
}
// Set keywords taxonomy (keywords).
if (!empty($asset['keywords']) && $entity->hasField('field_crowdriff_asset_keywords')) {
$keywords = [];
foreach ($asset['keywords'] as $keyword) {
// Check to see if keyword exists.
$tid = $this->termExists($keyword, CrowdriffAssetService::CROWDRIFF_KEYWORDS_VID);
if (!$tid) {
// Create term if none is found.
/** @var \Drupal\taxonomy\Entity\Term $term */
if ($term = $this->createTerm($keyword, CrowdriffAssetService::CROWDRIFF_KEYWORDS_VID)) {
$tid = $term->id();
}
}
if ($tid) {
$keywords[] = ['target_id' => $tid];
}
}
$entity->set('field_crowdriff_asset_keywords', $keywords);
}
// Set objects taxonomy (objects).
if (!empty($asset['objects']) && $entity->hasField('field_crowdriff_asset_objects')) {
$objects = [];
foreach ($asset['objects'] as $object) {
// Check to see if object exists.
$tid = $this->termExists($object, CrowdriffAssetService::CROWDRIFF_OBJECTS_VID);
if (!$tid) {
// Create term if none exists.
/** @var \Drupal\taxonomy\Entity\Term $term */
if ($term = $this->createTerm($object, CrowdriffAssetService::CROWDRIFF_OBJECTS_VID)) {
$tid = $term->id();
}
}
if ($tid) {
$objects[] = ['target_id' => $tid];
}
}
$entity->set('field_crowdriff_asset_objects', $objects);
}
// Set status (downloadable).
if ($entity->hasField('field_crowdriff_asset_status')) {
$entity->set('field_crowdriff_asset_status', (bool) $asset['downloadable']);
}
// Set exists flag.
if ($entity->hasField('field_crowdriff_asset_exists')) {
$entity->set('field_crowdriff_asset_exists', TRUE);
}
return $entity;
}
/**
* Delete asset media entities.
*
* @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
* @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
* @throws \Drupal\Core\Entity\EntityStorageException
*/
public function deleteAssets() {
// Get query.
$query = $this->entityTypeManager->getStorage('media')->getQuery();
$query = $query
->condition('bundle', ['crowdriff_image', 'crowdriff_video'], 'IN');
// Get array of entity ids.
$ids = $query->execute();
if (!empty($ids)) {
$entities = $this->entityTypeManager->getStorage('media')->loadMultiple($ids);
$this->entityTypeManager->getStorage('media')->delete($entities);
}
}
}
