View source
<?php
namespace Drupal\file_link\Plugin\Field\FieldType;
use Drupal\Core\Entity\RevisionableInterface;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Queue\QueueInterface;
use Drupal\Core\Site\Settings;
use Drupal\Core\TypedData\DataDefinition;
use Drupal\Core\Url;
use Drupal\file\Plugin\Field\FieldType\FileItem;
use Drupal\file_link\FileLinkInterface;
use Drupal\file_link\FileLinkQueueItem;
use Drupal\file_link\Plugin\QueueWorker\FileLinkMetadataUpdate;
use Drupal\link\Plugin\Field\FieldType\LinkItem;
use GuzzleHttp\Exception\RequestException;
use Psr\Http\Message\ResponseInterface;
class FileLinkItem extends LinkItem implements FileLinkInterface {
protected $response = NULL;
protected $exception = NULL;
protected $entityTypeManager;
protected $httpClient;
protected $queue;
protected $needsParsing = FALSE;
protected static $queued = [];
public static function defaultFieldSettings() {
return [
'file_extensions' => 'txt',
'no_extension' => FALSE,
'deferred_request' => FALSE,
] + parent::defaultFieldSettings();
}
public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
return parent::propertyDefinitions($field_definition) + [
'size' => DataDefinition::create('integer')
->setLabel(t('Size')),
'format' => DataDefinition::create('string')
->setLabel(t('Format')),
];
}
public static function schema(FieldStorageDefinitionInterface $field_definition) {
$schema = parent::schema($field_definition);
$schema['columns']['size'] = [
'description' => 'The size of the file.',
'type' => 'int',
'size' => 'big',
'unsigned' => TRUE,
];
$schema['columns']['format'] = [
'description' => 'The format of the file.',
'type' => 'varchar',
'length' => 255,
];
$schema['indexes']['size'] = [
'size',
];
$schema['indexes']['format'] = [
'format',
];
return $schema;
}
public function fieldSettingsForm(array $form, FormStateInterface $form_state) {
$element = parent::fieldSettingsForm($form, $form_state);
$extensions = str_replace(' ', ', ', $this
->getSetting('file_extensions'));
$element['file_extensions'] = [
'#type' => 'textfield',
'#title' => $this
->t('Allowed file extensions'),
'#default_value' => $extensions,
'#description' => $this
->t('Separate extensions with a space or comma and do not include the leading dot. Leave empty to allow any extension.'),
'#element_validate' => [
[
FileItem::class,
'validateExtensions',
],
],
'#maxlength' => 256,
];
$element['no_extension'] = [
'#type' => 'checkbox',
'#title' => $this
->t('Allow URLs without file extension'),
'#description' => $this
->t('The link can refer a document such as a wiki page or a dynamic generated page that has no extension. Check this if you want to allow such URLs.'),
'#default_value' => $this
->getSetting('no_extension'),
];
$element['deferred_request'] = [
'#type' => 'checkbox',
'#title' => $this
->t('Defer requests to cron'),
'#description' => $this
->t('The link will not be checked and validated immediately, instead the entity will be updated by cron and the size and format will be determined at that time. Check the logs for errors'),
'#default_value' => $this
->getSetting('deferred_request'),
];
return $element;
}
public static function generateSampleValue(FieldDefinitionInterface $field_definition) {
$values = parent::generateSampleValue($field_definition);
$values['size'] = 1234567;
$values['format'] = 'image/png';
return $values;
}
public function preSave() {
parent::preSave();
if (Settings::get('file_link.disable_http_requests', FALSE)) {
return;
}
$entity = $this
->getEntity();
$storage = $this
->getEntityTypeManager()
->getStorage($entity
->getEntityTypeId());
$original = $entity
->isNew() ? NULL : $storage
->loadUnchanged($entity
->id());
$field_name = $this
->getFieldDefinition()
->getName();
$original_uri = NULL;
$size = NULL;
$format = NULL;
if ($original !== NULL) {
$item = $original
->get($field_name)
->get($this
->getName());
if ($item != NULL) {
$values = $item
->getValue();
$original_uri = $values['uri'];
$size = $values['size'];
$format = $values['format'];
}
}
$this->needsParsing = $entity
->isNew() || $this->uri !== $original_uri || empty($size) || empty($format);
if ($this->needsParsing) {
if ($this
->needsQueue()) {
$this
->writePropertyValue('size', NULL);
$this
->writePropertyValue('format', NULL);
return;
}
$options = [
'exceptions' => FALSE,
'allow_redirects' => [
'strict' => TRUE,
],
];
$url = Url::fromUri($this->uri, [
'absolute' => TRUE,
])
->toString();
$this
->clearResponse();
$this
->clearException();
try {
$this
->setResponse($this
->getHttpClient()
->head($url, $options));
} catch (RequestException $request_exception) {
$this
->setException($request_exception);
}
$format = NULL;
$size = 0;
if (!$this
->getException() && ($response = $this
->getResponse()) && $this
->isSupportedResponse($response)) {
if ($response
->hasHeader('Content-Type')) {
$format = explode(';', $response
->getHeaderLine('Content-Type'))[0];
}
else {
$format = NULL;
}
if ($response
->hasHeader('Content-Length')) {
$size = (int) $response
->getHeaderLine('Content-Length');
}
else {
$response = $this
->getHttpClient()
->get($url, $options);
$size = (int) $response
->getBody()
->getSize();
$this
->setResponse($response);
}
$this
->writePropertyValue('size', $size);
$this
->writePropertyValue('format', $format);
}
}
}
public function postSave($update) {
if ($this->needsParsing && $this
->needsQueue()) {
$entity = $this
->getEntity();
$rev = $entity instanceof RevisionableInterface ? $entity
->getRevisionId() : NULL;
$item = new FileLinkQueueItem($entity
->getEntityTypeId(), $entity
->id(), $entity
->language()
->getId(), $rev);
if (!in_array($item
->getKey(), static::$queued)) {
$this
->getQueue()
->createItem($item);
static::$queued[] = $item
->getKey();
}
}
return parent::postSave($update);
}
public function getSize() {
return $this
->get('size')
->getValue();
}
public function getFormat() {
return $this
->get('format')
->getValue();
}
public function setResponse(ResponseInterface $response) {
$this->response = $response;
return $this;
}
public function getResponse() {
return $this->response;
}
public function clearResponse() {
$this->response = NULL;
return $this;
}
public function setException(RequestException $exception) {
$this->exception = $exception;
return $this;
}
public function getException() {
return $this->exception;
}
public function clearException() {
$this->exception = NULL;
return $this;
}
protected function getEntityTypeManager() {
if (!isset($this->entityTypeManager)) {
$this->entityTypeManager = \Drupal::entityTypeManager();
}
return $this->entityTypeManager;
}
protected function getHttpClient() {
if (!isset($this->httpClient)) {
$this->httpClient = \Drupal::httpClient();
}
return $this->httpClient;
}
protected function getQueue() : QueueInterface {
if (!isset($this->queue)) {
$this->queue = \Drupal::queue('file_link_metadata_update');
$this->queue
->createQueue();
}
return $this->queue;
}
protected function needsQueue() : bool {
return !FileLinkMetadataUpdate::isProcessing() && $this
->getFieldDefinition()
->getSetting('deferred_request');
}
protected function isSupportedResponse(ResponseInterface $response) {
return in_array($response
->getStatusCode(), [
'200',
'301',
'302',
]);
}
}