clean install

This commit is contained in:
2024-12-10 15:08:16 +01:00
commit e14eb2d8fd
31193 changed files with 3555714 additions and 0 deletions

View File

@@ -0,0 +1,19 @@
# Schema for hideable_boolean field formatter.
field.formatter.settings.hideable_boolean:
type: mapping
mapping:
format:
type: string
label: 'Output format'
format_custom_false:
type: label
label: 'Custom output for FALSE'
format_custom_true:
type: label
label: 'Custom output for TRUE'
hide_if_false:
type: boolean
label: 'Hide if false'
hide_if_true:
type: boolean
label: 'Hide if true'

View File

@@ -0,0 +1,11 @@
name: farmOS Field
description: Provides a service for generating farmOS field definitions.
type: module
package: farmOS
core_version_requirement: ^10
dependencies:
- drupal:field
- entity:entity
- farm:farm_map
- fraction:fraction
- inline_entity_form:inline_entity_form

View File

@@ -0,0 +1,3 @@
services:
farm_field.factory:
class: Drupal\farm_field\FarmFieldFactory

View File

@@ -0,0 +1,982 @@
<?php
namespace Drupal\farm_field;
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\Core\Field\FieldException;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\entity\BundleFieldDefinition;
use Drupal\inline_entity_form\Plugin\Field\FieldWidget\InlineEntityFormComplex;
/**
* Factory for generating farmOS field definitions.
*/
class FarmFieldFactory implements FarmFieldFactoryInterface {
/**
* Generate a base field definition.
*
* @param array $options
* An array of options.
*
* @return \Drupal\Core\Field\BaseFieldDefinition
* Returns a base field definition.
*/
public function baseFieldDefinition(array $options = []): BaseFieldDefinition {
$field = BaseFieldDefinition::create($options['type']);
$this->buildFieldDefinition($field, $options);
return $field;
}
/**
* Generates a bundle field definition.
*
* @param array $options
* An array of options.
*
* @return \Drupal\entity\BundleFieldDefinition
* Returns a bundle field definition.
*/
public function bundleFieldDefinition(array $options = []): BundleFieldDefinition {
$field = BundleFieldDefinition::create($options['type']);
$this->buildFieldDefinition($field, $options);
return $field;
}
/**
* Builds a field definition with farmOS opinions.
*
* @param \Drupal\Core\Field\BaseFieldDefinition &$field
* A base field definition object.
* @param array $options
* An array of options.
*/
protected function buildFieldDefinition(BaseFieldDefinition &$field, array $options = []) {
// Set label.
if (!empty($options['label'])) {
$field->setLabel($options['label']);
}
// Set description.
if (!empty($options['description'])) {
$field->setDescription($options['description']);
}
// Set computed.
if (!empty($options['computed'])) {
$field->setComputed(TRUE);
$field->setClass($options['computed']);
}
// Make the field required, if specified.
if (!empty($options['required'])) {
$field->setRequired(TRUE);
}
// Make the field revisionable, unless told otherwise.
if (empty($options['revisionable'])) {
$field->setRevisionable(TRUE);
}
else {
$field->setRevisionable(FALSE);
}
// Set cardinality, if specified.
if (!empty($options['cardinality'])) {
$field->setCardinality($options['cardinality']);
}
// Or, if `multiple` is set, set it to unlimited.
elseif (!empty($options['multiple'])) {
$field->setCardinality(FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED);
}
// Otherwise, set cardinality to 1.
else {
$field->setCardinality(1);
}
// Only make the field translatable if specified.
if (empty($options['translatable'])) {
$field->setTranslatable(FALSE);
}
else {
$field->setTranslatable(TRUE);
}
// Set the default value callback, if specified.
if (!empty($options['default_value_callback'])) {
$field->setDefaultValueCallback($options['default_value_callback']);
}
// Delegate to per-type helper functions to fill in more details.
switch ($options['type']) {
case 'boolean':
$this->modifyBooleanField($field, $options);
break;
case 'decimal':
$this->modifyDecimalField($field, $options);
break;
case 'email':
$this->modifyEmailField($field, $options);
break;
case 'entity_reference':
$this->modifyEntityReferenceField($field, $options);
break;
case 'entity_reference_revisions':
$this->modifyEntityReferenceRevisionsField($field, $options);
break;
case 'file':
case 'image':
$this->modifyFileField($field, $options);
break;
case 'fraction':
$this->modifyFractionField($field, $options);
break;
case 'geofield':
$this->modifyGeofieldField($field, $options);
break;
case 'id_tag':
$this->modifyIdTagField($field, $options);
break;
case 'integer':
$this->modifyIntegerField($field, $options);
break;
case 'inventory':
$this->modifyInventoryField($field, $options);
break;
case 'list_string':
$this->modifyListStringField($field, $options);
break;
case 'string':
case 'string_long':
$this->modifyStringField($field, $options);
break;
case 'text_long':
$this->modifyTextField($field, $options);
break;
case 'timestamp':
$this->modifyTimestampField($field, $options);
break;
case 'uri':
$this->modifyUriField($field, $options);
break;
default:
throw new FieldException('Unsupported field type.');
}
// Hide the field in form and view displays, if specified.
// The hidden option can either be set to TRUE, which will hide it in both
// form and view displays, or it can be set to "form" or "view", which will
// only hide it in the form or view display.
if (!empty($options['hidden'])) {
$display_options = [
'region' => 'hidden',
];
if ($options['hidden'] === TRUE || $options['hidden'] === 'form') {
$field->setDisplayOptions('form', $display_options);
}
if ($options['hidden'] === TRUE || $options['hidden'] === 'view') {
$field->setDisplayOptions('view', $display_options);
}
}
// Make the form and view displays configurable.
$field->setDisplayConfigurable('form', TRUE);
$field->setDisplayConfigurable('view', TRUE);
// Override form and view display options, if specified.
foreach (['form', 'view'] as $display_type) {
$key = $display_type . '_display_options';
if (isset($options[$key])) {
$field->setDisplayOptions($display_type, $options[$key]);
}
}
}
/**
* Boolean field modifier.
*
* @param \Drupal\Core\Field\BaseFieldDefinition &$field
* A base field definition object.
* @param array $options
* An array of options.
*/
protected function modifyBooleanField(BaseFieldDefinition &$field, array $options = []) {
// Set the on/off labels.
$field->setSetting('on_label', 'Yes');
$field->setSetting('off_label', 'No');
// Build form and view display settings.
$field->setDisplayOptions('form', [
'type' => 'boolean_checkbox',
'settings' => [
'display_label' => TRUE,
],
'weight' => $options['weight']['form'] ?? 0,
]);
$field->setDisplayOptions('view', [
'label' => 'inline',
'type' => 'boolean',
'settings' => [
'format' => 'default',
'format_custom_false' => '',
'format_custom_true' => '',
],
'weight' => $options['weight']['view'] ?? 0,
]);
}
/**
* Decimal field modifier.
*
* @param \Drupal\Core\Field\BaseFieldDefinition &$field
* A base field definition object.
* @param array $options
* An array of options.
*/
protected function modifyDecimalField(BaseFieldDefinition &$field, array $options = []) {
// Set the precision and scale, if specified.
if (!empty($options['precision'])) {
$field->setSetting('precision', $options['precision']);
}
if (!empty($options['scale'])) {
$field->setSetting('scale', $options['scale']);
}
// Set the min/max constraints, if specified.
if (isset($options['min'])) {
$field->setSetting('min', $options['min']);
}
if (isset($options['max'])) {
$field->setSetting('max', $options['max']);
}
// Build form and view display settings.
$field->setDisplayOptions('form', [
'type' => 'number',
'weight' => $options['weight']['form'] ?? 0,
]);
$view_display_options = [
'label' => 'inline',
'type' => 'number_decimal',
'weight' => $options['weight']['view'] ?? 0,
];
if (!empty($options['scale'])) {
$view_display_options['settings']['scale'] = $options['scale'];
}
$field->setDisplayOptions('view', $view_display_options);
}
/**
* Email field modifier.
*
* @param \Drupal\Core\Field\BaseFieldDefinition &$field
* A base field definition object.
* @param array $options
* An array of options.
*/
protected function modifyEmailField(BaseFieldDefinition &$field, array $options = []) {
// Build form and view display settings.
$field->setDisplayOptions('form', [
'type' => 'email_default',
'weight' => $options['weight']['form'] ?? 0,
]);
$field->setDisplayOptions('view', [
'label' => 'inline',
'type' => 'email_mailto',
'weight' => $options['weight']['view'] ?? 0,
]);
}
/**
* Entity reference field modifier.
*
* @param \Drupal\Core\Field\BaseFieldDefinition &$field
* A base field definition object.
* @param array $options
* An array of options.
*/
protected function modifyEntityReferenceField(BaseFieldDefinition &$field, array $options = []) {
// If a target type is not specified, throw an exception.
if (empty($options['target_type'])) {
throw new FieldException('No target_type was specified.');
}
// Set the target type.
$field->setSetting('target_type', $options['target_type']);
// Build additional settings based on the target type.
switch ($options['target_type']) {
// Asset reference.
case 'asset':
if (!empty($options['target_bundle'])) {
$handler = 'default:asset';
$handler_settings = [
'target_bundles' => [
$options['target_bundle'] => $options['target_bundle'],
],
'sort' => [
'field' => '_none',
],
'auto_create' => FALSE,
'auto_create_bundle' => '',
];
}
else {
$handler = 'views';
$handler_settings = [
'view' => [
'view_name' => 'farm_asset_reference',
'display_name' => 'entity_reference',
],
];
}
$form_display_options = [
'type' => 'entity_reference_autocomplete',
'weight' => $options['weight']['form'] ?? 0,
'settings' => [
'match_operator' => 'CONTAINS',
'match_limit' => '10',
'size' => '60',
'placeholder' => '',
],
];
$view_display_options = [
'label' => 'inline',
'type' => 'entity_reference_label',
'weight' => $options['weight']['view'] ?? 0,
'settings' => [
'link' => TRUE,
],
];
break;
// Log.
case 'log':
$handler = 'default:log';
$handler_settings = [
'target_bundles' => NULL,
'sort' => [
'field' => 'name',
'direction' => 'asc',
],
'auto_create' => FALSE,
'auto_create_bundle' => '',
];
$form_display_options = [
'type' => 'entity_reference_autocomplete',
'weight' => $options['weight']['form'] ?? 0,
];
$view_display_options = [
'label' => 'inline',
'type' => 'entity_reference_label',
'weight' => $options['weight']['view'] ?? 0,
'settings' => [
'link' => FALSE,
],
];
break;
// Term reference.
case 'taxonomy_term':
$handler = 'default:taxonomy_term';
$handler_settings = [
'target_bundles' => [
$options['target_bundle'] => $options['target_bundle'],
],
'sort' => [
'field' => 'name',
'direction' => 'asc',
],
'auto_create' => FALSE,
'auto_create_bundle' => '',
];
// Auto create term reference if auto_create is enabled.
if (!empty($options['auto_create'])) {
$handler_settings['auto_create'] = TRUE;
$handler_settings['auto_create_bundle'] = $options['target_bundle'];
}
$form_display_options = [
'type' => 'entity_reference_autocomplete',
'weight' => $options['weight']['form'] ?? 0,
];
$view_display_options = [
'label' => 'inline',
'type' => 'entity_reference_label',
'weight' => $options['weight']['view'] ?? 0,
'settings' => [
'link' => TRUE,
],
];
break;
// User reference.
case 'user':
$handler = 'default:user';
$handler_settings = [
'include_anonymous' => FALSE,
'filter' => [
'type' => '_none',
],
'target_bundles' => NULL,
'sort' => [
'field' => '_none',
],
'auto_create' => FALSE,
];
$form_display_options = [
'type' => 'options_select',
'weight' => $options['weight']['form'] ?? 0,
];
$view_display_options = [
'label' => 'inline',
'type' => 'entity_reference_label',
'weight' => $options['weight']['view'] ?? 0,
'settings' => [
'link' => TRUE,
],
];
break;
// Data stream reference.
case 'data_stream':
$handler = 'default:data_stream';
$handler_settings = [
'filter' => [
'type' => '_none',
],
'target_bundles' => NULL,
'sort' => [
'field' => '_none',
],
'auto_create' => FALSE,
];
$form_display_options = [
'type' => 'inline_entity_form_complex',
'settings' => [
'form_mode' => 'default',
'revision' => TRUE,
'override_labels' => FALSE,
'label_singular' => '',
'label_plural' => '',
'collapsible' => FALSE,
'collapsed' => FALSE,
'allow_new' => TRUE,
'allow_existing' => FALSE,
'match_operator' => 'CONTAINS',
'allow_duplicate' => FALSE,
'removed_reference' => InlineEntityFormComplex::REMOVED_DELETE,
],
'weight' => $options['weight']['form'] ?? 0,
];
$view_display_options = [
'label' => 'inline',
'type' => 'entity_reference_label',
'weight' => $options['weight']['view'] ?? 0,
'settings' => [
'link' => TRUE,
],
];
break;
// Otherwise, throw an exception.
default:
throw new FieldException('Unsupported target_type.');
}
// Set the handler and handler settings.
$field->setSetting('handler', $handler);
$field->setSetting('handler_settings', $handler_settings);
// Set form and view display options.
$field->setDisplayOptions('form', $form_display_options);
$field->setDisplayOptions('view', $view_display_options);
}
/**
* Entity reference revisions field modifier.
*
* @param \Drupal\Core\Field\BaseFieldDefinition &$field
* A base field definition object.
* @param array $options
* An array of options.
*/
protected function modifyEntityReferenceRevisionsField(BaseFieldDefinition &$field, array $options = []) {
// If a target type is not specified, throw an exception.
if (empty($options['target_type'])) {
throw new FieldException('No target_type was specified.');
}
// Set the target type.
$field->setSetting('target_type', $options['target_type']);
// Build additional settings based on the target type.
switch ($options['target_type']) {
// Quantity reference.
case 'quantity':
$handler = 'default:quantity';
$handler_settings = [
'target_bundles' => NULL,
'sort' => [
'field' => 'label',
'direction' => 'asc',
],
'auto_create' => FALSE,
'auto_create_bundle' => '',
];
$form_display_options = [
'type' => 'inline_entity_form_complex',
'settings' => [
'form_mode' => 'default',
'revision' => TRUE,
'override_labels' => FALSE,
'label_singular' => '',
'label_plural' => '',
'collapsible' => FALSE,
'collapsed' => FALSE,
'allow_new' => TRUE,
'allow_existing' => FALSE,
'match_operator' => 'CONTAINS',
'allow_duplicate' => FALSE,
'removed_reference' => InlineEntityFormComplex::REMOVED_KEEP,
],
'weight' => $options['weight']['form'] ?? 0,
];
$view_display_options = [
'label' => 'inline',
'type' => 'entity_reference_revisions_entity_view',
'settings' => [
'view_mode' => 'default',
'link' => FALSE,
],
'weight' => $options['weight']['view'] ?? 0,
];
break;
// Otherwise, throw an exception.
default:
throw new FieldException('Unsupported target_type.');
}
// Set the handler and handler settings.
$field->setSetting('handler', $handler);
$field->setSetting('handler_settings', $handler_settings);
// Set form and view display options.
$field->setDisplayOptions('form', $form_display_options);
$field->setDisplayOptions('view', $view_display_options);
}
/**
* File and image field modifier.
*
* @param \Drupal\Core\Field\BaseFieldDefinition &$field
* A base field definition object.
* @param array $options
* An array of options.
*/
protected function modifyFileField(BaseFieldDefinition &$field, array $options = []) {
// Determine the upload directory.
$file_directory = 'farm/[date:custom:Y]-[date:custom:m]';
if (!empty($options['file_directory'])) {
$file_directory = $options['file_directory'];
}
// Set field settings.
$settings = [
'file_directory' => $file_directory,
'max_filesize' => '',
'handler' => 'default:file',
'handler_settings' => [],
];
switch ($options['type']) {
case 'file':
$settings['file_extensions'] = 'csv doc docx gz geojson gpx kml kmz logz mp3 odp ods odt ogg pdf ppt pptx tar tif tiff txt wav xls xlsx zip';
$settings['description_field'] = TRUE;
break;
case 'image':
$settings['file_extensions'] = 'png gif jpg jpeg';
$settings['max_resolution'] = '';
$settings['min_resolution'] = '';
$settings['alt_field'] = FALSE;
$settings['alt_field_required'] = FALSE;
$settings['title_field'] = FALSE;
$settings['title_field_required'] = FALSE;
$settings['default_image'] = [
'uuid' => '',
'alt' => '',
'title' => '',
'width' => NULL,
'height' => NULL,
];
break;
}
$field->setSettings($settings);
// Build form and view display settings.
switch ($options['type']) {
case 'file':
$form_display_options = [
'type' => 'file_generic',
'settings' => [
'progress_indicator' => 'throbber',
],
'weight' => $options['weight']['form'] ?? 0,
];
$view_display_options = [
'type' => 'file_table',
'label' => 'above',
'settings' => [
'use_description_as_link_text' => TRUE,
],
'weight' => $options['weight']['view'] ?? 0,
];
break;
case 'image':
$form_display_options = [
'type' => 'image_image',
'settings' => [
'preview_image_style' => 'medium',
'progress_indicator' => 'throbber',
],
'weight' => $options['weight']['form'] ?? 0,
];
$view_display_options = [
'type' => 'image',
'label' => 'above',
'settings' => [
'image_style' => 'large',
'image_link' => 'file',
],
'weight' => $options['weight']['view'] ?? 0,
];
break;
}
if (!empty($form_display_options)) {
$field->setDisplayOptions('form', $form_display_options);
}
if (!empty($view_display_options)) {
$field->setDisplayOptions('view', $view_display_options);
}
}
/**
* Fraction field modifier.
*
* @param \Drupal\Core\Field\BaseFieldDefinition &$field
* A base field definition object.
* @param array $options
* An array of options.
*/
protected function modifyFractionField(BaseFieldDefinition &$field, array $options = []) {
// Build form and view display settings.
$field->setDisplayOptions('form', [
'type' => 'fraction_decimal',
'weight' => $options['weight']['form'] ?? 0,
'settings' => [
'precision' => 0,
'auto_precision' => TRUE,
],
]);
$field->setDisplayOptions('view', [
'label' => 'inline',
'type' => 'fraction_decimal',
'settings' => [
'precision' => 0,
'auto_precision' => TRUE,
'separator' => '/',
'prefix_suffix' => FALSE,
],
'weight' => $options['weight']['view'] ?? 0,
]);
}
/**
* Geofield field modifier.
*
* @param \Drupal\Core\Field\BaseFieldDefinition &$field
* A base field definition object.
* @param array $options
* An array of options.
*/
protected function modifyGeofieldField(BaseFieldDefinition &$field, array $options = []) {
// Set the geofield backend.
$field->setSetting('backend', 'geofield_backend_default');
// Build form and view display settings.
$field->setDisplayOptions('form', [
'type' => 'farm_map_geofield',
'weight' => $options['weight']['form'] ?? 0,
'settings' => [
'populate_file_field' => $options['populate_file_field'] ?? FALSE,
],
]);
$field->setDisplayOptions('view', [
'label' => 'inline',
'type' => 'farm_map_geofield',
'settings' => [
'output_format' => 'wkt',
'output_escape' => TRUE,
],
'weight' => $options['weight']['view'] ?? 0,
]);
}
/**
* ID tag field modifier.
*
* @param \Drupal\Core\Field\BaseFieldDefinition &$field
* A base field definition object.
* @param array $options
* An array of options.
*/
protected function modifyIdTagField(BaseFieldDefinition &$field, array $options = []) {
// Build form and view display settings.
$field->setDisplayOptions('form', [
'type' => 'id_tag',
'weight' => $options['weight']['form'] ?? 0,
]);
$field->setDisplayOptions('view', [
'label' => 'inline',
'type' => 'id_tag',
'weight' => $options['weight']['view'] ?? 0,
]);
}
/**
* Integer field modifier.
*
* @param \Drupal\Core\Field\BaseFieldDefinition &$field
* A base field definition object.
* @param array $options
* An array of options.
*/
protected function modifyIntegerField(BaseFieldDefinition &$field, array $options = []) {
// Set the size, if specified.
if (!empty($options['size'])) {
$field->setSetting('size', $options['size']);
}
// Set the min/max constraints, if specified.
if (isset($options['min'])) {
$field->setSetting('min', $options['min']);
}
if (isset($options['max'])) {
$field->setSetting('max', $options['max']);
}
// Build form and view display settings.
$field->setDisplayOptions('form', [
'type' => 'number',
'weight' => $options['weight']['form'] ?? 0,
]);
$field->setDisplayOptions('view', [
'label' => 'inline',
'type' => 'number_integer',
'weight' => $options['weight']['view'] ?? 0,
]);
}
/**
* Inventory field modifier.
*
* @param \Drupal\Core\Field\BaseFieldDefinition &$field
* A base field definition object.
* @param array $options
* An array of options.
*/
protected function modifyInventoryField(BaseFieldDefinition &$field, array $options = []) {
// Build view display settings.
$field->setDisplayOptions('view', [
'label' => 'inline',
'type' => 'inventory',
'weight' => $options['weight']['view'] ?? 0,
]);
}
/**
* List string field modifier.
*
* @param \Drupal\Core\Field\BaseFieldDefinition &$field
* A base field definition object.
* @param array $options
* An array of options.
*/
protected function modifyListStringField(BaseFieldDefinition &$field, array $options = []) {
// Set the allowed values, if specified.
if (!empty($options['allowed_values'])) {
$field->setSetting('allowed_values', $options['allowed_values']);
}
// Set the allowed values function, if specified.
if (!empty($options['allowed_values_function'])) {
$field->setSetting('allowed_values_function', $options['allowed_values_function']);
}
// Build form and view display settings.
$field->setDisplayOptions('form', [
'type' => 'options_buttons',
'weight' => $options['weight']['form'] ?? 0,
]);
$field->setDisplayOptions('view', [
'label' => 'inline',
'type' => 'list_default',
'weight' => $options['weight']['view'] ?? 0,
]);
}
/**
* String field modifier.
*
* @param \Drupal\Core\Field\BaseFieldDefinition &$field
* A base field definition object.
* @param array $options
* An array of options.
*/
protected function modifyStringField(BaseFieldDefinition &$field, array $options = []) {
// Set the maximum length, if specified.
if (!empty($options['max_length'])) {
$field->setSetting('max_length', $options['max_length']);
}
// Build form and view display settings.
$field->setDisplayOptions('form', [
'type' => 'string_textfield',
'settings' => [
'size' => 60,
'placeholder' => '',
],
'weight' => $options['weight']['form'] ?? 0,
]);
$field->setDisplayOptions('view', [
'label' => 'inline',
'type' => 'string',
'weight' => $options['weight']['view'] ?? 0,
]);
}
/**
* Text field modifier.
*
* @param \Drupal\Core\Field\BaseFieldDefinition &$field
* A base field definition object.
* @param array $options
* An array of options.
*/
protected function modifyTextField(BaseFieldDefinition &$field, array $options = []) {
// Build form and view display settings.
$field->setDisplayOptions('form', [
'type' => 'text_textarea',
'settings' => [
'rows' => '5',
'placeholder' => '',
],
'weight' => $options['weight']['form'] ?? 0,
]);
$field->setDisplayOptions('view', [
'label' => 'inline',
'type' => 'text_default',
'weight' => $options['weight']['view'] ?? 0,
]);
}
/**
* Timestamp field modifier.
*
* @param \Drupal\Core\Field\BaseFieldDefinition &$field
* A base field definition object.
* @param array $options
* An array of options.
*/
protected function modifyTimestampField(BaseFieldDefinition &$field, array $options = []) {
// Build form and view display settings.
$field->setDisplayOptions('form', [
'type' => 'datetime_timestamp_optional',
'weight' => $options['weight']['form'] ?? 0,
]);
$field->setDisplayOptions('view', [
'label' => 'inline',
'type' => 'timestamp',
'settings' => [
'date_format' => 'medium',
'custom_date_format' => '',
'timezone' => '',
],
'weight' => $options['weight']['view'] ?? 0,
]);
}
/**
* URI field modifier.
*
* @param \Drupal\Core\Field\BaseFieldDefinition &$field
* A base field definition object.
* @param array $options
* An array of options.
*/
protected function modifyUriField(BaseFieldDefinition &$field, array $options = []) {
// Build form and view display settings.
$field->setDisplayOptions('form', [
'type' => 'uri_string',
'settings' => [
'size' => 60,
'placeholder' => '',
],
'weight' => $options['weight']['form'] ?? 0,
]);
$field->setDisplayOptions('view', [
'label' => 'inline',
'type' => 'url_link',
'weight' => $options['weight']['view'] ?? 0,
]);
// Add URI validation constraint.
$field->addConstraint('Uri');
}
}

