diff --git a/patches/image_field_caption_3472997_12.patch b/patches/image_field_caption_3472997_12.patch new file mode 100644 index 0000000..ab2287c --- /dev/null +++ b/patches/image_field_caption_3472997_12.patch @@ -0,0 +1,1607 @@ +From a3b983a44de65ceb4f0c0347136cca29b3f03349 Mon Sep 17 00:00:00 2001 +From: Dieter Holvoet +Date: Mon, 9 Sep 2024 11:26:03 +0200 +Subject: [PATCH 1/4] Rewrite module to use default field storage + +--- + image_field_caption.info.yml | 3 +- + image_field_caption.install | 376 +++++++++------- + image_field_caption.module | 307 +++---------- + image_field_caption.services.yml | 7 - + src/ImageCaptionItem.php | 104 ++++- + src/ImageCaptionStorage.php | 425 ------------------ + .../FieldFormatter/ImageCaptionFormatter.php | 2 + + 7 files changed, 355 insertions(+), 869 deletions(-) + delete mode 100644 image_field_caption.services.yml + delete mode 100644 src/ImageCaptionStorage.php + +diff --git a/image_field_caption.info.yml b/image_field_caption.info.yml +index 17d7bf9..e04a8e9 100644 +--- a/image_field_caption.info.yml ++++ b/image_field_caption.info.yml +@@ -2,4 +2,5 @@ name: Image Field Caption + description: 'Provides a caption textarea for image fields.' + package: Other + type: module +-core_version_requirement: ^9 || ^10 ++core_version_requirement: ^9.2 || ^10 ++php: 7.1 +diff --git a/image_field_caption.install b/image_field_caption.install +index 92e9220..63a974b 100644 +--- a/image_field_caption.install ++++ b/image_field_caption.install +@@ -1,172 +1,222 @@ + 'The base table for the image_field_caption module.', +- 'fields' => [ +- 'entity_type' => [ +- 'description' => 'The entity type attached to this caption', +- 'type' => 'varchar', +- 'length' => 128, +- 'not null' => TRUE, +- 'default' => '', +- ], +- 'bundle' => [ +- 'description' => 'The bundle attached to this caption', +- 'type' => 'varchar', +- 'length' => 128, +- 'not null' => TRUE, +- 'default' => '', +- ], +- 'field_name' => [ +- 'description' => 'The field name attached to this caption', +- 'type' => 'varchar', +- 'length' => 32, +- 'not null' => TRUE, +- 'default' => '', +- ], +- 'entity_id' => [ +- 'description' => 'The entity id attached to this caption', +- 'type' => 'int', +- 'unsigned' => TRUE, +- 'not null' => TRUE, +- ], +- 'revision_id' => [ +- 'description' => 'The entity id attached to this caption, or NULL if the entity type is not versioned', +- 'type' => 'int', +- 'unsigned' => TRUE, +- 'not null' => FALSE, +- ], +- 'language' => [ +- 'description' => 'The language attached to this caption', +- 'type' => 'varchar', +- 'length' => 32, +- 'not null' => TRUE, +- 'default' => '', +- ], +- 'delta' => [ +- 'description' => 'The sequence number for this caption, used for multi-value fields', +- 'type' => 'int', +- 'unsigned' => TRUE, +- 'not null' => TRUE, +- ], +- 'caption' => [ +- 'description' => 'The caption text.', +- 'type' => 'text', +- 'not null' => FALSE, +- ], +- 'caption_format' => [ +- 'description' => 'The caption format.', +- 'type' => 'varchar', +- 'length' => 255, +- 'not null' => FALSE, +- ], +- ], +- 'indexes' => [ +- 'entity_type' => ['entity_type'], +- 'bundle' => ['bundle'], +- 'entity_id' => ['entity_id'], +- 'revision_id' => ['revision_id'], +- 'language' => ['language'], +- ], +- 'primary key' => [ +- 'entity_type', +- 'field_name', +- 'entity_id', +- 'language', +- 'delta', +- ], +- ]; +- +- // Image Field Caption revision table. +- $schema['image_field_caption_revision'] = [ +- 'description' => 'The revision table for the image_field_caption module.', +- 'fields' => [ +- 'entity_type' => [ +- 'description' => 'The entity type attached to this caption', +- 'type' => 'varchar', +- 'length' => 128, +- 'not null' => TRUE, +- 'default' => '', +- ], +- 'bundle' => [ +- 'description' => 'The bundle attached to this caption', +- 'type' => 'varchar', +- 'length' => 128, +- 'not null' => TRUE, +- 'default' => '', +- ], +- 'field_name' => [ +- 'description' => 'The field name attached to this caption', +- 'type' => 'varchar', +- 'length' => 32, +- 'not null' => TRUE, +- 'default' => '', +- ], +- 'entity_id' => [ +- 'description' => 'The entity id attached to this caption', +- 'type' => 'int', +- 'unsigned' => TRUE, +- 'not null' => TRUE, +- ], +- 'revision_id' => [ +- 'description' => 'The entity id attached to this caption, or NULL if the entity type is not versioned', +- 'type' => 'int', +- 'unsigned' => TRUE, +- 'not null' => TRUE, +- ], +- 'language' => [ +- 'description' => 'The language attached to this caption', +- 'type' => 'varchar', +- 'length' => 32, +- 'not null' => TRUE, +- 'default' => '', +- ], +- 'delta' => [ +- 'description' => 'The sequence number for this caption, used for multi-value fields', +- 'type' => 'int', +- 'unsigned' => TRUE, +- 'not null' => TRUE, +- ], +- 'caption' => [ +- 'description' => 'The caption text.', +- 'type' => 'text', +- 'not null' => FALSE, +- ], +- 'caption_format' => [ +- 'description' => 'The caption format.', +- 'type' => 'varchar', +- 'length' => 255, +- 'not null' => FALSE, +- ], +- ], +- 'indexes' => [ +- 'entity_type' => ['entity_type'], +- 'bundle' => ['bundle'], +- 'entity_id' => ['entity_id'], +- 'revision_id' => ['revision_id'], +- 'language' => ['language'], +- ], +- 'primary key' => [ +- 'entity_type', +- 'field_name', +- 'entity_id', +- 'revision_id', +- 'language', +- 'delta', +- ], +- ]; +- +- return $schema; ++function image_field_caption_uninstall(): void { ++ _image_field_caption_field_type_schema_column_remove_helper('image', ['caption', 'caption_format']); + } + +-/* @todo Programmatically set the default formatter for all fields that uses this field formatter using image_field_caption_uninstall(). */ ++/** ++ * Helper function to add new columns to a field type. ++ * ++ * @param $field_type ++ * The field type id. ++ * @param array $columns_to_add ++ * array of the column names from schema() function. ++ * ++ * @see https://gist.github.com/JPustkuchen/ce53d40303a51ca5f17ce7f48c363b9b ++ * @see https://www.drupal.org/project/drupal/issues/937442 ++ */ ++function _image_field_caption_field_type_schema_column_add_helper(string $field_type, array $columns_to_add = []): void { ++ $processed_fields = []; ++ $field_type_manager = \Drupal::service('plugin.manager.field.field_type'); ++ $field_definition = $field_type_manager->getDefinition($field_type); ++ $field_item_class = $field_definition['class']; ++ ++ $schema = \Drupal::database()->schema(); ++ $entity_type_manager = \Drupal::entityTypeManager(); ++ $entity_field_manager = \Drupal::service('entity_field.manager'); ++ $entity_field_map = $entity_field_manager->getFieldMapByFieldType($field_type); ++ // The key-value collection for tracking installed storage schema. ++ $entity_storage_schema_sql = \Drupal::keyValue('entity.storage_schema.sql'); ++ $entity_definitions_installed = \Drupal::keyValue('entity.definitions.installed'); ++ ++ foreach ($entity_field_map as $entity_type_id => $field_map) { ++ $entity_storage = $entity_type_manager->getStorage($entity_type_id); ++ ++ // Only SQL storage based entities are supported / throw known exception. ++ if (!($entity_storage instanceof SqlContentEntityStorage)) { ++ continue; ++ } ++ ++ $entity_type = $entity_type_manager->getDefinition($entity_type_id); ++ $field_storage_definitions = $entity_field_manager->getFieldStorageDefinitions($entity_type_id); ++ /** @var Drupal\Core\Entity\Sql\DefaultTableMapping $table_mapping */ ++ $table_mapping = $entity_storage->getTableMapping($field_storage_definitions); ++ // Only need field storage definitions of our field type: ++ /** @var \Drupal\Core\Field\FieldStorageDefinitionInterface $field_storage_definition */ ++ foreach (array_intersect_key($field_storage_definitions, $field_map) as $field_storage_definition) { ++ $field_name = $field_storage_definition->getName(); ++ try { ++ $table = $table_mapping->getFieldTableName($field_name); ++ } catch (SqlContentEntityStorageException $e) { ++ // Custom storage? Broken site? No matter what, if there is no table ++ // or column, there's little we can do. ++ continue; ++ } ++ // See if the field has a revision table. ++ $revision_table = NULL; ++ if ($entity_type->isRevisionable() && $field_storage_definition->isRevisionable()) { ++ if ($table_mapping->requiresDedicatedTableStorage($field_storage_definition)) { ++ $revision_table = $table_mapping->getDedicatedRevisionTableName($field_storage_definition); ++ } ++ elseif ($table_mapping->allowsSharedTableStorage($field_storage_definition)) { ++ $revision_table = $entity_type->getRevisionDataTable() ?: $entity_type->getRevisionTable(); ++ } ++ } ++ // Load the installed field schema so that it can be updated. ++ $schema_key = "$entity_type_id.field_schema_data.$field_name"; ++ $field_schema_data = $entity_storage_schema_sql->get($schema_key); ++ ++ $processed_fields[] = [$entity_type_id, $field_name]; ++ // Loop over each new column and add it as a schema column change. ++ foreach ($columns_to_add as $column_id) { ++ $column = $table_mapping->getFieldColumnName($field_storage_definition, $column_id); ++ // Add `initial_from_field` to the new spec, as this will copy over ++ // the entire data. ++ $field_schema = $field_item_class::schema($field_storage_definition); ++ $spec = $field_schema['columns'][$column_id]; ++ ++ // Add the new column. ++ $schema->addField($table, $column, $spec); ++ if ($revision_table) { ++ $schema->addField($revision_table, $column, $spec); ++ } ++ ++ // Add the new column to the installed field schema. ++ if (!empty($field_schema_data)) { ++ $field_schema_data[$table]['fields'][$column] = $field_schema['columns'][$column_id]; ++ $field_schema_data[$table]['fields'][$column]['not null'] = FALSE; ++ if ($revision_table) { ++ $field_schema_data[$revision_table]['fields'][$column] = $field_schema['columns'][$column_id]; ++ $field_schema_data[$revision_table]['fields'][$column]['not null'] = FALSE; ++ } ++ } ++ } ++ ++ // Save changes to the installed field schema. ++ if (!empty($field_schema_data)) { ++ $entity_storage_schema_sql->set($schema_key, $field_schema_data); ++ } ++ if ($table_mapping->allowsSharedTableStorage($field_storage_definition)) { ++ $key = "$entity_type_id.field_storage_definitions"; ++ if ($definitions = $entity_definitions_installed->get($key)) { ++ $definitions[$field_name] = $field_storage_definition; ++ $entity_definitions_installed->set($key, $definitions); ++ } ++ } ++ } ++ } ++} ++ ++/** ++ * Helper function to remove columns from a field type. ++ * ++ * @param $field_type ++ * The field type id. ++ * @param array $columns_to_remove ++ * array of the column names from schema() function. ++ * ++ * @see https://gist.github.com/JPustkuchen/ce53d40303a51ca5f17ce7f48c363b9b ++ * @see https://www.drupal.org/project/drupal/issues/937442 ++ */ ++function _image_field_caption_field_type_schema_column_remove_helper(string $field_type, array $columns_to_remove = []): void { ++ $processed_fields = []; ++ $field_type_manager = \Drupal::service('plugin.manager.field.field_type'); ++ $field_definition = $field_type_manager->getDefinition($field_type); ++ $field_item_class = $field_definition['class']; ++ ++ $schema = \Drupal::database()->schema(); ++ $entity_type_manager = \Drupal::entityTypeManager(); ++ $entity_field_manager = \Drupal::service('entity_field.manager'); ++ $entity_field_map = $entity_field_manager->getFieldMapByFieldType($field_type); ++ // The key-value collection for tracking installed storage schema. ++ $entity_storage_schema_sql = \Drupal::keyValue('entity.storage_schema.sql'); ++ $entity_definitions_installed = \Drupal::keyValue('entity.definitions.installed'); ++ ++ foreach ($entity_field_map as $entity_type_id => $field_map) { ++ $entity_storage = $entity_type_manager->getStorage($entity_type_id); ++ ++ // Only SQL storage based entities are supported / throw known exception. ++ if (!($entity_storage instanceof SqlContentEntityStorage)) { ++ continue; ++ } ++ ++ $entity_type = $entity_type_manager->getDefinition($entity_type_id); ++ $field_storage_definitions = $entity_field_manager->getFieldStorageDefinitions($entity_type_id); ++ /** @var Drupal\Core\Entity\Sql\DefaultTableMapping $table_mapping */ ++ $table_mapping = $entity_storage->getTableMapping($field_storage_definitions); ++ // Only need field storage definitions of our field type: ++ /** @var \Drupal\Core\Field\FieldStorageDefinitionInterface $field_storage_definition */ ++ foreach (array_intersect_key($field_storage_definitions, $field_map) as $field_storage_definition) { ++ $field_name = $field_storage_definition->getName(); ++ try { ++ $table = $table_mapping->getFieldTableName($field_name); ++ } catch (SqlContentEntityStorageException $e) { ++ // Custom storage? Broken site? No matter what, if there is no table ++ // or column, there's little we can do. ++ continue; ++ } ++ // See if the field has a revision table. ++ $revision_table = NULL; ++ if ($entity_type->isRevisionable() && $field_storage_definition->isRevisionable()) { ++ if ($table_mapping->requiresDedicatedTableStorage($field_storage_definition)) { ++ $revision_table = $table_mapping->getDedicatedRevisionTableName($field_storage_definition); ++ } ++ elseif ($table_mapping->allowsSharedTableStorage($field_storage_definition)) { ++ $revision_table = $entity_type->getRevisionDataTable() ?: $entity_type->getRevisionTable(); ++ } ++ } ++ // Load the installed field schema so that it can be updated. ++ $schema_key = "$entity_type_id.field_schema_data.$field_name"; ++ $field_schema_data = $entity_storage_schema_sql->get($schema_key); ++ ++ $processed_fields[] = [$entity_type_id, $field_name]; ++ // Loop over each new column and add it as a schema column change. ++ foreach ($columns_to_remove as $column_id) { ++ $column = $table_mapping->getFieldColumnName($field_storage_definition, $column_id); ++ // Add `initial_from_field` to the new spec, as this will copy over ++ // the entire data. ++ $field_schema = $field_item_class::schema($field_storage_definition); ++ $spec = $field_schema['columns'][$column_id]; ++ ++ // Add the new column. ++ $schema->dropField($table, $column); ++ if ($revision_table) { ++ $schema->dropField($revision_table, $column); ++ } ++ ++ // Remove the column from the installed field schema. ++ if (!empty($field_schema_data)) { ++ unset($field_schema_data[$table]['fields'][$column]); ++ if ($revision_table) { ++ unset($field_schema_data[$revision_table]['fields'][$column]); ++ } ++ } ++ } ++ ++ // Save changes to the installed field schema. ++ if (!empty($field_schema_data)) { ++ $entity_storage_schema_sql->set($schema_key, $field_schema_data); ++ } ++ if ($table_mapping->allowsSharedTableStorage($field_storage_definition)) { ++ $key = "$entity_type_id.field_storage_definitions"; ++ if ($definitions = $entity_definitions_installed->get($key)) { ++ $definitions[$field_name] = $field_storage_definition; ++ $entity_definitions_installed->set($key, $definitions); ++ } ++ } ++ } ++ } ++} +diff --git a/image_field_caption.module b/image_field_caption.module +index d49aa67..63f8999 100644 +--- a/image_field_caption.module ++++ b/image_field_caption.module +@@ -5,112 +5,72 @@ + * Provides a caption textarea for image fields. + */ + +-use Drupal\Core\Entity\EntityInterface; +-use Drupal\Core\Entity\FieldableEntityInterface; + use Drupal\Core\Field\FieldDefinitionInterface; + use Drupal\Core\Form\FormStateInterface; +-use Drupal\Component\Utility\NestedArray; +- +-/** +- * List to do. +- * +- * Support for Views, maybe built in in D8? +- * Support the revision management. +- * */ ++use Drupal\image\Plugin\Field\FieldWidget\ImageWidget; ++use Drupal\image_field_caption\ImageCaptionItem; + + /** + * Implements hook_field_info_alter(). + */ +-function image_field_caption_field_info_alter(&$info) { +- // Set a new class for the image fields. +- $info['image']['class'] = '\Drupal\image_field_caption\ImageCaptionItem'; ++function image_field_caption_field_info_alter(array &$info): void { ++ // Override the image field type class ++ $info['image']['class'] = ImageCaptionItem::class; ++ ++ // Enable translation for the caption property ++ $info['image']['column_groups']['caption'] = [ ++ 'label' => t('Caption'), ++ 'translatable' => TRUE, ++ ]; + } + + /** + * Implements hook_field_widget_single_element_form_alter(). + */ +-function image_field_caption_field_widget_single_element_form_alter(&$element, FormStateInterface $form_state, $context) { +- /** @var \Drupal\field\Entity\FieldConfig $field */ ++function image_field_caption_field_widget_single_element_form_alter(array &$element, FormStateInterface $form_state, $context): void { + $field = $context['items']->getFieldDefinition(); +- // If the current field is an image field. +- if ($field->getType() == 'image') { +- // Get the current field settings. +- $settings = $field->getSettings(); +- // Check if the current field has the caption. +- if (!empty($settings['caption_field'])) { +- $element['#caption_field_required'] = $settings['caption_field_required']; +- $element['#process'][] = '_image_field_caption_widget_process'; +- } ++ assert($field instanceof FieldDefinitionInterface); ++ ++ if ($field->getType() !== 'image') { ++ return; + } ++ ++ $settings = $field->getSettings(); ++ $element['#caption_field'] = $settings['caption_field']; ++ $element['#caption_field_required'] = $settings['caption_field_required']; ++ $element['#process'][] = '_image_field_caption_widget_process'; + } + + /** + * Custom callback function for the #process of an image field type. + */ +-function _image_field_caption_widget_process($element, &$form_state, $form) { +- // Get the entity. +- // $entity = $form_state->getFormObject()->getEntity(); +- // // Get the fields definitions. +- // $field_definitions = $entity->getFieldDefinitions(); +- // // Get the current field definition. +- // if (!empty($field_definitions[$element['#field_name']])) { +- // $field_definition = $field_definitions[$element['#field_name']]; +- // } +- // elseif (!empty($field_definitions[$element['#field_parents'][0]])) { +- // $field_definition = $field_definitions[$element['#field_parents'][0]]; +- // } +- // else { +- // $field_definition = NULL; +- // }. +- // Get the current field values (form state). +- $field_values = $form_state->getValues(); +- // If the field has parents (ex: paragraphs) then get the nested values. +- if (!empty($element['#field_parents'])) { +- $field_values = NestedArray::getValue($field_values, $element['#field_parents']); +- } +- $field_value = (isset($field_values[$element['#field_name']][$element['#delta']]['image_field_caption'])) ? $field_values[$element['#field_name']][$element['#delta']]['image_field_caption'] : []; ++function _image_field_caption_widget_process(array $element, FormStateInterface $form_state, array $form): array { ++ $item = $element['#value']; ++ $item['fids'] = $element['fids']['#value']; + +- // Add the additional caption fields. +- $element['image_field_caption'] = [ ++ // TODO: Make #allowed_formats configurable. ++ ++ $element['caption'] = [ + '#title' => t('Caption'), + '#type' => 'text_format', +- '#value' => (!empty($field_value['value'])) ? $field_value['value'] : ((!empty($element['#value']['caption'])) ? $element['#value']['caption'] : []), +- '#default_value' => (!empty($element['#value']['caption'])) ? $element['#value']['caption'] : (!empty($element['#value']['image_field_caption']) ? $element['#value']['image_field_caption']['value'] : ''), +- '#access' => (bool) $element['#value']['fids'], +- '#format' => (!empty($field_value['format'])) ? $field_value['format'] : ((!empty($element['#value']['caption_format'])) ? $element['#value']['caption_format'] : 'plain_text'), ++ '#default_value' => $item['caption'] ?? '', ++ '#access' => $item['fids'] && $element['#caption_field'], ++ '#format' => $item['caption_format'] ?? 'plain_text', + '#required' => $element['#caption_field_required'], +- '#element_validate' => $element['#caption_field_required'] ? ['_image_field_caption_validate_required'] : [], ++ '#element_validate' => $element['#caption_field_required'] ? [[ImageWidget::class, 'validateRequiredFields']] : [], + ]; + + return $element; + } + +-/** +- * Validate callback for caption field, if the user wants them required. +- * +- * This is separated in a validate function instead of a #required flag to +- * avoid being validated on the process callback. +- */ +-function _image_field_caption_validate_required($element, FormStateInterface $form_state) { +- // Only do validation if the function is triggered from other places than +- // the image process form. +- // Only do validation if the function is triggered from other places than +- // the image process form. +- $triggering_element = $form_state->getTriggeringElement(); +- if (!empty($triggering_element['#submit']) && in_array('file_managed_file_submit', $triggering_element['#submit'], TRUE)) { +- $form_state->setLimitValidationErrors([]); +- } +-} +- + /** + * Implements hook_theme(). + */ +-function image_field_caption_theme() { ++function image_field_caption_theme(): array { + return [ + 'image_caption_formatter' => [ + // As we extend the default image format, the variables passed to the +- // callback function are the same than the original +- // "callback" function ("image_formatter"). ++ // callback function are the same as the original (image_formatter). + 'variables' => [ + 'item' => NULL, + 'item_attributes' => NULL, +@@ -131,10 +91,11 @@ function image_field_caption_theme() { + * (template_preprocess_image_formatter()) and also: + * - caption: An optional caption text. + */ +-function template_preprocess_image_caption_formatter(&$variables) { +- Drupal::moduleHandler()->loadInclude('image', 'inc', 'image.field'); ++function template_preprocess_image_caption_formatter(array &$variables): void { + // Prepare the variables array with the original function. ++ Drupal::moduleHandler()->loadInclude('image', 'inc', 'image.field'); + template_preprocess_image_formatter($variables); ++ + // Set the caption value. + $values = $variables['item']->getValue(); + if (!empty($values['caption'])) { +@@ -147,187 +108,19 @@ function template_preprocess_image_caption_formatter(&$variables) { + } + + /** +- * Implements hook_entity_storage_load(). +- */ +-function image_field_caption_entity_storage_load(array $entities, $entity_type_id) { +- $imageCaption = Drupal::service('image_field_caption.storage'); +- +- if (in_array($entity_type_id, $imageCaption->list('entity_type'))) { +- // This means we already have some captions. +- // No need to do all kinds of checking then. +- /** @var \Drupal\Core\Entity\Entity $entity */ +- foreach ($entities as $entity) { +- // Same load avoiding check. +- if (in_array($entity->bundle(), $imageCaption->list('bundle'))) { +- $needToSave = FALSE; +- +- /** @var \Drupal\Core\Field\FieldItemList $field */ +- foreach ($entity->getFields() as $fieldName => $field) { +- $values = $entity->get($fieldName)->getValue(); +- foreach ($values as $delta => $value) { +- // Get the caption associated to this field. +- $revision_id = (empty($entity->getRevisionId()) ? $entity->id() : $entity->getRevisionId()); +- $caption = $imageCaption->getCaption( +- $entity->getEntityTypeId(), +- $entity->bundle(), +- $fieldName, +- $entity->id(), +- $revision_id, +- $entity->language()->getId(), +- $delta +- ); +- +- // Set the caption value. +- if (!empty($caption)) { +- $values[$delta] = $values[$delta] + $caption; +- $needToSave = TRUE; +- } +- } +- +- if ($needToSave) { +- // Save all values. +- $entity->get($fieldName)->setValue($values); +- } +- } +- +- } +- } +- } +-} +- +-/** +- * Implements hook_entity_insert(). ++ * Implements hook_config_schema_info_alter(). + */ +-function image_field_caption_entity_insert(EntityInterface $entity) { +- image_field_caption_entity_update($entity); +-} +- +-/** +- * Implements hook_entity_update(). +- */ +-function image_field_caption_entity_update(EntityInterface $entity) { +- $imageCaption = Drupal::service('image_field_caption.storage'); +- +- // For a fieldable entity. +- if (($entity instanceof FieldableEntityInterface)) { +- // Get the field names of all image fields. +- $field_names = _image_field_caption_get_image_field_names($entity); +- foreach ($field_names as $field_name) { +- // Get the current field settings. +- $settings = $entity->get($field_name)->getSettings(); +- // If the caption is not enabled => pass this field. +- if (empty($settings['caption_field'])) { +- continue; +- } +- // Delete the caption associated to this field. +- $imageCaption->deleteCaption($entity->getEntityTypeId(), $entity->bundle(), $field_name, $entity->id(), $entity->language() +- ->getId()); +- // Delete the caption revision associated to this field. +- /* +- $imageCaption->deleteCaptionRevision( +- $entity->getEntityTypeId(), $entity->bundle(), +- $field_name, $entity->id(), $entity->getRevisionId(), +- $entity->language()->getId()); */ +- // Get the current field values. +- $values = $entity->get($field_name)->getValue(); +- foreach ($values as $delta => $value) { +- // If a caption text is defined. +- if (!empty($value['image_field_caption']['value'])) { +- // Insert the caption associated to this field. +- // @todo Do the insertion using a multiple query instead several queries into a foreach; +- $revision_id = (empty($entity->getRevisionId()) ? $entity->id() : $entity->getRevisionId()); +- $imageCaption->insertCaption( +- $entity->getEntityTypeId(), +- $entity->bundle(), +- $field_name, +- $entity->id(), +- $revision_id, +- $entity->language()->getId(), +- $delta, +- $value['image_field_caption']['value'], +- $value['image_field_caption']['format'] +- ); +- // Insert the caption revision associated to this field. +- /* +- if ($entity->isNewRevision()) { +- $imageCaption->insertCaptionRevision( +- $entity->getEntityTypeId(), +- $entity->bundle(), +- $field_name, +- $entity->id(), +- $revision_id, +- $entity->language()->getId(), +- $delta, +- $value['image_field_caption']['value'], +- $value['image_field_caption']['format'] +- ); +- } +- */ +- } +- } +- } +- } +-} +- +-/** +- * Implements hook_entity_delete(). +- */ +-function image_field_caption_entity_delete(EntityInterface $entity) { +- $imageCaption = Drupal::service('image_field_caption.storage'); +- +- // For a fieldable entity. +- if (($entity instanceof FieldableEntityInterface)) { +- // Get the field names of all image fields. +- $field_names = _image_field_caption_get_image_field_names($entity); +- foreach ($field_names as $field_name) { +- // Delete the caption associated to this field. +- $imageCaption->deleteCaption($entity->getEntityTypeId(), $entity->bundle(), $field_name, $entity->id(), $entity->language() +- ->getId()); +- // Delete the caption revisions associated to this field. +- /* +- $imageCaption->deleteCaptionRevisions( +- $entity->getEntityTypeId(), $entity->bundle(), +- $field_name, $entity->id(), $entity->language()->getId() +- );*/ +- } +- } +-} +- +-/** +- * Implements hook_entity_revision_delete(). +- */ +-function image_field_caption_entity_revision_delete(EntityInterface $entity) { +- // $imageCaption = Drupal::service('image_field_caption.storage'); +- /* +- // For a fieldable entity. +- if (($entity instanceof FieldableEntityInterface)) { +- // Get the field names of all image fields. +- $field_names = _image_field_caption_get_image_field_names($entity); +- if (!empty($field_names)) { +- // Delete the caption revisions associated to this specific revision. +- $imageCaption->deleteCaptionRevisionsByRevisionId($entity->getRevisionId()); +- } +- } +- */ +-} +- +-/** +- * Determines the image fields on an entity. +- * +- * @param \Drupal\Core\Entity\FieldableEntityInterface $entity +- * An entity whose fields to analyze. +- * +- * @return array +- * The names of the fields on this entity that support formatted text. +- */ +-function _image_field_caption_get_image_field_names(FieldableEntityInterface $entity) { +- // Check if fields definitions are available. +- $field_definitions = $entity->getFieldDefinitions(); +- if (empty($field_definitions)) { +- return []; +- } +- // Only return image fields. +- return array_keys(array_filter($field_definitions, function (FieldDefinitionInterface $definition) { +- return in_array($definition->getType(), ['image'], TRUE); +- })); ++function image_field_caption_config_schema_info_alter(array &$definitions): void { ++ $definitions['field.field_settings.image']['mapping']['caption_field'] = [ ++ 'type' => 'boolean', ++ 'label' => 'Enable Caption field', ++ ]; ++ $definitions['field.field_settings.image']['mapping']['caption_field_required'] = [ ++ 'type' => 'boolean', ++ 'label' => 'Caption field required', ++ ]; ++ $definitions['field_default_image']['mapping']['caption'] = [ ++ 'type' => 'label', ++ 'label' => 'Caption', ++ ]; + } +diff --git a/image_field_caption.services.yml b/image_field_caption.services.yml +deleted file mode 100644 +index 845c2e6..0000000 +--- a/image_field_caption.services.yml ++++ /dev/null +@@ -1,7 +0,0 @@ +-services: +- image_field_caption.storage: +- class: Drupal\image_field_caption\ImageCaptionStorage +- arguments: +- - '@cache.data' +- - '@cache_tags.invalidator' +- - '@database' +diff --git a/src/ImageCaptionItem.php b/src/ImageCaptionItem.php +index 1367735..02c0ada 100644 +--- a/src/ImageCaptionItem.php ++++ b/src/ImageCaptionItem.php +@@ -2,46 +2,90 @@ + + namespace Drupal\image_field_caption; + ++use Drupal\Core\Field\FieldStorageDefinitionInterface; + use Drupal\Core\Form\FormStateInterface; ++use Drupal\Core\StringTranslation\TranslatableMarkup; ++use Drupal\Core\TypedData\DataDefinition; + use Drupal\image\Plugin\Field\FieldType\ImageItem; + + /** +- * Class to provide functionlality for ImageCaptionItem. ++ * Class to provide functionality for ImageCaptionItem. + */ + class ImageCaptionItem extends ImageItem { + ++ /** ++ * {@inheritdoc} ++ */ ++ public static function defaultStorageSettings() { ++ $settings = parent::defaultStorageSettings(); ++ $settings['default_image']['caption'] = ''; ++ ++ return $settings; ++ } ++ + /** + * {@inheritdoc} + */ + public static function defaultFieldSettings() { +- return [ +- 'default_image' => [ +- 'caption' => '', +- ], +- 'caption_field' => FALSE, +- 'caption_field_required' => FALSE, +- ] + parent::defaultFieldSettings(); ++ $settings = parent::defaultFieldSettings(); ++ $settings['default_image']['caption'] = ''; ++ $settings['caption_field'] = FALSE; ++ $settings['caption_field_required'] = FALSE; ++ ++ return $settings; ++ } ++ ++ public static function schema(FieldStorageDefinitionInterface $field_definition) { ++ $schema = parent::schema($field_definition); ++ ++ $schema['columns']['caption'] = [ ++ 'description' => 'The caption text.', ++ 'type' => 'text', ++ 'not null' => FALSE, ++ ]; ++ ++ $schema['columns']['caption_format'] = [ ++ 'description' => 'The caption format.', ++ 'type' => 'varchar', ++ 'length' => 255, ++ 'not null' => FALSE, ++ ]; ++ ++ return $schema; ++ } ++ ++ /** ++ * {@inheritdoc} ++ */ ++ public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) { ++ $properties = parent::propertyDefinitions($field_definition); ++ ++ $properties['caption'] = DataDefinition::create('string') ++ ->setLabel(new TranslatableMarkup('Caption')) ++ ->setDescription(new TranslatableMarkup("Short description of the image displayed underneath the image.")); ++ ++ $properties['caption_format'] = DataDefinition::create('filter_format') ++ ->setLabel(new TranslatableMarkup('Text format of the caption')); ++ ++ return $properties; + } + + /** + * {@inheritdoc} + */ + public function fieldSettingsForm(array $form, FormStateInterface $form_state) { +- // Get base form from ImageItem. + $element = parent::fieldSettingsForm($form, $form_state); +- // Get field settings. + $settings = $this->getSettings(); +- // Get the default field settings. +- $settings_default = self::defaultFieldSettings(); + + // Add caption option. + $element['caption_field'] = [ + '#type' => 'checkbox', + '#title' => t('Enable Caption field'), +- '#default_value' => (!empty($settings['caption_field'])) ? $settings['caption_field'] : $settings_default['caption_field'], +- '#description' => t('Adds an extra text area for captions on image fields.'), ++ '#default_value' => $settings['caption_field'], ++ '#description' => $this->t('Short description of the image displayed underneath the image.'), + '#weight' => 13, + ]; ++ + // Add caption (required) option. + $element['caption_field_required'] = [ + '#type' => 'checkbox', +@@ -51,16 +95,44 @@ class ImageCaptionItem extends ImageItem { + '#weight' => 14, + '#states' => [ + 'visible' => [ +- ':input[name="settings[image_caption_field]"]' => ['checked' => TRUE], ++ ':input[name="settings[caption_field]"]' => ['checked' => TRUE], + ], + ], + ]; ++ + // Add default caption. + $element['default_image']['caption'] = [ + '#type' => 'value', +- '#value' => (!empty($settings['default_image']['caption'])) ? $settings['default_image']['caption'] : $settings_default['default_image']['caption'], ++ '#value' => $settings['default_image']['caption'] ?? '', + ]; + + return $element; + } ++ ++ /** ++ * {@inheritdoc} ++ */ ++ protected function defaultImageForm(array &$element, array $settings) { ++ parent::defaultImageForm($element, $settings); ++ ++ $element['default_image']['caption'] = [ ++ '#type' => 'textfield', ++ '#title' => $this->t('Caption'), ++ '#description' => $this->t('Short description of the image displayed underneath the image.'), ++ '#default_value' => $settings['default_image']['caption'] ?? '', ++ ]; ++ } ++ ++ /** ++ * {@inheritdoc} ++ */ ++ public function setValue($values, $notify = TRUE): void { ++ if (isset($values['caption']) && is_array($values['caption'])) { ++ $values['caption_format'] = $values['caption']['format']; ++ $values['caption'] = $values['caption']['value']; ++ } ++ ++ parent::setValue($values, $notify); ++ } ++ + } +diff --git a/src/ImageCaptionStorage.php b/src/ImageCaptionStorage.php +deleted file mode 100644 +index 9640903..0000000 +--- a/src/ImageCaptionStorage.php ++++ /dev/null +@@ -1,425 +0,0 @@ +-cacheBackend = $cacheBackend; +- $this->cacheTagsInvalidator = $cacheTagsInvalidator; +- $this->database = $database; +- } +- +- /** +- * Check if a caption is already defined for the specified arguments. +- * +- * @param string $entity_type +- * The entity type, like 'node' or 'comment'. +- * @param string $bundle +- * The bundle, like 'article' or 'news'. +- * @param string $field_name +- * The field name of the image field, +- * like 'field_image' or 'field_article_image'. +- * @param int $entity_id +- * The entity id. +- * @param int $revision_id +- * The revision id. +- * @param string $language +- * The language key, like 'en' or 'fr'. +- * @param int $delta +- * The delta of the image field. +- * +- * @return bool +- * TRUE if a caption exists or FALSE if not. +- */ +- public function isCaption($entity_type, $bundle, $field_name, $entity_id, $revision_id, $language, $delta) { +- return (!empty(self::getCaption($entity_type, $bundle, $field_name, $entity_id, $revision_id, $language, $delta))) ? TRUE : FALSE; +- } +- +- /** +- * Get a caption from the database for the specified arguments. +- * +- * @param string $entity_type +- * The entity type, like 'node' or 'comment'. +- * @param string $bundle +- * The bundle, like 'article' or 'news'. +- * @param string $field_name +- * The field name of the image field, +- * like 'field_image' or 'field_article_image'. +- * @param int $entity_id +- * The entity id. +- * @param int $revision_id +- * The revision id. +- * @param string $language +- * The language key, like 'en' or 'fr'. +- * @param int $delta +- * The delta of the image field. +- * +- * @return array +- * A caption array +- * - caption: The caption text. +- * - caption_format: The caption format. +- * or an empty array, if no value found. +- */ +- public function getCaption($entity_type, $bundle, $field_name, $entity_id, $revision_id, $language, $delta) { +- $captions = &drupal_static(__FUNCTION__); +- +- $cacheKey = $this->getCacheKey($entity_type, $entity_id, $revision_id, $language, $field_name, $delta); +- +- if (isset($captions[$cacheKey])) { +- $caption = $captions[$cacheKey]; +- } +- elseif ($cached = $this->cacheBackend->get($cacheKey)) { +- $caption = $cached->data; +- } +- else { +- // Query. +- $query = $this->database->select($this->tableData, 'ifc'); +- $result = $query +- ->fields('ifc', ['caption', 'caption_format']) +- ->condition('entity_type', $entity_type, '=') +- ->condition('bundle', $bundle, '=') +- ->condition('field_name', $field_name, '=') +- ->condition('entity_id', $entity_id, '=') +- ->condition('revision_id', $revision_id, '=') +- ->condition('language', $language, '=') +- ->condition('delta', $delta, '=') +- ->execute() +- ->fetchAssoc(); +- +- // Caption array. +- $caption = []; +- if (!empty($result)) { +- $caption = $result; +- } +- +- // Let the cache depends on the entity. +- // @todo Use getCacheTags() to get the default list. +- $this->cacheBackend->set( +- $cacheKey, +- $caption, +- Cache::PERMANENT, +- [ +- $field_name, +- 'image_field_caption', +- ] +- ); +- } +- +- return $caption; +- } +- +- /** +- * Insert a caption into the database for the specified arguments. +- * +- * @param string $entity_type +- * The entity type, like 'node' or 'comment'. +- * @param string $bundle +- * The bundle, like 'article' or 'news'. +- * @param string $field_name +- * The field name of the image field, +- * like 'field_image' or 'field_article_image'. +- * @param int $entity_id +- * The entity id. +- * @param int $revision_id +- * The revision id. +- * @param string $language +- * The language key, like 'en' or 'fr'. +- * @param int $delta +- * The delta of the image field. +- * @param string $caption +- * The caption text. +- * @param string $caption_format +- * The text format of the caption. +- */ +- public function insertCaption($entity_type, $bundle, $field_name, $entity_id, $revision_id, $language, $delta, $caption, $caption_format) { +- +- $query = $this->database->insert($this->tableData); +- $query +- ->fields([ +- 'entity_type' => $entity_type, +- 'bundle' => $bundle, +- 'field_name' => $field_name, +- 'entity_id' => $entity_id, +- 'revision_id' => $revision_id, +- 'language' => $language, +- 'delta' => $delta, +- 'caption' => $caption, +- 'caption_format' => $caption_format, +- ]) +- ->execute(); +- $this->clearCache($field_name); +- } +- +- /** +- * Insert a caption revision into the database for the specified arguments. +- * +- * @param string $entity_type +- * The entity type, like 'node' or 'comment'. +- * @param string $bundle +- * The bundle, like 'article' or 'news'. +- * @param string $field_name +- * The field name of the image field, +- * like 'field_image' or 'field_article_image'. +- * @param int $entity_id +- * The entity id. +- * @param int $revision_id +- * The revision id. +- * @param string $language +- * The language key, like 'en' or 'fr'. +- * @param int $delta +- * The delta of the image field. +- * @param string $caption +- * The caption text. +- * @param string $caption_format +- * The text format of the caption. +- */ +- public function insertCaptionRevision($entity_type, $bundle, $field_name, $entity_id, $revision_id, $language, $delta, $caption, $caption_format) { +- $query = $this->database->insert($this->tableRevision); +- $query +- ->fields([ +- 'entity_type' => $entity_type, +- 'bundle' => $bundle, +- 'field_name' => $field_name, +- 'entity_id' => $entity_id, +- 'revision_id' => $revision_id, +- 'language' => $language, +- 'delta' => $delta, +- 'caption' => $caption, +- 'caption_format' => $caption_format, +- ]) +- ->execute(); +- $this->clearCache($field_name); +- } +- +- /** +- * Delete a caption from the database for the specified arguments. +- * +- * @param string $entity_type +- * The entity type, like 'node' or 'comment'. +- * @param string $bundle +- * The bundle, like 'article' or 'news'. +- * @param string $field_name +- * The field name of the image field, like +- * 'field_image' or 'field_article_image'. +- * @param int $entity_id +- * The entity id. +- * @param string $language +- * The language key, like 'en' or 'fr'. +- */ +- public function deleteCaption($entity_type, $bundle, $field_name, $entity_id, $language) { +- $query = $this->database->delete($this->tableData); +- $query +- ->condition('entity_type', $entity_type, '=') +- ->condition('bundle', $bundle, '=') +- ->condition('field_name', $field_name, '=') +- ->condition('entity_id', $entity_id, '=') +- ->condition('language', $language, '=') +- ->execute(); +- $this->clearCache($field_name); +- // @todo Try to return the count of the affected rows. +- } +- +- /** +- * Delete a caption revision from the database for the specified arguments. +- * +- * @param string $entity_type +- * The entity type, like 'node' or 'comment'. +- * @param string $bundle +- * The bundle, like 'article' or 'news'. +- * @param string $field_name +- * The field name of the image field, like +- * 'field_image' or 'field_article_image'. +- * @param int $entity_id +- * The entity id. +- * @param int $revision_id +- * The revision id. +- * @param string $language +- * The language key, like 'en' or 'fr'. +- */ +- public function deleteCaptionRevision($entity_type, $bundle, $field_name, $entity_id, $revision_id, $language) { +- $query = $this->database->delete($this->tableRevision); +- $query +- ->condition('entity_type', $entity_type, '=') +- ->condition('bundle', $bundle, '=') +- ->condition('field_name', $field_name, '=') +- ->condition('entity_id', $entity_id, '=') +- ->condition('revision_id', $revision_id, '=') +- ->condition('language', $language, '=') +- ->execute(); +- $this->clearCache($field_name); +- // @todo Try to return the count of the affected rows. +- } +- +- /** +- * Delete all captions revisions for the specified arguments. +- * +- * @param string $entity_type +- * The entity type, like 'node' or 'comment'. +- * @param string $bundle +- * The bundle, like 'article' or 'news'. +- * @param string $field_name +- * The field name of the image field, like 'field_image' +- * or 'field_article_image'. +- * @param int $entity_id +- * The entity id. +- * @param string $language +- * The language key, like 'en' or 'fr'. +- */ +- public function deleteCaptionRevisions($entity_type, $bundle, $field_name, $entity_id, $language) { +- $query = $this->database->delete($this->tableRevision); +- $query +- ->condition('entity_type', $entity_type, '=') +- ->condition('bundle', $bundle, '=') +- ->condition('field_name', $field_name, '=') +- ->condition('entity_id', $entity_id, '=') +- ->condition('language', $language, '=') +- ->execute(); +- $this->clearCache($field_name); +- // @todo Try to return the count of the affected rows. +- } +- +- /** +- * Delete all captions revisions for a specific revision id. +- * +- * @param int $revision_id +- * The revision id. +- */ +- public function deleteCaptionRevisionsByRevisionId($revision_id) { +- $query = $this->database->delete($this->tableRevision); +- $query +- ->condition('revision_id', $revision_id, '=') +- ->execute(); +- } +- +- /** +- * Clears the cache for a certain field name. +- * +- * @param string $field_name +- * The field name of the image field, like 'field_image' +- * or 'field_article_image'. +- */ +- public function clearCache($field_name) { +- $this->cacheTagsInvalidator->invalidateTags([ +- $field_name, +- 'image_field_caption', +- ]); +- } +- +- /** +- * Constructs the cache key. +- * +- * @param string $entity_type +- * The entity type, like 'node' or 'comment'. +- * @param int $entity_id +- * The entity id. +- * @param int $revision_id +- * The revision id. +- * @param string $language +- * The language key, like 'en' or 'fr'. +- * @param string $field_name +- * The field name of the image field, like 'field_image' +- * or 'field_article_image'. +- * @param int $delta +- * The delta of the image field. +- */ +- public function getCacheKey($entity_type, $entity_id, $revision_id, $language, $field_name, $delta) { +- return implode( +- ":", +- [ +- 'caption', +- $entity_type, +- $entity_id, +- $revision_id, +- $language, +- $field_name, +- $delta, +- ] +- ); +- } +- +- /** +- * Function to list out entity type field. +- */ +- public function list($key = 'entity_type') { +- $list = &drupal_static(__FUNCTION__); +- +- if (!isset($list[$key])) { +- // Query. +- $query = $this->database->select($this->tableData, 'ifc'); +- $result = $query +- ->fields('ifc', [$key]) +- ->distinct() +- ->execute() +- ->fetchAll(); +- +- $list[$key] = []; +- foreach ($result as $row) { +- $list[$key][] = $row->{$key}; +- } +- } +- +- return $list[$key]; +- } +- +-} +diff --git a/src/Plugin/Field/FieldFormatter/ImageCaptionFormatter.php b/src/Plugin/Field/FieldFormatter/ImageCaptionFormatter.php +index 7ba4c62..c761a0d 100644 +--- a/src/Plugin/Field/FieldFormatter/ImageCaptionFormatter.php ++++ b/src/Plugin/Field/FieldFormatter/ImageCaptionFormatter.php +@@ -23,10 +23,12 @@ class ImageCaptionFormatter extends ImageFormatter { + */ + public function viewElements(FieldItemListInterface $items, $langcode) { + $elements = parent::viewElements($items, $langcode); ++ + foreach ($elements as $delta => $element) { + // Set a new theme callback function for the image caption formatter. + $elements[$delta]['#theme'] = 'image_caption_formatter'; + } ++ + return $elements; + } + +-- +GitLab + + +From 0066f4a953597385e284bfb5bdee941c4dc78b66 Mon Sep 17 00:00:00 2001 +From: Dieter Holvoet +Date: Mon, 9 Sep 2024 11:28:27 +0200 +Subject: [PATCH 2/4] Add back image module dependency + +--- + image_field_caption.info.yml | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/image_field_caption.info.yml b/image_field_caption.info.yml +index e04a8e9..4861584 100644 +--- a/image_field_caption.info.yml ++++ b/image_field_caption.info.yml +@@ -1,6 +1,10 @@ + name: Image Field Caption + description: 'Provides a caption textarea for image fields.' + package: Other ++ ++dependencies: ++ - drupal:image ++ + type: module + core_version_requirement: ^9.2 || ^10 + php: 7.1 +-- +GitLab + + +From 2e5f7c84145f267c1ac44c37e759d48a9aea07b7 Mon Sep 17 00:00:00 2001 +From: Dieter Holvoet +Date: Mon, 9 Sep 2024 11:31:50 +0200 +Subject: [PATCH 3/4] Fix cs issues + +--- + README.md | 3 ++- + image_field_caption.install | 19 +++++++++++++------ + image_field_caption.module | 7 +++---- + src/ImageCaptionItem.php | 5 ++++- + 4 files changed, 22 insertions(+), 12 deletions(-) + +diff --git a/README.md b/README.md +index 8bc42d9..2c46688 100644 +--- a/README.md ++++ b/README.md +@@ -2,7 +2,8 @@ + + Adds an extra text area for captions on image fields. + Similar to the alt and title text fields available with an image field, +-the caption text area can be used to enter text or html descriptions of an image. ++the caption text area can be used to enter text or html descriptions of an ++image. + + For a full description of the module, visit the + [Image Field Caption](https://www.drupal.org/project/image_field_caption). +diff --git a/image_field_caption.install b/image_field_caption.install +index 63a974b..dab5ae6 100644 +--- a/image_field_caption.install ++++ b/image_field_caption.install +@@ -1,5 +1,10 @@ + getName(); + try { + $table = $table_mapping->getFieldTableName($field_name); +- } catch (SqlContentEntityStorageException $e) { ++ } ++ catch (SqlContentEntityStorageException $e) { + // Custom storage? Broken site? No matter what, if there is no table + // or column, there's little we can do. + continue; +@@ -123,10 +129,10 @@ function _image_field_caption_field_type_schema_column_add_helper(string $field_ + /** + * Helper function to remove columns from a field type. + * +- * @param $field_type ++ * @param string $field_type + * The field type id. + * @param array $columns_to_remove +- * array of the column names from schema() function. ++ * Array of the column names from schema() function. + * + * @see https://gist.github.com/JPustkuchen/ce53d40303a51ca5f17ce7f48c363b9b + * @see https://www.drupal.org/project/drupal/issues/937442 +@@ -163,7 +169,8 @@ function _image_field_caption_field_type_schema_column_remove_helper(string $fie + $field_name = $field_storage_definition->getName(); + try { + $table = $table_mapping->getFieldTableName($field_name); +- } catch (SqlContentEntityStorageException $e) { ++ } ++ catch (SqlContentEntityStorageException $e) { + // Custom storage? Broken site? No matter what, if there is no table + // or column, there's little we can do. + continue; +diff --git a/image_field_caption.module b/image_field_caption.module +index 63f8999..8bae0a6 100644 +--- a/image_field_caption.module ++++ b/image_field_caption.module +@@ -14,10 +14,10 @@ use Drupal\image_field_caption\ImageCaptionItem; + * Implements hook_field_info_alter(). + */ + function image_field_caption_field_info_alter(array &$info): void { +- // Override the image field type class ++ // Override the image field type class. + $info['image']['class'] = ImageCaptionItem::class; + +- // Enable translation for the caption property ++ // Enable translation for the caption property. + $info['image']['column_groups']['caption'] = [ + 'label' => t('Caption'), + 'translatable' => TRUE, +@@ -48,8 +48,7 @@ function _image_field_caption_widget_process(array $element, FormStateInterface + $item = $element['#value']; + $item['fids'] = $element['fids']['#value']; + +- // TODO: Make #allowed_formats configurable. +- ++ // @todo Make #allowed_formats configurable. + $element['caption'] = [ + '#title' => t('Caption'), + '#type' => 'text_format', +diff --git a/src/ImageCaptionItem.php b/src/ImageCaptionItem.php +index 02c0ada..8fb5d92 100644 +--- a/src/ImageCaptionItem.php ++++ b/src/ImageCaptionItem.php +@@ -35,6 +35,9 @@ class ImageCaptionItem extends ImageItem { + return $settings; + } + ++ /** ++ * {@inheritdoc} ++ */ + public static function schema(FieldStorageDefinitionInterface $field_definition) { + $schema = parent::schema($field_definition); + +@@ -65,7 +68,7 @@ class ImageCaptionItem extends ImageItem { + ->setDescription(new TranslatableMarkup("Short description of the image displayed underneath the image.")); + + $properties['caption_format'] = DataDefinition::create('filter_format') +- ->setLabel(new TranslatableMarkup('Text format of the caption')); ++ ->setLabel(new TranslatableMarkup('Text format of the caption')); + + return $properties; + } +-- +GitLab + + +From fffc01d559c45e4b43729619d5cef93916d19417 Mon Sep 17 00:00:00 2001 +From: Dieter Holvoet +Date: Mon, 9 Sep 2024 11:45:46 +0200 +Subject: [PATCH 4/4] Fix issue reported by phpstan + +--- + src/ImageCaptionItem.php | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/src/ImageCaptionItem.php b/src/ImageCaptionItem.php +index 8fb5d92..3ef0772 100644 +--- a/src/ImageCaptionItem.php ++++ b/src/ImageCaptionItem.php +@@ -79,6 +79,7 @@ class ImageCaptionItem extends ImageItem { + public function fieldSettingsForm(array $form, FormStateInterface $form_state) { + $element = parent::fieldSettingsForm($form, $form_state); + $settings = $this->getSettings(); ++ $defaultSettings = self::defaultFieldSettings(); + + // Add caption option. + $element['caption_field'] = [ +@@ -93,7 +94,7 @@ class ImageCaptionItem extends ImageItem { + $element['caption_field_required'] = [ + '#type' => 'checkbox', + '#title' => t('Caption field required'), +- '#default_value' => (!empty($settings['caption_field_required'])) ? $settings['caption_field_required'] : $settings_default['caption_field_required'], ++ '#default_value' => (!empty($settings['caption_field_required'])) ? $settings['caption_field_required'] : $defaultSettings['caption_field_required'], + '#description' => '', + '#weight' => 14, + '#states' => [ +-- +GitLab +