coveo-1.0.0-alpha1/modules/coveo_search_api/src/Plugin/search_api/processor/CoveoFileExtractor.php
modules/coveo_search_api/src/Plugin/search_api/processor/CoveoFileExtractor.php
<?php
declare(strict_types=1);
namespace Drupal\coveo_search_api\Plugin\search_api\processor;
use Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException;
use Drupal\Component\Plugin\Exception\PluginNotFoundException;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Field\EntityReferenceFieldItemListInterface;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Plugin\PluginFormInterface;
use Drupal\Core\Utility\Error;
use Drupal\file\Plugin\Field\FieldType\FileFieldItemList;
use Drupal\search_api\Datasource\DatasourceInterface;
use Drupal\search_api\Item\FieldInterface;
use Drupal\search_api\Item\ItemInterface;
use Drupal\search_api\Processor\ProcessorInterface;
use Drupal\search_api\Processor\ProcessorPluginBase;
use Drupal\search_api\Processor\ProcessorProperty;
use Psr\Log\LoggerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Provides file fields processor.
*
* @todo Limit file types
* @todo Limit field cardinality? Warn?
* @todo Limit upload size.
*
* @SearchApiProcessor(
* id = "coveo_file_attachments",
* label = @Translation("Coveo file attachments"),
* description = @Translation("Adds the file attachments content to the indexed data."),
* stages = {
* "add_properties" = 0,
* }
* )
*/
class CoveoFileExtractor extends ProcessorPluginBase implements PluginFormInterface, ProcessorInterface {
/**
* Prefix of the properties provided by this module.
*/
const COVEO_PREFIX = 'c_attachment_';
/**
* Name of the "virtual" field for file entity content.
*
* This is used to represent the contents of a file when indexing a file
* entity directly.
*/
const COVEO_FILE_ENTITY = self::COVEO_PREFIX . 'file_entity';
/**
* {@inheritdoc}
*/
public function __construct(
array $configuration,
$plugin_id,
array $plugin_definition,
private EntityTypeManagerInterface $entityTypeManager,
private LoggerInterface $logger,
) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
}
/**
* {@inheritdoc}
*/
public static function create(
ContainerInterface $container,
array $configuration,
$plugin_id,
$plugin_definition,
) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('entity_type.manager'),
$container->get('logger.channel.coveo'),
);
}
/**
* {@inheritdoc}
*/
public function getPropertyDefinitions(?DatasourceInterface $datasource = NULL) {
$properties = [];
if ($datasource === NULL) {
// Add properties for all index available file fields and for file entity.
foreach ($this->getFileFieldsAndFileEntityItems() as $field_name => $label) {
$properties[static::COVEO_PREFIX . $field_name] = new ProcessorProperty([
'label' => $this->t(
'Coveo Attachment: @label',
['@label' => $label]
),
'description' => $this->t(
'Coveo Attachment: @label',
['@label' => $label]
),
'type' => 'coveo_file',
'processor_id' => $this->getPluginId(),
]);
}
}
return $properties;
}
/**
* {@inheritdoc}
*/
public function addFieldValues(ItemInterface $item) {
$item_fields = $item->getFields();
$entity = $item->getOriginalObject()->getValue();
foreach ($item_fields as $item_field) {
$field_name = str_replace(self::COVEO_PREFIX, '', $item_field->getPropertyPath());
if ($item_field->getType() === 'coveo_file') {
if ($entity->hasField($field_name)) {
$type = $entity->get($field_name)->getFieldDefinition()->getType();
if ($type === 'file') {
$this->extractFile($item_field, $entity->get($field_name));
}
elseif ($type == 'entity_reference') {
$this->extractMedia($item_field, $entity->get($field_name));
}
}
}
}
}
/**
* Get the file fields of indexed bundles and an entity file general item.
*
* @return array
* An array of file field with field name as key and label as value and
* an element for generic file entity item.
*/
private function getFileFieldsAndFileEntityItems() {
$file_elements = [];
// Retrieve file fields of indexed bundles.
foreach ($this->getIndex()->getDatasources() as $datasource) {
if ($datasource->getPluginId() == 'entity:file') {
$file_elements[static::COVEO_FILE_ENTITY] = $this->t('File entity');
}
foreach ($datasource->getPropertyDefinitions() as $property) {
if ($property instanceof FieldDefinitionInterface) {
if ($property->getType() == 'file') {
$file_elements[$property->getName()] = $property->getLabel();
}
if ($property->getType() == "entity_reference") {
if ($property->getSetting('target_type') === 'media') {
$settings = $property->getItemDefinition()->getSettings();
if (isset($settings['handler_settings']['target_bundles'])) {
// For each media bundle allowed, check if the source field is a
// file field.
foreach ($settings['handler_settings']['target_bundles'] as $bundle_name) {
try {
$type = $this->entityTypeManager
->getStorage('media_type')
->load($bundle_name);
if (!empty($type)) {
$bundle_configuration = $this->entityTypeManager
->getStorage('media_type')
->load($bundle_name)->toArray();
if (isset($bundle_configuration['source_configuration']['source_field'])) {
$source_field = $bundle_configuration['source_configuration']['source_field'];
if ($field_config = $this->entityTypeManager
->getStorage('field_storage_config')
->load(sprintf('media.%s', $source_field))) {
$field_config = $field_config->toArray();
// @todo Should we allow other media types like image?
if (isset($field_config['type']) && $field_config['type'] === 'file') {
$file_elements[$property->getName()] = $property->getLabel();
}
}
}
}
}
catch (InvalidPluginDefinitionException $e) {
Error::logException($this->logger, $e);
continue;
}
catch (PluginNotFoundException $e) {
Error::logException($this->logger, $e);
continue;
}
}
}
}
}
}
}
}
return $file_elements;
}
/**
* {@inheritDoc}
*/
#[\Override]
public function buildConfigurationForm(
array $form,
FormStateInterface $form_state,
): array {
return $form;
}
/**
* {@inheritDoc}
*/
#[\Override]
public function validateConfigurationForm(
array &$form,
FormStateInterface $form_state,
): void {
// @todo Implement validateConfigurationForm() method.
}
/**
* {@inheritDoc}
*/
#[\Override]
public function submitConfigurationForm(
array &$form,
FormStateInterface $form_state,
): void {
// @todo Implement submitConfigurationForm() method.
}
/**
* Extract files from files fields.
*
* @param \Drupal\search_api\Item\FieldInterface $item_field
* Search API field to add values to.
* @param \Drupal\file\Plugin\Field\FieldType\FileFieldItemList $field_list
* File list field.
*/
private function extractFile(FieldInterface $item_field, FileFieldItemList $field_list): void {
foreach ($field_list->filterEmptyItems()->referencedEntities() as $file) {
/** @var \Drupal\file\Entity\File $file */
$item_field->addValue($file);
}
}
/**
* Extract files from media fields.
*
* @param \Drupal\search_api\Item\FieldInterface $item_field
* Search API field to add values to.
* @param \Drupal\Core\Field\EntityReferenceFieldItemListInterface $field_list
* List of media fields.
*/
private function extractMedia(FieldInterface $item_field, EntityReferenceFieldItemListInterface $field_list): void {
$field_def = $field_list->getFieldDefinition();
if ($field_def->getItemDefinition()->getSetting('target_type') === 'media') {
/** @var \Drupal\media\Entity\Media $media */
foreach ($field_list->filterEmptyItems()->referencedEntities() as $media) {
$bundle_configuration = $media->getSource()->getConfiguration();
if (isset($bundle_configuration['source_field'])) {
$media_field = $media->get($bundle_configuration['source_field']);
if ($media_field instanceof FileFieldItemList) {
$this->extractFile($item_field, $media_field);
}
}
}
}
}
}