View File

@@ -0,0 +1,35 @@
<?php
namespace Drupal\farm_field;
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\entity\BundleFieldDefinition;
/**
* Provides an interface for defining a farmOS field factory.
*/
interface FarmFieldFactoryInterface {
/**
* Generate a base field definition.
*
* @param array $options
* An array of options.
*
* @return \Drupal\Core\Field\BaseFieldDefinition
* Returns a base field definition.
*/
public function baseFieldDefinition(array $options = []): BaseFieldDefinition;
/**
* Generates a bundle field definition.
*
* @param array $options
* An array of options.
*
* @return \Drupal\entity\BundleFieldDefinition
* Returns a bundle field definition.
*/
public function bundleFieldDefinition(array $options = []): BundleFieldDefinition;
}

View File

@@ -0,0 +1,87 @@
<?php
namespace Drupal\farm_field\Plugin\Field\FieldFormatter;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Field\Plugin\Field\FieldFormatter\BooleanFormatter;
use Drupal\Core\Form\FormStateInterface;
/**
* Plugin implementation of the 'hideable_boolean' formatter.
*
* Extends the core BooleanFormatter to provide settings to conditionally hide
* a field.
*
* @FieldFormatter(
* id = "hideable_boolean",
* label = @Translation("Hideable Boolean"),
* field_types = {
* "boolean",
* }
* )
*/
class HideableBoolean extends BooleanFormatter {
/**
* {@inheritdoc}
*/
public static function defaultSettings() {
$settings = parent::defaultSettings();
$settings['hide_if_true'] = FALSE;
$settings['hide_if_false'] = FALSE;
return $settings;
}
/**
* {@inheritdoc}
*/
public function settingsForm(array $form, FormStateInterface $form_state) {
$form = parent::settingsForm($form, $form_state);
$form['hide_if_true'] = [
'#type' => 'checkbox',
'#title' => $this->t('Hide if TRUE'),
'#default_value' => $this->getSetting('hide_if_true'),
];
$form['hide_if_false'] = [
'#type' => 'checkbox',
'#title' => $this->t('Hide if FALSE'),
'#default_value' => $this->getSetting('hide_if_false'),
];
return $form;
}
/**
* {@inheritdoc}
*/
public function viewElements(FieldItemListInterface $items, $langcode) {
$elements = [];
$formats = $this->getOutputFormats();
foreach ($items as $delta => $item) {
$format = $this->getSetting('format');
$hide_if_true = (bool) $this->getSetting('hide_if_true');
$hide_if_false = (bool) $this->getSetting('hide_if_false');
// If the item should be hidden, skip it.
if ($item->value && $hide_if_true || !$item->value && $hide_if_false) {
continue;
}
if ($format == 'custom') {
$elements[$delta] = ['#markup' => $item->value ? $this->getSetting('format_custom_true') : $this->getSetting('format_custom_false')];
}
else {
$elements[$delta] = ['#markup' => $item->value ? $formats[$format][0] : $formats[$format][1]];
}
}
return $elements;
}
}

