Commit 9a20eda0 authored by dpi's avatar dpi
Browse files

Issue #3323157 by dpi: Move event subscriber listeners to their own services

parent ae1ff3ff
Loading
Loading
Loading
Loading
+16 −16
Original line number Diff line number Diff line
@@ -11,11 +11,9 @@ services:
      - '@stream_wrapper_manager'
      - '@file_system'

  auditfiles.not_in_database:
    class: Drupal\auditfiles\Auditor\AuditFilesNotInDatabase
  auditfiles.audit_listener:
    class: Drupal\auditfiles\AuditFilesListener
    arguments:
      - '@auditfiles.config'
      - '@auditfiles.exclusions'
      - '@database'
      - '@file_system'
      - '@file.mime_type.guesser'
@@ -24,30 +22,34 @@ services:
    tags:
      - { name: event_subscriber }

  auditfiles.not_on_server:
  auditfiles.auditor.not_in_database:
    class: Drupal\auditfiles\Auditor\AuditFilesNotInDatabase
    arguments:
      - '@auditfiles.config'
      - '@auditfiles.exclusions'
      - '@database'
      - '@file_system'

  auditfiles.auditor.not_on_server:
    class: Drupal\auditfiles\Auditor\AuditFilesNotOnServer
    arguments:
      - '@auditfiles.config'
      - '@database'
      - '@file_system'

  auditfiles.managed_not_used:
  auditfiles.auditor.managed_not_used:
    class: Drupal\auditfiles\Auditor\AuditFilesManagedNotUsed
    arguments:
      - '@auditfiles.config'
      - '@database'
    tags:
      - { name: event_subscriber }

  auditfiles.used_not_managed:
  auditfiles.auditor.used_not_managed:
    class: Drupal\auditfiles\Auditor\AuditFilesUsedNotManaged
    arguments:
      - '@auditfiles.config'
      - '@database'
    tags:
      - { name: event_subscriber }

  auditfiles.used_not_referenced:
  auditfiles.auditor.used_not_referenced:
    class: Drupal\auditfiles\Auditor\AuditFilesUsedNotReferenced
    arguments:
      - '@auditfiles.config'
@@ -56,16 +58,14 @@ services:
      - '@entity_type.manager'
      - '@file_url_generator'

  auditfiles.referenced_not_used:
  auditfiles.auditor.referenced_not_used:
    class: Drupal\auditfiles\Auditor\AuditFilesReferencedNotUsed
    arguments:
      - '@auditfiles.config'
      - '@database'
      - '@entity_field.manager'
    tags:
      - { name: event_subscriber }

  auditfiles.merge_file_references:
  auditfiles.auditor.merge_file_references:
    class: Drupal\auditfiles\Auditor\AuditFilesMergeFileReferences
    arguments:
      - '@auditfiles.config'
+229 −0
Original line number Diff line number Diff line
<?php

declare(strict_types=1);

namespace Drupal\auditfiles;

use Drupal\auditfiles\Event\AuditFilesAddFileOnDiskEvent;
use Drupal\auditfiles\Event\AuditFilesAddUsageForFileFieldReferenceEvent;
use Drupal\auditfiles\Event\AuditFilesDeleteFileEntityEvent;
use Drupal\auditfiles\Event\AuditFilesDeleteFileFieldReferenceEvent;
use Drupal\auditfiles\Event\AuditFilesDeleteFileOnDiskEvent;
use Drupal\auditfiles\Event\AuditFilesDeleteFileUsageEvent;
use Drupal\Component\Datetime\TimeInterface;
use Drupal\Core\Database\Connection;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\File\FileSystemInterface;
use Drupal\file\FileInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Mime\MimeTypeGuesserInterface;

/**
 * Default event listeners.
 *
 * To override, choose from:
 *  - Listen to events, set weight so your listener happens before this.
 *  - Alter subscribed events.
 *  - Utilize event stopPropagation
 *  - Set was* property on events to non-NULL value.
 *  - Remove this service entirely.
 *
 * @internal
 *   There is no extensibility promise for this class.
 */
final class AuditFilesListener implements EventSubscriberInterface {

  /**
   * Constructs a new AuditFilesListener.
   */
  final public function __construct(
    protected Connection $connection,
    protected FileSystemInterface $fileSystem,
    protected MimeTypeGuesserInterface $fileMimeTypeGuesser,
    protected TimeInterface $time,
    protected EntityTypeManagerInterface $entityTypeManager,
  ) {
  }

