Skip to content
Snippets Groups Projects

Issue #3504027: Add a VBO Plugin to Bulk Generate Alt Text for Media Items with Short or Empty Alt Text

Open Issue #3504027: Add a VBO Plugin to Bulk Generate Alt Text for Media Items with Short or Empty Alt Text
<?php
namespace Drupal\ai_image_bulk_alt_text\Plugin\Action;
use Drupal\views_bulk_operations\Action\ViewsBulkOperationsActionBase;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\Config;
use Drupal\ai\OperationType\Chat\ChatInput;
use Drupal\ai\OperationType\Chat\ChatMessage;
use Drupal\ai\OperationType\GenericType\ImageFile;
use Drupal\file\Entity\File;
use Drupal\ai_image_alt_text\ProviderHelper;
/**
* AI Image Bulk Alt Text Action
*
* @Action(
* id = "ai_image_bulk_alt_text",
* label = @Translation("Generate Alt text"),
* type = "media"
* )
*/
class AiImageBulkAltTextAction extends ViewsBulkOperationsActionBase {
use StringTranslationTrait;
/**
* {@inheritdoc}
*/
public function execute($entity = NULL) {
// Only execute if the user has permission to generate alt text with AI.
// And has permission to batch fix AI images alt text.
if (\Drupal::currentUser()->hasPermission('generate ai alt tags')
&& \Drupal::currentUser()->hasPermission('batch fix ai images alt text')) {
if (isset($entity)) {
$entity_type_id = $entity->getEntityTypeId();
$bundle_id = $entity->bundle();
$lang_code = $entity->language()->getId();
$entity_id = $entity->id();
if (isset($entity_id)) {
$entity = \Drupal::service('entity_type.manager')->getStorage($entity_type_id)->load($entity_id);
$entityFieldsWithAltText = $this->getEntityFieldsWithAltText($entity);
if (isset($entityFieldsWithAltText)
&& count($entityFieldsWithAltText) > 0) {
foreach ($entityFieldsWithAltText as $onEntityFieldWithAltText) {
$newGeneratedAiAltText = $this->generateAltText($onEntityFieldWithAltText['file_entity'], $lang_code);
$entity->{$onEntityFieldWithAltText['field_name']}->alt = $newGeneratedAiAltText;
}
$entity->save();
}
}
}
return $this->t('Bulk Generate Alt Text with AI');
}
$this->messenger()->addError('You do not have permission to generate alt text with AI or to batch fix AI image alt text.');
return $this->t('Not updated');
}
/**
* {@inheritdoc}
*/
public function access($object, AccountInterface $account = NULL, $return_as_object = FALSE) {
return $object->access('update', $account, $return_as_object);
}
/**
* Get entity fields with alt text.
*
* @return array
* An array of entities and fields.
*/
protected function getEntityFieldsWithAltText($entity) {
$imageFieldsForAllEntityTypes = $this->getImageFieldsForAllEntityTypes();
$entityFieldsWithAltText = [];
foreach ($imageFieldsForAllEntityTypes as $field) {
if (isset($entity->{$field['field']})) {
// Check if the entity has a field with an existing alt text value or an empty alt text value.
if (isset($entity->{$field['field']}->alt)
|| (empty($entity->{$field['field']}->alt) && !empty($entity->{$field['field']}->entity))) {
$entityFieldsWithAltText[] = [
'file_entity' => $entity->{$field['field']}->entity,
'field_name' => $field['field'],
];
}
}
}
return $entityFieldsWithAltText;
}
/**
* Get the list of all image fields for all entity types.
*
* @return array
* An array of image fields.
*/
protected function getImageFieldsForAllEntityTypes() {
$fields = [];
$entity_types = $this->getEntityTypes();
foreach ($entity_types as $entity_type_id => $bundles) {
foreach ($bundles as $bundle_id => $bundle) {
foreach (\Drupal::service('entity_field.manager')->getFieldDefinitions($entity_type_id, $bundle_id) as $field_name => $field_definition) {
if ($field_definition->getType() == 'image') {
// Check the config if alt text is enabled.
$config = \Drupal::service('config.factory')->getEditable('field.field.' . $entity_type_id . '.' . $bundle_id . '.' . $field_name);
if (!$config->get('settings.alt_field')) {
continue;
}
// Get the table name for this field.
$table = $entity_type_id . '__' . $field_name;
$fields[$table] = [
'entity_type' => $entity_type_id,
'bundle' => $bundle_id,
'field' => $field_name,
];
}
}
}
}
return $fields;
}
/**
* Get a list of all content entities and bundles.
*
* @return array
* An array of entity types and bundles.
*/
protected function getEntityTypes() {
$entity_types = \Drupal::entityTypeManager()->getDefinitions();
$bundles = [];
foreach ($entity_types as $entity_type_id => $entity_type) {
// Check if its a content entity type.
if (!$entity_type->entityClassImplements('\Drupal\Core\Entity\ContentEntityInterface')) {
continue;
}
$bundles[$entity_type_id] = \Drupal::service('entity_type.bundle.info')->getBundleInfo($entity_type_id);
}
return $bundles;
}
/**
* Generate Alt text.
*
* @param \Drupal\file\Entity\File $file
* File entity.
*
* @return string
* The alt text as string.
*/
public function generateAltText(File $file = NULL, $lang_code = 'en') {
$entityTypeManager = \Drupal::service('entity_type.manager');
$languageManager = \Drupal::service('language_manager');
$aiConfig = \Drupal::service('config.factory')->get('ai.settings');
$altTextConfig = \Drupal::service('config.factory')->get('ai_image_alt_text.settings');
$aiProviderManager = \Drupal::service('ai.provider');
$twig = \Drupal::service('twig');
$providerHelper = \Drupal::service('ai_image_alt_text.provider');
// Check that the user has access to the file.
if (!$file || !$file->access('view')) {
$this->messenger()->addError('The file does not exist or you do not have access to it.');
}
// Check that the file is an image.
if (!$file->getMimeType() || strpos($file->getMimeType(), 'image/') !== 0) {
$this->messenger()->addError('The file is not an image.');
}
// Check if there is a preferred model.
$data = $providerHelper->getSetProvider();
if (!$data) {
$this->messenger()->addError('No AI provider found.');
}
$provider = $data['provider_id'];
$model = $data['model_id'];
// Get the configuration.
$prompt = $altTextConfig->get('prompt');
$image_style_name = $altTextConfig->get('image_style');
// If the image style is set, get the image URL.
$image = new ImageFile();
if ($image_style_name) {
/** @var \Drupal\Image\Entity\ImageStyle */
$image_style = $entityTypeManager->getStorage('image_style')->load($image_style_name);
$image_uri = $file->getFileUri();
// Get the image style url and generate it.
$scaled_image_uri = $image_style->buildUri($image_uri);
$image_style->createDerivative($image_uri, $scaled_image_uri);
$image->setBinary(file_get_contents($scaled_image_uri));
$image->setMimeType('image/png');
} else {
// Just get the file.
$image->setFileFromFile($file);
}
$images[] = $image;
$language = $languageManager->getLanguageName($lang_code) ?? 'English';
$prompt_text = $twig->renderInline($prompt, [
'entity_lang_name' => $language,
]);
$input = new ChatInput([
new ChatMessage('user',
(string) $prompt_text,
$images
),
]);
$output = $provider->chat($input, $model);
$alt_text = $output->getNormalized()->getText();
return $alt_text;
}
}
Loading