View File

@@ -0,0 +1,52 @@
<?php
namespace Drupal\farm_field\Plugin\Field\FieldFormatter;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Field\FormatterBase;
use Drupal\Core\Url;
/**
* Plugin implementation of the 'url_link' formatter.
*
* @FieldFormatter(
* id = "url_link",
* label = @Translation("Link to URL"),
* field_types = {
* "uri",
* }
* )
*/
class UrlLinkFormatter extends FormatterBase {
/**
* {@inheritdoc}
*/
public function viewElements(FieldItemListInterface $items, $langcode) {
$elements = [];
// This is a clone of Drupal core's UriLinkFormatter, but it only renders
// a link if the URI is a valid URL.
foreach ($items as $delta => $item) {
if (!$item->isEmpty()) {
if (filter_var($item->value, FILTER_VALIDATE_URL)) {
$element = [
'#type' => 'link',
'#url' => Url::fromUri($item->value),
'#title' => $item->value,
];
}
else {
$element = [
'#type' => 'markup',
'#markup' => $item->value,
];
}
$elements[$delta] = $element;
}
}
return $elements;
}
}

View File

@@ -0,0 +1,44 @@
<?php
namespace Drupal\farm_field\Plugin\Field\FieldWidget;
use Drupal\Core\Datetime\DrupalDateTime;
use Drupal\Core\Datetime\Plugin\Field\FieldWidget\TimestampDatetimeWidget;
use Drupal\Core\Form\FormStateInterface;
/**
* Plugin implementation of the 'datetime timestamp optional' widget.
*
* Extends the core datetime_timestamp widget to not default to current time.
* This is useful when a timestamp field is optional and should not default
* to the current time, such as the animal birthdate.
*
* @FieldWidget(
* id = "datetime_timestamp_optional",
* label = @Translation("Datetime Timestamp Optional"),
* field_types = {
* "timestamp",
* }
* )
*/
class TimestampDatetimeOptionalWidget extends TimestampDatetimeWidget {
/**
* {@inheritdoc}
*/
public function massageFormValues(array $values, array $form, FormStateInterface $form_state) {
foreach ($values as &$item) {
// @todo The structure is different whether access is denied or not, to
// be fixed in https://www.drupal.org/node/2326533.
if (isset($item['value']) && $item['value'] instanceof DrupalDateTime) {
$date = $item['value'];
}
elseif (isset($item['value']['object']) && $item['value']['object'] instanceof DrupalDateTime) {
$date = $item['value']['object'];
}
$item['value'] = !empty($date) ? $date->getTimestamp() : NULL;
}
return $values;
}
}

