class FileLinkItem in File Link 2.0.x
Same name and namespace in other branches
- 8 src/Plugin/Field/FieldType/FileLinkItem.php \Drupal\file_link\Plugin\Field\FieldType\FileLinkItem
Implements a 'file_link' plugin field type.
This field type is an extension of 'link' filed-type that points only to files, not to directories and, additionally, stores some meta-data related to targeted file, like size and mime-type.
Plugin annotation
@FieldType(
id = "file_link",
label = @Translation("File Link"),
description = @Translation("Stores a URL string pointing to a file, optional varchar link text, and file metadata, like size and mime-type."),
default_widget = "file_link_default",
default_formatter = "link",
constraints = {
"LinkAccess" = {},
"LinkToFile" = {},
"LinkExternalProtocols" = {},
"LinkNotExistingInternal" = {}
}
)
Hierarchy
- class \Drupal\Core\TypedData\TypedData implements PluginInspectionInterface, TypedDataInterface uses DependencySerializationTrait, StringTranslationTrait, TypedDataTrait
- class \Drupal\Core\TypedData\Plugin\DataType\Map implements \Drupal\Core\TypedData\Plugin\DataType\IteratorAggregate, ComplexDataInterface
- class \Drupal\Core\Field\FieldItemBase implements FieldItemInterface
- class \Drupal\link\Plugin\Field\FieldType\LinkItem implements LinkItemInterface
- class \Drupal\file_link\Plugin\Field\FieldType\FileLinkItem implements FileLinkInterface
- class \Drupal\link\Plugin\Field\FieldType\LinkItem implements LinkItemInterface
- class \Drupal\Core\Field\FieldItemBase implements FieldItemInterface
- class \Drupal\Core\TypedData\Plugin\DataType\Map implements \Drupal\Core\TypedData\Plugin\DataType\IteratorAggregate, ComplexDataInterface
Expanded class hierarchy of FileLinkItem
2 files declare their use of FileLinkItem
- FileLinkDeferredTest.php in tests/
src/ Kernel/ FileLinkDeferredTest.php - LinkToFileConstraint.php in src/
Plugin/ Validation/ Constraint/ LinkToFileConstraint.php
File
- src/
Plugin/ Field/ FieldType/ FileLinkItem.php, line 42
Namespace
Drupal\file_link\Plugin\Field\FieldTypeView source
class FileLinkItem extends LinkItem implements FileLinkInterface {
/**
* The HTTP response of the last client request, if any.
*
* @var \Psr\Http\Message\ResponseInterface
*/
protected $response = NULL;
/**
* The exception throw by the the last HTTP client request, if any.
*
* @var \GuzzleHttp\Exception\RequestException
*/
protected $exception = NULL;
/**
* The entity type manager service.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;
/**
* The HTTP client service.
*
* @var \GuzzleHttp\Client
*/
protected $httpClient;
/**
* The queue to use when deferring the request to get the metadata.
*
* @var \Drupal\Core\Queue\QueueInterface
*/
protected $queue;
/**
* Save the needs parsing state as a property.
*
* @var bool
*/
protected $needsParsing = FALSE;
/**
* Keep the already queued entities in a static cache.
*
* @var array
*/
protected static $queued = [];
/**
* {@inheritdoc}
*/
public static function defaultFieldSettings() {
return [
'file_extensions' => 'txt',
'no_extension' => FALSE,
'deferred_request' => FALSE,
] + parent::defaultFieldSettings();
}
/**
* {@inheritdoc}
*/
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')),
];
}
/**
* {@inheritdoc}
*/
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;
}
/**
* {@inheritdoc}
*/
public function fieldSettingsForm(array $form, FormStateInterface $form_state) {
$element = parent::fieldSettingsForm($form, $form_state);
// Make the extension list a little more human-friendly by comma-separation.
$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.'),
// Use the 'file' field type validator.
'#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;
}
/**
* {@inheritdoc}
*/
public static function generateSampleValue(FieldDefinitionInterface $field_definition) {
$values = parent::generateSampleValue($field_definition);
$values['size'] = 1234567;
$values['format'] = 'image/png';
return $values;
}
/**
* {@inheritdoc}
*/
public function preSave() {
parent::preSave();
// Skip performing HTTP requests, useful when running bulk imports.
if (Settings::get('file_link.disable_http_requests', FALSE)) {
return;
}
$entity = $this
->getEntity();
$storage = $this
->getEntityTypeManager()
->getStorage($entity
->getEntityTypeId());
/** @var \Drupal\Core\Entity\ContentEntityInterface $original */
$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'];
}
}
// We parse the metadata in any of the next cases:
// - The host entity is new.
// - The 'file_link' URI has changed.
// - Stored metadata is empty, possible due to an previous failure. We try
// again to parse, hoping the connection was fixed in the meantime.
$this->needsParsing = $entity
->isNew() || $this->uri !== $original_uri || empty($size) || empty($format);
if ($this->needsParsing) {
if ($this
->needsQueue()) {
// We set the needs_parsing property and check it in ::postSave.
// Now just reset the values, cron will set them later.
$this
->writePropertyValue('size', NULL);
$this
->writePropertyValue('format', NULL);
return;
}
// Don't throw exceptions on HTTP level errors (e.g. 404, 403, etc).
$options = [
'exceptions' => FALSE,
'allow_redirects' => [
'strict' => TRUE,
],
];
$url = Url::fromUri($this->uri, [
'absolute' => TRUE,
])
->toString();
// Clear any previous stored results (response and/or exception).
$this
->clearResponse();
$this
->clearException();
try {
// Perform only a HEAD method to save bandwidth.
$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')) {
// The format may have the pattern 'text/html; charset=UTF-8'. In this
// case, keep only the first relevant part.
$format = explode(';', $response
->getHeaderLine('Content-Type'))[0];
}
else {
$format = NULL;
}
if ($response
->hasHeader('Content-Length')) {
$size = (int) $response
->getHeaderLine('Content-Length');
}
else {
// The server didn't sent the Content-Length header. In this case,
// perform a full GET and measure the size of the returned body.
$response = $this
->getHttpClient()
->get($url, $options);
$size = (int) $response
->getBody()
->getSize();
$this
->setResponse($response);
}
$this
->writePropertyValue('size', $size);
$this
->writePropertyValue('format', $format);
}
}
}
/**
* {@inheritdoc}
*/
public function postSave($update) {
if ($this->needsParsing && $this
->needsQueue()) {
// We need to queue the entity update in postSave because we need to
// know the entity id so that we can load it in cron and re-save it.
$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);
// Save the queued entity in a static cache so that we don't queue it
// more than once when using a multi-value field.
static::$queued[] = $item
->getKey();
}
}
return parent::postSave($update);
}
/**
* {@inheritdoc}
*/
public function getSize() {
return $this
->get('size')
->getValue();
}
/**
* {@inheritdoc}
*/
public function getFormat() {
return $this
->get('format')
->getValue();
}
/**
* {@inheritdoc}
*/
public function setResponse(ResponseInterface $response) {
$this->response = $response;
return $this;
}
/**
* {@inheritdoc}
*/
public function getResponse() {
return $this->response;
}
/**
* {@inheritdoc}
*/
public function clearResponse() {
$this->response = NULL;
return $this;
}
/**
* {@inheritdoc}
*/
public function setException(RequestException $exception) {
$this->exception = $exception;
return $this;
}
/**
* {@inheritdoc}
*/
public function getException() {
return $this->exception;
}
/**
* {@inheritdoc}
*/
public function clearException() {
$this->exception = NULL;
return $this;
}
/**
* Returns the entity type manager service.
*
* @return \Drupal\Core\Entity\EntityTypeManagerInterface
* The entity type manager service.
*/
protected function getEntityTypeManager() {
if (!isset($this->entityTypeManager)) {
$this->entityTypeManager = \Drupal::entityTypeManager();
}
return $this->entityTypeManager;
}
/**
* Returns the HTTP client service.
*
* @return \GuzzleHttp\Client
* The Guzzle client.
*/
protected function getHttpClient() {
if (!isset($this->httpClient)) {
$this->httpClient = \Drupal::httpClient();
}
return $this->httpClient;
}
/**
* Returns the queue.
*
* @return \Drupal\Core\Queue\QueueInterface
* The queue for deferred entity updates.
*/
protected function getQueue() : QueueInterface {
if (!isset($this->queue)) {
$this->queue = \Drupal::queue('file_link_metadata_update');
$this->queue
->createQueue();
}
return $this->queue;
}
/**
* Check to see if a queue is needed.
*
* @return bool
* True if the field is set up to defer requests and cron is not processing.
*/
protected function needsQueue() : bool {
return !FileLinkMetadataUpdate::isProcessing() && $this
->getFieldDefinition()
->getSetting('deferred_request');
}
/**
* Check whereas given response is supported by field type.
*
* @param \Psr\Http\Message\ResponseInterface $response
* Response object.
*
* @return bool
* TRUE if supported, FALSE otherwise.
*/
protected function isSupportedResponse(ResponseInterface $response) {
return in_array($response
->getStatusCode(), [
'200',
'301',
'302',
]);
}
}
Members
Name![]() |
Modifiers | Type | Description | Overrides |
---|---|---|---|---|
DependencySerializationTrait:: |
protected | property | ||
DependencySerializationTrait:: |
protected | property | ||
DependencySerializationTrait:: |
public | function | 2 | |
DependencySerializationTrait:: |
public | function | 2 | |
FieldItemBase:: |
public static | function |
Calculates dependencies for field items. Overrides FieldItemInterface:: |
2 |
FieldItemBase:: |
public static | function |
Calculates dependencies for field items on the storage level. Overrides FieldItemInterface:: |
1 |
FieldItemBase:: |
public static | function |
Defines the storage-level settings for this plugin. Overrides FieldItemInterface:: |
10 |
FieldItemBase:: |
public | function |
Defines custom delete behavior for field values. Overrides FieldItemInterface:: |
2 |
FieldItemBase:: |
public | function |
Defines custom revision delete behavior for field values. Overrides FieldItemInterface:: |
|
FieldItemBase:: |
public static | function |
Returns a settings array in the field type's canonical representation. Overrides FieldItemInterface:: |
1 |
FieldItemBase:: |
public static | function |
Returns a settings array that can be stored as a configuration value. Overrides FieldItemInterface:: |
1 |
FieldItemBase:: |
public | function |
Gets the entity that field belongs to. Overrides FieldItemInterface:: |
|
FieldItemBase:: |
public | function |
Gets the field definition. Overrides FieldItemInterface:: |
|
FieldItemBase:: |
public | function |
Gets the langcode of the field values held in the object. Overrides FieldItemInterface:: |
|
FieldItemBase:: |
protected | function | Returns the value of a field setting. | |
FieldItemBase:: |
protected | function | Returns the array of field settings. | |
FieldItemBase:: |
public static | function |
Informs the plugin that a dependency of the field will be deleted. Overrides FieldItemInterface:: |
1 |
FieldItemBase:: |
public | function |
Returns a form for the storage-level settings. Overrides FieldItemInterface:: |
8 |
FieldItemBase:: |
public static | function |
Returns a settings array in the field type's canonical representation. Overrides FieldItemInterface:: |
2 |
FieldItemBase:: |
public static | function |
Returns a settings array that can be stored as a configuration value. Overrides FieldItemInterface:: |
2 |
FieldItemBase:: |
public | function |
Returns a renderable array for a single field item. Overrides FieldItemInterface:: |
|
FieldItemBase:: |
protected | function |
Different to the parent Map class, we avoid creating property objects as
far as possible in order to optimize performance. Thus we just update
$this->values if no property object has been created yet. Overrides Map:: |
|
FieldItemBase:: |
public | function |
Constructs a TypedData object given its definition and context. Overrides TypedData:: |
1 |
FieldItemBase:: |
public | function |
Magic method: Gets a property value. Overrides FieldItemInterface:: |
2 |
FieldItemBase:: |
public | function |
Magic method: Determines whether a property is set. Overrides FieldItemInterface:: |
|
FieldItemBase:: |
public | function |
Magic method: Sets a property value. Overrides FieldItemInterface:: |
1 |
FieldItemBase:: |
public | function |
Magic method: Unsets a property. Overrides FieldItemInterface:: |
|
FileLinkItem:: |
protected | property | The entity type manager service. | |
FileLinkItem:: |
protected | property | The exception throw by the the last HTTP client request, if any. | |
FileLinkItem:: |
protected | property | The HTTP client service. | |
FileLinkItem:: |
protected | property | Save the needs parsing state as a property. | |
FileLinkItem:: |
protected | property | The queue to use when deferring the request to get the metadata. | |
FileLinkItem:: |
protected static | property | Keep the already queued entities in a static cache. | |
FileLinkItem:: |
protected | property | The HTTP response of the last client request, if any. | |
FileLinkItem:: |
public | function |
Clears a previous stored Guzzle exception. Overrides FileLinkInterface:: |
|
FileLinkItem:: |
public | function |
Clears a previous stored HTTP response. Overrides FileLinkInterface:: |
|
FileLinkItem:: |
public static | function |
Defines the field-level settings for this plugin. Overrides LinkItem:: |
|
FileLinkItem:: |
public | function |
Returns a form for the field-level settings. Overrides LinkItem:: |
|
FileLinkItem:: |
public static | function |
Generates placeholder field values. Overrides LinkItem:: |
|
FileLinkItem:: |
protected | function | Returns the entity type manager service. | |
FileLinkItem:: |
public | function |
Gets the last Guzzle client exception. Overrides FileLinkInterface:: |
|
FileLinkItem:: |
public | function |
Get file format. Overrides FileLinkInterface:: |
|
FileLinkItem:: |
protected | function | Returns the HTTP client service. | |
FileLinkItem:: |
protected | function | Returns the queue. | |
FileLinkItem:: |
public | function |
Gets the latest stored HTTP response. Overrides FileLinkInterface:: |
|
FileLinkItem:: |
public | function |
Get raw file size. Overrides FileLinkInterface:: |
|
FileLinkItem:: |
protected | function | Check whereas given response is supported by field type. | |
FileLinkItem:: |
protected | function | Check to see if a queue is needed. | |
FileLinkItem:: |
public | function |
Defines custom post-save behavior for field values. Overrides FieldItemBase:: |
|
FileLinkItem:: |
public | function |
Defines custom presave behavior for field values. Overrides FieldItemBase:: |
|
FileLinkItem:: |
public static | function |
Defines field item properties. Overrides LinkItem:: |
|
FileLinkItem:: |
public static | function |
Returns the schema for the field. Overrides LinkItem:: |
|
FileLinkItem:: |
public | function |
Sets the exception throw by the last HTTP client request. Overrides FileLinkInterface:: |
|
FileLinkItem:: |
public | function |
Sets the latest HTTP response. Overrides FileLinkInterface:: |
|
LinkItem:: |
public | function |
Gets the URL object. Overrides LinkItemInterface:: |
|
LinkItem:: |
public | function |
Determines whether the data structure is empty. Overrides Map:: |
|
LinkItem:: |
public | function |
Determines if a link is external. Overrides LinkItemInterface:: |
|
LinkItem:: |
public static | function |
Returns the name of the main property, if any. Overrides FieldItemBase:: |
|
LinkItem:: |
public | function |
Sets the data value. Overrides FieldItemBase:: |
|
LinkItemInterface:: |
constant | Specifies whether the field supports only external URLs. | ||
LinkItemInterface:: |
constant | Specifies whether the field supports both internal and external URLs. | ||
LinkItemInterface:: |
constant | Specifies whether the field supports only internal URLs. | ||
Map:: |
protected | property |
The data definition. Overrides TypedData:: |
|
Map:: |
protected | property | The array of properties. | |
Map:: |
protected | property | An array of values for the contained properties. | |
Map:: |
public | function |
Applies the default value. Overrides TypedData:: |
4 |
Map:: |
public | function |
Gets a property object. Overrides ComplexDataInterface:: |
|
Map:: |
public | function | ||
Map:: |
public | function |
Gets an array of property objects. Overrides ComplexDataInterface:: |
|
Map:: |
public | function |
Returns a string representation of the data. Overrides TypedData:: |
|
Map:: |
public | function |
Gets the data value. Overrides TypedData:: |
1 |
Map:: |
public | function |
Overrides TraversableTypedDataInterface:: |
4 |
Map:: |
public | function |
Sets a property value. Overrides ComplexDataInterface:: |
|
Map:: |
public | function |
Returns an array of all property values. Overrides ComplexDataInterface:: |
1 |
Map:: |
public | function | Magic method: Implements a deep clone. | |
StringTranslationTrait:: |
protected | property | The string translation service. | 4 |
StringTranslationTrait:: |
protected | function | Formats a string containing a count of items. | |
StringTranslationTrait:: |
protected | function | Returns the number of plurals supported by a given language. | |
StringTranslationTrait:: |
protected | function | Gets the string translation service. | |
StringTranslationTrait:: |
public | function | Sets the string translation service to use. | 2 |
StringTranslationTrait:: |
protected | function | Translates a string to the current language or to a given language. | |
TypedData:: |
protected | property | The property name. | |
TypedData:: |
protected | property | The parent typed data object. | |
TypedData:: |
public static | function |
Constructs a TypedData object given its definition and context. Overrides TypedDataInterface:: |
|
TypedData:: |
public | function |
Gets a list of validation constraints. Overrides TypedDataInterface:: |
9 |
TypedData:: |
public | function |
Gets the data definition. Overrides TypedDataInterface:: |
|
TypedData:: |
public | function |
Returns the name of a property or item. Overrides TypedDataInterface:: |
|
TypedData:: |
public | function |
Returns the parent data structure; i.e. either complex data or a list. Overrides TypedDataInterface:: |
|
TypedData:: |
public | function |
Gets the definition of the plugin implementation. Overrides PluginInspectionInterface:: |
|
TypedData:: |
public | function |
Gets the plugin_id of the plugin instance. Overrides PluginInspectionInterface:: |
|
TypedData:: |
public | function |
Returns the property path of the data. Overrides TypedDataInterface:: |
|
TypedData:: |
public | function |
Returns the root of the typed data tree. Overrides TypedDataInterface:: |
|
TypedData:: |
public | function |
Sets the context of a property or item via a context aware parent. Overrides TypedDataInterface:: |
|
TypedData:: |
public | function |
Validates the currently set data value. Overrides TypedDataInterface:: |
|
TypedDataTrait:: |
protected | property | The typed data manager used for creating the data types. | |
TypedDataTrait:: |
public | function | Gets the typed data manager. | 2 |
TypedDataTrait:: |
public | function | Sets the typed data manager. | 2 |