  /**
   * An event subscriber for adding a file reference to the file_usage table.
   *
   * @internal
   *   There is no extensibility promise for this method; Use events instead.
   */
  final public function listenerAddUsage(AuditFilesAddUsageForFileFieldReferenceEvent $event): void {
    if ($event->wasAdded !== NULL) {
      return;
    }

    $reference = $event->reference;

    // Make sure the file is not already in the database.
    $result = (int) $this->connection
      ->select('file_usage')
      ->condition('fid', $reference->getFileReference()->getId())
      // @todo This is hard coded for now, but need to determine how to figure out
      // which module needs to be here.
      ->condition('module', 'file')
      ->condition('type', $reference->entityTypeId)
      ->condition('id', $reference->entityId)
      ->countQuery()
      ->execute()
      ->fetchField();

    if (0 !== $result) {
      // The file is already in the file_usage table.
      $event->wasAdded = FALSE;
      return;
    }

    // The file is not already in the database, so add it.
    $this->connection
      ->insert('file_usage')
      ->fields([
        'fid' => $reference->getFileReference()->getId(),
        // @todo This is hard coded for now, but need to determine how to figure out
        // which module needs to be here.
        'module' => 'file',
        'type' => $reference->entityTypeId,
        'id' => $reference->entityId,
        'count' => 1,
      ])
      ->execute();

    $event->wasAdded = TRUE;
  }

  /**
   * An event subscriber for creating a file.
   *
   * @internal
   *   There is no extensibility promise for this method: Use events instead:
   *   set the file property in a listener with a weight before this listener.
   */
  final public function listenerCreateFile(AuditFilesAddFileOnDiskEvent $event): void {
    // Exit earlier if a file was created before this listener.
    if ($event->file !== NULL) {
      return;
    }

    $uri = $event->reference->getUri();
    $realFilenamePath = $this->fileSystem->realpath($uri);

    $file = $this->entityTypeManager
      ->getStorage('file')
      ->create();
    assert($file instanceof FileInterface);

    $file
      ->set('langcode', 'en')
      ->set('created', $this->time->getCurrentTime())
      ->setChangedTime($this->time->getCurrentTime());
    $file->setFilename(trim(basename($uri)));
    $file->setFileUri($uri);
    $file->setMimeType($this->fileMimeTypeGuesser->guessMimeType($realFilenamePath));
    $file->setSize(filesize($realFilenamePath));
    $file->setPermanent();

    $event->file = $file;
  }

  /**
   * An event subscriber for creating a file.
   *
   * @internal
   *   There is no extensibility promise for this method; Use events instead.
   */
  final public function listenerDeleteFile(AuditFilesDeleteFileOnDiskEvent $event): void {
    if ($event->wasDeleted !== NULL) {
      return;
    }

    $event->wasDeleted = $this->fileSystem->delete(
      $event->reference->getUri(),
    );
  }

  /**
   * An event subscriber for deleting a file from the file_managed table.
   *
   * @internal
   *   There is no extensibility promise for this method; Use events instead.
   */
  final public function listenerDeleteFileEntity(AuditFilesDeleteFileEntityEvent $event): void {
    if ($event->wasDeleted !== NULL) {
      return;
    }

    $event->reference->getFile()?->delete();
    $event->wasDeleted = TRUE;
  }

  /**
   * An event subscriber for deleting a file reference.
   *
   * @internal
   *   There is no extensibility promise for this method; Use events instead.
   */
  final public function listenerDeleteFileFieldReference(AuditFilesDeleteFileFieldReferenceEvent $event): void {
    if ($event->wasDeleted !== NULL) {
      return;
    }

    $reference = $event->reference;
    $affected = (int) $this->connection->delete($reference->table)
      ->condition($reference->column, $reference->getFileReference()->getId())
      ->execute();
    $event->wasDeleted = ($affected !== 0);
  }

  /**
   * An event subscriber for deleting usage for a file.
   *
   * @internal
   *   There is no extensibility promise for this method; Use events instead.
   */
  final public function listenerDeleteUsage(AuditFilesDeleteFileUsageEvent $event): void {
    if ($event->wasDeleted !== NULL) {
      return;
    }

    $event->wasDeleted = TRUE;
    $event->affectedUsages = (int) $this->connection
      ->delete('file_usage')
      ->condition('fid', $event->reference->getId())
      ->execute();
  }