View File

@@ -0,0 +1,36 @@
<?php
namespace Drupal\farm_field\Plugin\Field\FieldWidget;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Field\Plugin\Field\FieldWidget\UriWidget;
use Drupal\Core\Form\FormStateInterface;
/**
* Plugin implementation of the 'uri_string' widget.
*
* @FieldWidget(
* id = "uri_string",
* label = @Translation("URI string field"),
* field_types = {
* "uri",
* }
* )
*/
class UriStringWidget extends UriWidget {
/**
* {@inheritdoc}
*/
public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
// The core uri element uses a type of url, which does not support RFC3986.
// So we just override the type and use a constraint validator instead.
// @see https://www.drupal.org/project/drupal/issues/3186651
// @see \Drupal\farm_field\Plugin\Validation\Constraint\UriValidator
$element = parent::formElement($items, $delta, $element, $form, $form_state);
$element['value']['#type'] = 'textfield';
return $element;
}
}

View File

@@ -0,0 +1,24 @@
<?php
namespace Drupal\farm_field\Plugin\Validation\Constraint;
use Symfony\Component\Validator\Constraints\Url;
/**
* Checks that a URI is valid.
*
* @Constraint(
* id = "Uri",
* label = @Translation("Valid URI", context = "Validation"),
* )
*/
class Uri extends Url {
/**
* The default violation message.
*
* @var string
*/
public $message = 'This value is not a valid URI.';
}

