diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 90c7a2fd3963f9526f0a2bdba38ca7efbed48a22..6b77b7835dbf9e22602b01833365923f0256211a 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -46,6 +46,7 @@ include: - "/includes/include.drupalci.workflows.yml" variables: _CSPELL_WORDS: 'alt_text, alt-text, atvfat, atvqueue, dropzonejs, DUMU, ENDHERE, updb' + _CSPELL_IGNORE_PATHS: 'config/install/views.view.alt_text_report.yml, .ddev/*' ################ # Pipeline configuration variables # diff --git a/alt_text_validation.install b/alt_text_validation.install index a27cb12db38dbb32fd4da4a4d9a50873e5db80fc..9ec03b8c10862a36eed05b84ca8386a607ccf992 100644 --- a/alt_text_validation.install +++ b/alt_text_validation.install @@ -12,7 +12,7 @@ use Drupal\alt_text_validation\AtvCommonTrait; */ function alt_text_validation_schema() { - $schema[AtvCommonTrait::AUDIT_TABLE] = [ + $schema[AtvCommonTrait::getAuditTableName()] = [ 'description' => 'Audit results for alt_text_validation.', 'fields' => [ 'filename' => [ @@ -83,10 +83,10 @@ function alt_text_validation_schema() { function alt_text_validation_uninstall($is_syncing) : void { // Clean up states. $state = \Drupal::state(); - $state->delete(AtvCommonTrait::AUDIT_STATUS_KEY); - $state->delete(AtvCommonTrait::AUDIT_START_TIME_KEY); - $state->delete(AtvCommonTrait::AUDIT_END_TIME_KEY); + $state->delete(AtvCommonTrait::getAuditStatusKey()); + $state->delete(AtvCommonTrait::getAuditStartTimeKey()); + $state->delete(AtvCommonTrait::getAuditEndTimeKey()); // Clean up Queues. - \Drupal::queue(AtvCommonTrait::ATV_ENTITY_TYPE_QUEUE)->deleteQueue(); - \Drupal::queue(AtvCommonTrait::ATV_ENTITY_INSTANCE_QUEUE)->deleteQueue(); + \Drupal::queue(AtvCommonTrait::getEntityTypeQueueName())->deleteQueue(); + \Drupal::queue(AtvCommonTrait::getEntityInstanceQueueName())->deleteQueue(); } diff --git a/alt_text_validation.views.inc b/alt_text_validation.views.inc index 202c311bf61cafac491d575e103dc459a6b5a5be..0fef234b28f09da1df14bf2f33eb79e1a888d85d 100644 --- a/alt_text_validation.views.inc +++ b/alt_text_validation.views.inc @@ -202,5 +202,14 @@ function alt_text_validation_views_data() { ], ]; + $data['views']['atv_summary'] = [ + 'title' => t('Alt text validation report summary'), + 'help' => t('Timing details about the generation of the Alt Text Validation Report.'), + 'category' => 'Alt Text Validation', + 'area' => [ + 'id' => 'alt_text_validation_report_summary', + ], + ]; + return $data; } diff --git a/config/install/views.view.alt_text_report.yml b/config/install/views.view.alt_text_report.yml index 083b90a811d93cdb4484dd284c36376f7d1ea245..2604c2b39f649b2efa4d823bf512c449ffab517f 100644 --- a/config/install/views.view.alt_text_report.yml +++ b/config/install/views.view.alt_text_report.yml @@ -7,7 +7,7 @@ dependencies: - alt_text_validation - user _core: - default_config_hash: arbE1NwXoURiTCq74RUpJLLiDi-mzkWxP_m04IiDUMU + default_config_hash: Ea2rTJTCaLHnfJ487BPPUdrt_VQ-bCbf4tLcMtFFUCI id: alt_text_report label: 'Alt Text Report' module: views @@ -878,6 +878,52 @@ display: title: '' operator: '=' value: '' + field_name: + id: field_name + table: alt_text_validation_audit + field: field_name + relationship: none + group_type: group + admin_label: '' + plugin_id: string + operator: '=' + value: '' + group: 1 + exposed: true + expose: + operator_id: field_name_op + label: 'Field name' + description: "The field's machine name." + use_operator: false + operator: field_name_op + operator_limit_selection: false + operator_list: { } + identifier: field_name + required: false + remember: false + multiple: false + remember_roles: + authenticated: authenticated + anonymous: '0' + administrator: '0' + api_user: '0' + alternate_api_user: '0' + editor: '0' + publisher: '0' + power_user: '0' + placeholder: '' + is_grouped: false + group_info: + label: '' + description: '' + identifier: '' + optional: true + widget: select + multiple: false + remember: false + default_group: All + default_group_multiple: { } + group_items: { } file_source: id: file_source table: alt_text_validation_audit @@ -1038,6 +1084,15 @@ display: query_tags: { } relationships: { } header: + atv_summary: + id: atv_summary + table: views + field: atv_summary + relationship: none + group_type: group + admin_label: '' + plugin_id: alt_text_validation_report_summary + empty: true result: id: result table: views diff --git a/src/AtvCommonTrait.php b/src/AtvCommonTrait.php index 961b8547cef1fe9c16462958e1f1e3844c573a7a..9b812159818464ea93ffc45c3235bfdf7d8cc374 100644 --- a/src/AtvCommonTrait.php +++ b/src/AtvCommonTrait.php @@ -11,34 +11,67 @@ trait AtvCommonTrait { /** * The name of the audit table. + * + * @return string + * The name of the db audit table. */ - public const AUDIT_TABLE = 'alt_text_validation_audit'; + public static function getAuditTableName(): string { + return 'alt_text_validation_audit'; + } + /** - * The name of the State audit status key. + * The State key for storing the audit status. + * + * @return string + * The State key for audit status. */ - public const AUDIT_STATUS_KEY = 'alt_text_validation.audit_status'; + public static function getAuditStatusKey(): string { + return 'alt_text_validation.audit_status'; + } + /** - * The name of the State audit start time key. + * The State key for storing the audit start time. + * + * @return string + * The State key for start time. */ - public const AUDIT_START_TIME_KEY = 'alt_text_validation.audit_start_time'; + public static function getAuditStartTimeKey(): string { + return 'alt_text_validation.audit_start_time'; + } + /** - * The name of the State audit end time key. + * The State key for storing the audit end time. + * + * @return string + * The State key for end time. */ - public const AUDIT_END_TIME_KEY = 'alt_text_validation.audit_end_time'; + public static function getAuditEndTimeKey(): string { + return 'alt_text_validation.audit_end_time'; + } /** * The queue name for the entity type queue. * * This is the primary queue that generates the secondary queue. + * + * @return string + * The machine name of the entity type queue. */ - public const ATV_ENTITY_TYPE_QUEUE = 'atv_entity_types'; + public static function getEntityTypeQueueName(): string { + return 'atv_entity_types'; + } /** - * The queue name for the entity instances that have image capable fields. + * The queue name for the entity type queue. + * + * This is the primary queue that generates the secondary queue. * - * This is the secondary queue and contains an entry for each entity instance. + * @return string + * The machine name of the entity type queue. */ - public const ATV_ENTITY_INSTANCE_QUEUE = 'atv_entity_instances'; + public static function getEntityInstanceQueueName(): string { + return 'atv_entity_instances'; + } /** * Checks if a field is considered an image field. diff --git a/src/Drush/Commands/AltTextValidationCommands.php b/src/Drush/Commands/AltTextValidationCommands.php index bee4147b8b019baf2d0d9bd412374af9ac37e6d8..20231183c3c4849dee641de8f61f02f7ac5477bd 100644 --- a/src/Drush/Commands/AltTextValidationCommands.php +++ b/src/Drush/Commands/AltTextValidationCommands.php @@ -101,7 +101,7 @@ final class AltTextValidationCommands extends DrushCommands implements Container public function fillAuditTest($options = ['reset' => FALSE]) { $this->auditStorage->generateTestData(); $vars = [ - '@table' => self::AUDIT_TABLE, + '@table' => self::getAuditTableName(), ]; Drush::output()->writeln($this->t('The @table has been emptied and filled with test rows.', $vars)); @@ -118,7 +118,7 @@ final class AltTextValidationCommands extends DrushCommands implements Container public function queueAudit($options = ['reset' => FALSE]) { $this->auditor->queueAllImages(); $vars = [ - '@table' => self::AUDIT_TABLE, + '@table' => self::getAuditTableName(), ]; Drush::output()->writeln($this->t('All images have been queued for auditing.', $vars)); diff --git a/src/Plugin/QueueWorker/EntityImageFieldQueuer.php b/src/Plugin/QueueWorker/EntityImageFieldQueuer.php index 16a011121d1451fd7d5229dfb6a80d92f69a27b5..a272fc98aa5455fd9d815fdc222c0d8aab004585 100644 --- a/src/Plugin/QueueWorker/EntityImageFieldQueuer.php +++ b/src/Plugin/QueueWorker/EntityImageFieldQueuer.php @@ -135,7 +135,7 @@ class EntityImageFieldQueuer extends QueueWorkerBase implements ContainerFactory $start = time(); foreach ($entity_ids as $revision => $id) { // Queue this because it could be a lot of items to process. - $this->queue->get($this->auditor::ATV_ENTITY_INSTANCE_QUEUE)->createItem([ + $this->queue->get($this->auditor::getEntityInstanceQueueName())->createItem([ 'entity_type' => $entity_type, 'bundle' => $bundle, 'id' => $id, diff --git a/src/Plugin/views/area/AtvSummary.php b/src/Plugin/views/area/AtvSummary.php new file mode 100644 index 0000000000000000000000000000000000000000..6265e7cb8174327ece13530242ccd18f4f2d9782 --- /dev/null +++ b/src/Plugin/views/area/AtvSummary.php @@ -0,0 +1,116 @@ +<?php + +namespace Drupal\alt_text_validation\Plugin\views\area; + +use Drupal\alt_text_validation\AtvCommonTrait; +use Drupal\Core\Datetime\DateFormatterInterface; +use Drupal\Core\State\State; +use Drupal\views\Attribute\ViewsArea; +use Drupal\views\Plugin\views\area\AreaPluginBase; +use Symfony\Component\DependencyInjection\ContainerInterface; + +/** + * Defines Views area plugin for the alt text validation summary. + * + * @ingroup views_area_handlers + */ +#[ViewsArea("alt_text_validation_report_summary")] +class AtvSummary extends AreaPluginBase { + + use AtvCommonTrait; + + /** + * The date formatter service. + * + * @var \Drupal\Core\Datetime\DateFormatterInterface + */ + public $dateFormatter; + + /** + * The state service. + * + * @var \Drupal\Core\State\State + */ + public $state; + + /** + * Constructs a Block plugin. + * + * @param array $configuration + * A configuration array containing information about the plugin instance. + * @param string $plugin_id + * The plugin_id for the plugin instance. + * @param mixed $plugin_definition + * The plugin implementation definition. + * @param Drupal\Core\Datetime\DateFormatterInterface $date_formatter + * The date formatter service. + * @param Drupal\Core\State\State $state + * The state service. + */ + public function __construct( + array $configuration, + $plugin_id, + $plugin_definition, + DateFormatterInterface $date_formatter, + State $state, + ) { + $this->areaType = 'header'; + parent::__construct($configuration, $plugin_id, $plugin_definition); + $this->dateFormatter = $date_formatter; + $this->state = $state; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { + return new static( + $configuration, + $plugin_id, + $plugin_definition, + $container->get('date.formatter'), + $container->get('state') + ); + } + + /** + * {@inheritdoc} + */ + public function render($empty = FALSE) { + $vars = [ + '@start' => $this->formatDate($this->state->get(self::getAuditStartTimeKey(), NULL)), + '@end' => $this->formatDate($this->state->get(self::getAuditEndTimeKey(), NULL)), + '@status' => $this->state->get(self::getAuditStatusKey(), '?'), + ]; + $status_text = $this->t('Audit status: @status', $vars); + $start_text = $this->t('Started: @start', $vars); + $end_text = $this->t('Completed: @end', $vars); + return [ + '#markup' => "<div>$status_text </br> $start_text </br> $end_text</br></div>", + ]; + } + + /** + * {@inheritdoc} + */ + public function getCacheMaxAge() { + return 0; + } + + /** + * Optionally converts a timestamp to a date and time. + * + * @param int|null $timestamp + * The timestamp to convert. + * + * @return string + * The formatted timestamp or empty string if timestamp was empty. + */ + protected function formatDate(?int $timestamp): string { + if (!empty($timestamp)) { + return $this->dateFormatter->format($timestamp, 'custom', 'm-d-Y h:i a'); + } + return ''; + } + +} diff --git a/src/Service/AuditStorage.php b/src/Service/AuditStorage.php index 8c852bd210777daafca20613f0b4f6f88d37fd95..7f37c1af651e858b848a36d24959acdb1caccf0d 100644 --- a/src/Service/AuditStorage.php +++ b/src/Service/AuditStorage.php @@ -114,7 +114,7 @@ class AuditStorage implements AuditStorageInterface, ContainerInjectionInterface array $violation_list, ): void { - $result = $this->databaseConnection->insert($this::AUDIT_TABLE) + $result = $this->databaseConnection->insert($this::getAuditTableName()) ->fields([ 'filename' => $filename, 'alt_text' => $alt_text, @@ -135,11 +135,11 @@ class AuditStorage implements AuditStorageInterface, ContainerInjectionInterface * {@inheritdoc} */ public function truncateTable(): void { - $this->databaseConnection->truncate($this::AUDIT_TABLE)->execute(); + $this->databaseConnection->truncate($this::getAuditTableName())->execute(); $states = [ - self::AUDIT_STATUS_KEY => '', - self::AUDIT_START_TIME_KEY => NULL, - self::AUDIT_END_TIME_KEY => NULL, + self::getAuditStatusKey() => '', + self::getAuditStartTimeKey() => NULL, + self::getAuditEndTimeKey() => NULL, ]; $this->state->setMultiple($states); } diff --git a/src/Service/Auditor.php b/src/Service/Auditor.php index 55d08ec1c06980a270f8c6dff8943b19fe0b2078..81aae9455f46e65b5095d4ae8045403b1c292dc2 100644 --- a/src/Service/Auditor.php +++ b/src/Service/Auditor.php @@ -150,12 +150,12 @@ class Auditor implements AuditorInterface, ContainerInjectionInterface { $this->auditStorage->truncateTable(); $this->truncateQueues(); $states = [ - self::AUDIT_STATUS_KEY => $this->t('Queueing entities with images.'), - self::AUDIT_START_TIME_KEY => time(), + self::getAuditStatusKey() => $this->t('Queueing entities with images.'), + self::getAuditStartTimeKey() => time(), ]; $this->state->setMultiple($states); $image_containing_entities = $this->collectImageContainingFields(); - $atvqueue = $this->queue->get(self::ATV_ENTITY_TYPE_QUEUE); + $atvqueue = $this->queue->get(self::getEntityTypeQueueName()); foreach ($image_containing_entities as $entity_type => $bundles) { foreach ($bundles as $bundle => $fields) { $item = new \stdClass(); @@ -172,7 +172,7 @@ class Auditor implements AuditorInterface, ContainerInjectionInterface { } $item_count = $atvqueue->numberOfItems(); $vars = [ - '@queue_name' => self::ATV_ENTITY_TYPE_QUEUE, + '@queue_name' => self::getEntityTypeQueueName(), '@count' => $item_count, ]; $this->logger->info('Queued @count entity:bundles that have fields that could support images into @queue_name.', $vars); @@ -182,8 +182,8 @@ class Auditor implements AuditorInterface, ContainerInjectionInterface { * Truncates all alt_text_validation queues. */ protected function truncateQueues(): void { - $this->queue->get(self::ATV_ENTITY_TYPE_QUEUE)->deleteQueue(); - $this->queue->get(self::ATV_ENTITY_INSTANCE_QUEUE)->deleteQueue(); + $this->queue->get(self::getEntityTypeQueueName())->deleteQueue(); + $this->queue->get(self::getEntityInstanceQueueName())->deleteQueue(); } }