  /**
   * An event subscriber for saving the file from listenerCreateFile.
   *
   * @internal
   *   There is no extensibility promise for this method: Use events instead:
   *   nullify the file property in a listener with a weight before this
   *   listener.
   */
  final public function listenerSaveFile(AuditFilesAddFileOnDiskEvent $event): void {
    if ($event->file !== NULL) {
      $event->file->save();
    }
  }

  /**
   * {@inheritdoc}
   */
  public static function getSubscribedEvents() {
    return [
      AuditFilesAddFileOnDiskEvent::class => [
        ['listenerCreateFile', 0],
        ['listenerSaveFile', -1000],
      ],
      AuditFilesAddUsageForFileFieldReferenceEvent::class => ['listenerAddUsage'],
      AuditFilesDeleteFileEntityEvent::class => ['listenerDeleteFileEntity'],
      AuditFilesDeleteFileFieldReferenceEvent::class => ['listenerDeleteFileFieldReference'],
      AuditFilesDeleteFileOnDiskEvent::class => ['listenerDeleteFile'],
      AuditFilesDeleteFileUsageEvent::class => ['listenerDeleteUsage'],
    ];
  }

}
+2 −32
Original line number Diff line number Diff line
@@ -5,26 +5,20 @@ declare(strict_types=1);
namespace Drupal\auditfiles\Auditor;

use Drupal\auditfiles\AuditFilesAuditorInterface;
use Drupal\auditfiles\Event\AuditFilesDeleteFileEntityEvent;
use Drupal\auditfiles\Reference\FileEntityReference;
use Drupal\auditfiles\Services\AuditFilesConfigInterface;
use Drupal\Core\Database\Connection;
use Drupal\Core\Messenger\MessengerTrait;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

/**
 * Service managed not used functions.
 *
 * @internal
 *   There is no extensibility promise for this class. This class may be marked
 *   as final, introducing an interface. Service decorators are recommended for
 *   extension. If extending directly, mark the original service as a service
 *   parent, and use service calls and setter injection for DI and construction
 *   as constructor is final.
 *   There is no extensibility promise for this class.
 *
 *  @template R of \Drupal\auditfiles\Reference\FileEntityReference
 */
final class AuditFilesManagedNotUsed implements AuditFilesAuditorInterface, EventSubscriberInterface {
final class AuditFilesManagedNotUsed implements AuditFilesAuditorInterface {

  use MessengerTrait;

@@ -61,28 +55,4 @@ final class AuditFilesManagedNotUsed implements AuditFilesAuditorInterface, Even
    }
  }

  /**
   * An event subscriber for deleting a file from the file_managed table.
   *
   * @internal
   *   There is no extensibility promise for this method; Use events instead.
   */
  public function listenerDeleteFileEntity(AuditFilesDeleteFileEntityEvent $event): void {
    if ($event->wasDeleted !== NULL) {
      return;
    }

    $event->reference->getFile()?->delete();
    $event->wasDeleted = TRUE;
  }

  /**
   * {@inheritdoc}
   */
  public static function getSubscribedEvents() {
    return [
      AuditFilesDeleteFileEntityEvent::class => ['listenerDeleteFileEntity'],
    ];
  }

}
+1 −5
Original line number Diff line number Diff line
@@ -21,11 +21,7 @@ use Symfony\Component\EventDispatcher\EventSubscriberInterface;
 * Define all methods that used in merge file references functionality.
 *
 * @internal
 *   There is no extensibility promise for this class. This class may be marked
 *   as final, introducing an interface. Service decorators are recommended for
 *   extension. If extending directly, mark the original service as a service
 *   parent, and use service calls and setter injection for DI and construction
 *   as constructor is final.
 *   There is no extensibility promise for this class.
 *
 * @template R of \Drupal\auditfiles\Reference\FileEntityReference
 */
+2 −93
Original line number Diff line number Diff line
@@ -5,32 +5,21 @@ declare(strict_types=1);
namespace Drupal\auditfiles\Auditor;