View File

@@ -0,0 +1,117 @@
<?php
namespace Drupal\farm_field\Plugin\Validation\Constraint;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
/**
* Validates the Uri constraint.
*/
class UriValidator extends ConstraintValidator {
/**
* {@inheritdoc}
*/
public function validate(mixed $value, Constraint $constraint) {
// Allow empty values.
if (empty($value->value)) {
return;
}
// Require a scheme.
if (!str_contains($value->value, ':')) {
$this->context->addViolation($constraint->message);
}
// Require a valid URI.
$regex = "/^(?:
[A-Za-z][A-Za-z0-9+\-.]* :
(?: \/\/
(?: (?:[A-Za-z0-9\-._~!$&'()*+,;=:]|%[0-9A-Fa-f]{2})* @)?
(?:
\[
(?:
(?:
(?: (?:[0-9A-Fa-f]{1,4}:){6}
| :: (?:[0-9A-Fa-f]{1,4}:){5}
| (?: [0-9A-Fa-f]{1,4})? :: (?:[0-9A-Fa-f]{1,4}:){4}
| (?: (?:[0-9A-Fa-f]{1,4}:){0,1} [0-9A-Fa-f]{1,4})? :: (?:[0-9A-Fa-f]{1,4}:){3}
| (?: (?:[0-9A-Fa-f]{1,4}:){0,2} [0-9A-Fa-f]{1,4})? :: (?:[0-9A-Fa-f]{1,4}:){2}
| (?: (?:[0-9A-Fa-f]{1,4}:){0,3} [0-9A-Fa-f]{1,4})? :: [0-9A-Fa-f]{1,4}:
| (?: (?:[0-9A-Fa-f]{1,4}:){0,4} [0-9A-Fa-f]{1,4})? ::
) (?:
[0-9A-Fa-f]{1,4} : [0-9A-Fa-f]{1,4}
| (?: (?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?) \.){3}
(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)
)
| (?: (?:[0-9A-Fa-f]{1,4}:){0,5} [0-9A-Fa-f]{1,4})? :: [0-9A-Fa-f]{1,4}
| (?: (?:[0-9A-Fa-f]{1,4}:){0,6} [0-9A-Fa-f]{1,4})? ::
)
| [Vv][0-9A-Fa-f]+\.[A-Za-z0-9\-._~!$&'()*+,;=:]+
)
\]
| (?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}
(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)
| (?:[A-Za-z0-9\-._~!$&'()*+,;=]|%[0-9A-Fa-f]{2})*
)
(?: : [0-9]* )?
(?:\/ (?:[A-Za-z0-9\-._~!$&'()*+,;=:@]|%[0-9A-Fa-f]{2})* )*
| \/
(?: (?:[A-Za-z0-9\-._~!$&'()*+,;=:@]|%[0-9A-Fa-f]{2})+
(?:\/ (?:[A-Za-z0-9\-._~!$&'()*+,;=:@]|%[0-9A-Fa-f]{2})* )*
)?
| (?:[A-Za-z0-9\-._~!$&'()*+,;=:@]|%[0-9A-Fa-f]{2})+
(?:\/ (?:[A-Za-z0-9\-._~!$&'()*+,;=:@]|%[0-9A-Fa-f]{2})* )*
|
)
(?:\? (?:[A-Za-z0-9\-._~!$&'()*+,;=:@\/?]|%[0-9A-Fa-f]{2})* )?
(?:\# (?:[A-Za-z0-9\-._~!$&'()*+,;=:@\/?]|%[0-9A-Fa-f]{2})* )?
| (?: \/\/
(?: (?:[A-Za-z0-9\-._~!$&'()*+,;=:]|%[0-9A-Fa-f]{2})* @)?
(?:
\[
(?:
(?:
(?: (?:[0-9A-Fa-f]{1,4}:){6}
| :: (?:[0-9A-Fa-f]{1,4}:){5}
| (?: [0-9A-Fa-f]{1,4})? :: (?:[0-9A-Fa-f]{1,4}:){4}
| (?: (?:[0-9A-Fa-f]{1,4}:){0,1} [0-9A-Fa-f]{1,4})? :: (?:[0-9A-Fa-f]{1,4}:){3}
| (?: (?:[0-9A-Fa-f]{1,4}:){0,2} [0-9A-Fa-f]{1,4})? :: (?:[0-9A-Fa-f]{1,4}:){2}
| (?: (?:[0-9A-Fa-f]{1,4}:){0,3} [0-9A-Fa-f]{1,4})? :: [0-9A-Fa-f]{1,4}:
| (?: (?:[0-9A-Fa-f]{1,4}:){0,4} [0-9A-Fa-f]{1,4})? ::
) (?:
[0-9A-Fa-f]{1,4} : [0-9A-Fa-f]{1,4}
| (?: (?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?) \.){3}
(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)
)
| (?: (?:[0-9A-Fa-f]{1,4}:){0,5} [0-9A-Fa-f]{1,4})? :: [0-9A-Fa-f]{1,4}
| (?: (?:[0-9A-Fa-f]{1,4}:){0,6} [0-9A-Fa-f]{1,4})? ::
)
| [Vv][0-9A-Fa-f]+\.[A-Za-z0-9\-._~!$&'()*+,;=:]+
)
\]
| (?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}
(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)
| (?:[A-Za-z0-9\-._~!$&'()*+,;=]|%[0-9A-Fa-f]{2})*
)
(?: : [0-9]* )?
(?:\/ (?:[A-Za-z0-9\-._~!$&'()*+,;=:@]|%[0-9A-Fa-f]{2})* )*
| \/
(?: (?:[A-Za-z0-9\-._~!$&'()*+,;=:@]|%[0-9A-Fa-f]{2})+
(?:\/ (?:[A-Za-z0-9\-._~!$&'()*+,;=:@]|%[0-9A-Fa-f]{2})* )*
)?
| (?:[A-Za-z0-9\-._~!$&'()*+,;=@] |%[0-9A-Fa-f]{2})+
(?:\/ (?:[A-Za-z0-9\-._~!$&'()*+,;=:@]|%[0-9A-Fa-f]{2})* )*
|
)
(?:\? (?:[A-Za-z0-9\-._~!$&'()*+,;=:@\/?]|%[0-9A-Fa-f]{2})* )?
(?:\# (?:[A-Za-z0-9\-._~!$&'()*+,;=:@\/?]|%[0-9A-Fa-f]{2})* )?
)$/x";
if (!preg_match($regex, $value->value)) {
$this->context->addViolation($constraint->message);
}
}
}