322 lines
9.6 KiB
322 lines
9.6 KiB
<?php |
|
|
|
namespace Drupal\islandora\MediaSource; |
|
|
|
use Drupal\Core\Entity\EntityTypeManager; |
|
use Drupal\Core\Entity\Query\QueryFactory; |
|
use Drupal\Core\File\FileSystem; |
|
use Drupal\Core\Language\LanguageManagerInterface; |
|
use Drupal\Core\Session\AccountInterface; |
|
use Drupal\file\FileInterface; |
|
use Drupal\media\MediaInterface; |
|
use Drupal\media\MediaTypeInterface; |
|
use Drupal\node\NodeInterface; |
|
use Drupal\taxonomy\TermInterface; |
|
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; |
|
use Symfony\Component\HttpKernel\Exception\HttpException; |
|
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; |
|
|
|
/** |
|
* Utility functions for working with source files for Media. |
|
*/ |
|
class MediaSourceService { |
|
|
|
/** |
|
* The entity type manager. |
|
* |
|
* @var \Drupal\Core\Entity\EntityTypeManager |
|
*/ |
|
protected $entityTypeManager; |
|
|
|
/** |
|
* Current user. |
|
* |
|
* @var \Drupal\Core\Session\AccountInterface |
|
*/ |
|
protected $account; |
|
|
|
/** |
|
* Language manager. |
|
* |
|
* @var \Drupal\Core\Language\LanguageManagerInterface |
|
*/ |
|
protected $languageManager; |
|
|
|
/** |
|
* Entity query. |
|
* |
|
* @var \Drupal\Core\Entity\Query\QueryFactory |
|
*/ |
|
protected $entityQuery; |
|
|
|
/** |
|
* File system service. |
|
* |
|
* @var \Drupal\Core\File\FileSystem |
|
*/ |
|
protected $fileSystem; |
|
|
|
/** |
|
* Constructor. |
|
* |
|
* @param \Drupal\Core\Entity\EntityTypeManager $entity_type_manager |
|
* The entity type manager. |
|
* @param \Drupal\Core\Session\AccountInterface $account |
|
* The current user. |
|
* @param \Drupal\Core\Language\LanguageManagerInterface $language_manager |
|
* Language manager. |
|
* @param \Drupal\Core\Entity\Query\QueryFactory $entity_query |
|
* Entity query. |
|
* @param \Drupal\Core\File\FileSystem $file_system |
|
* File system service. |
|
*/ |
|
public function __construct( |
|
EntityTypeManager $entity_type_manager, |
|
AccountInterface $account, |
|
LanguageManagerInterface $language_manager, |
|
QueryFactory $entity_query, |
|
FileSystem $file_system |
|
) { |
|
$this->entityTypeManager = $entity_type_manager; |
|
$this->account = $account; |
|
$this->languageManager = $language_manager; |
|
$this->entityQuery = $entity_query; |
|
$this->fileSystem = $file_system; |
|
} |
|
|
|
/** |
|
* Gets the name of a source field for a Media. |
|
* |
|
* @param string $media_type |
|
* Media bundle whose source field you are searching for. |
|
* |
|
* @return string|null |
|
* Field name if it exists in configuration, else NULL. |
|
*/ |
|
public function getSourceFieldName($media_type) { |
|
$bundle = $this->entityTypeManager->getStorage('media_type')->load($media_type); |
|
if (!$bundle) { |
|
throw new NotFoundHttpException("Bundle $media_type does not exist"); |
|
} |
|
|
|
$type_configuration = $bundle->get('source_configuration'); |
|
if (!isset($type_configuration['source_field'])) { |
|
return NULL; |
|
} |
|
|
|
return $type_configuration['source_field']; |
|
} |
|
|
|
/** |
|
* Gets the value of a source field for a Media. |
|
* |
|
* @param \Drupal\media\MediaInterface $media |
|
* Media whose source field you are searching for. |
|
* |
|
* @return \Drupal\file\FileInterface |
|
* File if it exists |
|
* |
|
* @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException |
|
*/ |
|
public function getSourceFile(MediaInterface $media) { |
|
// Get the source field for the media type. |
|
$source_field = $this->getSourceFieldName($media->bundle()); |
|
|
|
if (empty($source_field)) { |
|
throw new NotFoundHttpException("Source field not set for {$media->bundle()} media"); |
|
} |
|
|
|
// Get the file from the media. |
|
$files = $media->get($source_field)->referencedEntities(); |
|
$file = reset($files); |
|
|
|
return $file; |
|
} |
|
|
|
/** |
|
* Updates a media's source field with the supplied resource. |
|
* |
|
* @param \Drupal\media\MediaInterface $media |
|
* The media to update. |
|
* @param resource $resource |
|
* New file contents as a resource. |
|
* @param string $mimetype |
|
* New mimetype of contents. |
|
* |
|
* @throws HttpException |
|
*/ |
|
public function updateSourceField( |
|
MediaInterface $media, |
|
$resource, |
|
$mimetype |
|
) { |
|
$source_field = $this->getSourceFieldName($media->bundle()); |
|
$file = $this->getSourceFile($media); |
|
|
|
// Update it. |
|
$this->updateFile($file, $resource, $mimetype); |
|
$file->save(); |
|
|
|
// Set fields provided by type plugin and mapped in bundle configuration |
|
// for the media. |
|
foreach ($media->bundle->entity->getFieldMap() as $source => $destination) { |
|
if ($media->hasField($destination) && $value = $media->getSource()->getMetadata($media, $source)) { |
|
$media->set($destination, $value); |
|
} |
|
// Ensure width and height are updated on File reference when it's an |
|
// image. Otherwise you run into scaling problems when updating images |
|
// with different sizes. |
|
if ($source == 'width' || $source == 'height') { |
|
$media->get($source_field)->first()->set($source, $value); |
|
} |
|
} |
|
|
|
$media->save(); |
|
} |
|
|
|
/** |
|
* Updates a File's binary contents on disk. |
|
* |
|
* @param \Drupal\file\FileInterface $file |
|
* File to update. |
|
* @param resource $resource |
|
* Stream holding the new contents. |
|
* @param string $mimetype |
|
* Mimetype of new contents. |
|
*/ |
|
protected function updateFile(FileInterface $file, $resource, $mimetype = NULL) { |
|
$uri = $file->getFileUri(); |
|
|
|
$destination = fopen($uri, 'wb'); |
|
if (!$destination) { |
|
throw new HttpException(500, "File $uri could not be opened to write."); |
|
} |
|
|
|
$content_length = stream_copy_to_stream($resource, $destination); |
|
|
|
if ($content_length === FALSE) { |
|
throw new HttpException(500, "Request body could not be copied to $uri"); |
|
} |
|
|
|
if ($content_length === 0) { |
|
// Clean up the newly created, empty file. |
|
unlink($uri); |
|
throw new HttpException(400, "No bytes were copied to $uri"); |
|
} |
|
|
|
if (!empty($mimetype)) { |
|
$file->setMimeType($mimetype); |
|
} |
|
|
|
// Flush the image cache for the image so thumbnails get regenerated. |
|
image_path_flush($uri); |
|
} |
|
|
|
/** |
|
* Creates a new Media using the provided resource, adding it to a Node. |
|
* |
|
* @param \Drupal\node\NodeInterface $node |
|
* The node to reference the newly created Media. |
|
* @param \Drupal\media\MediaTypeInterface $media_type |
|
* Media type for new media. |
|
* @param \Drupal\taxonomy\TermInterface $taxonomy_term |
|
* Term from the 'Behavior' vocabulary to give to new media. |
|
* @param resource $resource |
|
* New file contents as a resource. |
|
* @param string $mimetype |
|
* New mimetype of contents. |
|
* @param string $content_location |
|
* Drupal/PHP stream wrapper for where to upload the binary. |
|
* |
|
* @throws HttpException |
|
*/ |
|
public function putToNode( |
|
NodeInterface $node, |
|
MediaTypeInterface $media_type, |
|
TermInterface $taxonomy_term, |
|
$resource, |
|
$mimetype, |
|
$content_location |
|
) { |
|
$existing = $this->entityQuery->get('media') |
|
->condition('field_media_of', $node->id()) |
|
->condition('field_tags', $taxonomy_term->id()) |
|
->execute(); |
|
|
|
if (!empty($existing)) { |
|
// Just update already existing media. |
|
$media = $this->entityTypeManager->getStorage('media')->load(reset($existing)); |
|
$this->updateSourceField( |
|
$media, |
|
$resource, |
|
$mimetype |
|
); |
|
return FALSE; |
|
} |
|
else { |
|
// Otherwise, the media doesn't exist yet. |
|
// So make everything by hand. |
|
// Get the source field for the media type. |
|
$bundle = $media_type->id(); |
|
$source_field = $this->getSourceFieldName($bundle); |
|
if (empty($source_field)) { |
|
throw new NotFoundHttpException("Source field not set for $bundle media"); |
|
} |
|
|
|
// Construct the File. |
|
$file = $this->entityTypeManager->getStorage('file')->create([ |
|
'uid' => $this->account->id(), |
|
'uri' => $content_location, |
|
'filename' => $this->fileSystem->basename($content_location), |
|
'filemime' => $mimetype, |
|
'status' => FILE_STATUS_PERMANENT, |
|
]); |
|
|
|
// Validate file extension. |
|
$source_field_config = $this->entityTypeManager->getStorage('field_config')->load("media.$bundle.$source_field"); |
|
$valid_extensions = $source_field_config->getSetting('file_extensions'); |
|
$errors = file_validate_extensions($file, $valid_extensions); |
|
|
|
if (!empty($errors)) { |
|
throw new BadRequestHttpException("Invalid file extension. Valid types are $valid_extensions"); |
|
} |
|
|
|
$directory = $this->fileSystem->dirname($content_location); |
|
if (!file_prepare_directory($directory, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS)) { |
|
throw new HttpException(500, "The destination directory does not exist, could not be created, or is not writable"); |
|
} |
|
|
|
// Copy over the file content. |
|
$this->updateFile($file, $resource, $mimetype); |
|
$file->save(); |
|
|
|
// Construct the Media. |
|
$media_struct = [ |
|
'bundle' => $bundle, |
|
'uid' => $this->account->id(), |
|
'name' => $file->getFilename(), |
|
'langcode' => $this->languageManager->getDefaultLanguage()->getId(), |
|
"$source_field" => [ |
|
'target_id' => $file->id(), |
|
], |
|
'field_tags' => [ |
|
'target_id' => $taxonomy_term->id(), |
|
], |
|
'field_media_of' => [ |
|
'target_id' => $node->id(), |
|
], |
|
]; |
|
|
|
// Set alt text. |
|
if ($source_field_config->getSetting('alt_field') && $source_field_config->getSetting('alt_field_required')) { |
|
$media_struct[$source_field]['alt'] = $file->getFilename; |
|
} |
|
|
|
$media = $this->entityTypeManager->getStorage('media')->create($media_struct); |
|
$media->save(); |
|
return $media; |
|
} |
|
|
|
} |
|
|
|
}
|
|
|