use Drupal\auditfiles\AuditFilesAuditorInterface;
use Drupal\auditfiles\Event\AuditFilesAddFileOnDiskEvent;
use Drupal\auditfiles\Event\AuditFilesDeleteFileOnDiskEvent;
use Drupal\auditfiles\Reference\DiskReference;
use Drupal\auditfiles\Services\AuditFilesConfigInterface;
use Drupal\auditfiles\Services\AuditFilesExclusions;
use Drupal\Component\Datetime\TimeInterface;
use Drupal\Core\Database\Connection;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\File\FileSystemInterface;
use Drupal\file\FileInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Mime\MimeTypeGuesserInterface;

/**
 * Define all methods used on Files not in database functionality.
 *
 * @internal
 *   There is no extensibility promise for this class. This class may be marked
 *   as final, introducing an interface. Service decorators are recommended for
 *   extension. If extending directly, mark the original service as a service
 *   parent, and use service calls and setter injection for DI and construction
 *   as constructor is final.
 *   There is no extensibility promise for this class.
 *
 *  @template R of \Drupal\auditfiles\Reference\DiskReference
 */
final class AuditFilesNotInDatabase implements AuditFilesAuditorInterface, EventSubscriberInterface {
final class AuditFilesNotInDatabase implements AuditFilesAuditorInterface {

  /**
   * Constructs a new AuditFilesNotInDatabase.
@@ -40,9 +29,6 @@ final class AuditFilesNotInDatabase implements AuditFilesAuditorInterface, Event
    protected AuditFilesExclusions $exclusions,
    protected Connection $connection,
    protected FileSystemInterface $fileSystem,
    protected MimeTypeGuesserInterface $fileMimeTypeGuesser,
    protected TimeInterface $time,
    protected EntityTypeManagerInterface $entityTypeManager,
  ) {
  }

@@ -68,70 +54,6 @@ final class AuditFilesNotInDatabase implements AuditFilesAuditorInterface, Event
    }
  }

  /**
   * An event subscriber for creating a file.
   *
   * @internal
   *   There is no extensibility promise for this method: Use events instead:
   *   set the file property in a listener with a weight before this listener.
   */
  final public function listenerCreateFile(AuditFilesAddFileOnDiskEvent $event): void {
    // Exit earlier if a file was created before this listener.
    if ($event->file !== NULL) {
      return;
    }

    $uri = $event->reference->getUri();
    $realFilenamePath = $this->fileSystem->realpath($uri);

    $file = $this->entityTypeManager
      ->getStorage('file')
      ->create();
    assert($file instanceof FileInterface);

    $file
      ->set('langcode', 'en')
      ->set('created', $this->time->getCurrentTime())
      ->setChangedTime($this->time->getCurrentTime());
    $file->setFilename(trim(basename($uri)));
    $file->setFileUri($uri);
    $file->setMimeType($this->fileMimeTypeGuesser->guessMimeType($realFilenamePath));
    $file->setSize(filesize($realFilenamePath));
    $file->setPermanent();

    $event->file = $file;
  }

  /**
   * An event subscriber for saving the file from listenerCreateFile.
   *
   * @internal
   *   There is no extensibility promise for this method: Use events instead:
   *   nullify the file property in a listener with a weight before this
   *   listener.
   */
  final public function listenerSaveFile(AuditFilesAddFileOnDiskEvent $event): void {
    if ($event->file !== NULL) {
      $event->file->save();
    }
  }

  /**
   * An event subscriber for creating a file.
   *
   * @internal
   *   There is no extensibility promise for this method; Use events instead.
   */
  final public function listenerDeleteFile(AuditFilesDeleteFileOnDiskEvent $event): void {
    if ($event->wasDeleted !== NULL) {
      return;
    }

    $event->wasDeleted = $this->fileSystem->delete(
      $event->reference->getUri(),
    );
  }

  /**
   * Get files for report.
   */
@@ -271,17 +193,4 @@ final class AuditFilesNotInDatabase implements AuditFilesAuditorInterface, Event
    return sprintf('%s://%s', $this->auditFilesConfig->getFileSystemPath(), $filePathname);
  }

  /**
   * {@inheritdoc}
   */
  public static function getSubscribedEvents() {
    return [
      AuditFilesAddFileOnDiskEvent::class => [
        ['listenerCreateFile', 0],
        ['listenerSaveFile', -1000],
      ],
      AuditFilesDeleteFileOnDiskEvent::class => ['listenerDeleteFile'],
    ];
  }

}
Loading