Compare commits

..

No commits in common. 'main' and '1.x' have entirely different histories.
main ... 1.x

  1. 2
      .github/workflows/lint.yml
  2. 2
      .github/workflows/semver.yml
  3. 2
      README.md
  4. 4
      composer.json
  5. 2
      dgi_fixity.info.yml
  6. 10
      dgi_fixity.module
  7. 5
      dgi_fixity.services.yml
  8. 9
      src/Commands/FixityCheck.php
  9. 1
      src/Controller/FixityCheckController.php
  10. 8
      src/Entity/FixityCheck.php
  11. 12
      src/FixityCheckBatchCheck.php
  12. 31
      src/FixityCheckService.php
  13. 5
      src/FixityCheckServiceInterface.php
  14. 6
      src/Form/SettingsForm.php
  15. 8
      src/Plugin/QueueWorker/FixityCheckWorker.php
  16. 38
      src/Plugin/QueueWorker/ProcessSourceWorker.php
  17. 7
      src/Routing/FixityCheckRouteSubscriber.php

2
.github/workflows/lint.yml

@ -3,7 +3,7 @@ name: Code Linting
on: on:
pull_request: pull_request:
branches: branches:
- main - 1.x
workflow_dispatch: workflow_dispatch:
jobs: jobs:

2
.github/workflows/semver.yml

@ -4,7 +4,7 @@ on:
pull_request_target: pull_request_target:
types: closed types: closed
branches: branches:
- main - 1.x
jobs: jobs:
update: update:
if: github.event.pull_request.merged == true if: github.event.pull_request.merged == true

2
README.md

@ -111,5 +111,5 @@ and or contact [discoverygarden].
[discoverygarden]: http://support.discoverygarden.ca [discoverygarden]: http://support.discoverygarden.ca
[filehash]: https://www.drupal.org/project/filehash [filehash]: https://www.drupal.org/project/filehash
[gplv2]: http://www.gnu.org/licenses/gpl-2.0.txt [gplv2]: http://www.gnu.org/licenses/gpl-2.0.txt
[install]: https://www.drupal.org/docs/extending-drupal/installing-modules [install]: https://drupal.org/documentation/install/modules-themes/modules-8
[CTDA: Connecticut Digital Archive]: https://lib.uconn.edu/find/connecticut-digital-archive/ [CTDA: Connecticut Digital Archive]: https://lib.uconn.edu/find/connecticut-digital-archive/

4
composer.json

@ -1,8 +1,8 @@
{ {
"name": "roblib/dgi_fixity", "name": "discoverygarden/dgi_fixity",
"type": "drupal-module", "type": "drupal-module",
"license": "GPL-2.0-or-later", "license": "GPL-2.0-or-later",
"require": { "require": {
"drupal/filehash": "^3.0" "drupal/filehash": "^2.0"
} }
} }

2
dgi_fixity.info.yml

@ -2,7 +2,7 @@ name: 'Fixity'
description: "Performs fixity checks on files." description: "Performs fixity checks on files."
type: module type: module
package: DGI package: DGI
core_version_requirement: ^9 || ^10 core_version_requirement: ^8 || ^9
configure: dgi_fixity.settings configure: dgi_fixity.settings
dependencies: dependencies:
- drupal:file - drupal:file

10
dgi_fixity.module

@ -17,7 +17,6 @@ use Drupal\Core\Mail\MailFormatHelper;
use Drupal\Core\Render\Element; use Drupal\Core\Render\Element;
use Drupal\Core\Routing\RouteMatchInterface; use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Url; use Drupal\Core\Url;
use Drupal\dgi_fixity\FixityCheckServiceInterface;
use Drupal\dgi_fixity\Form\SettingsForm; use Drupal\dgi_fixity\Form\SettingsForm;
use Drupal\file\Plugin\Field\FieldWidget\FileWidget; use Drupal\file\Plugin\Field\FieldWidget\FileWidget;
use Drupal\user\Entity\User; use Drupal\user\Entity\User;
@ -146,16 +145,15 @@ function dgi_fixity_cron() {
* Implements hook_entity_type_alter(). * Implements hook_entity_type_alter().
*/ */
function dgi_fixity_entity_type_alter(array &$entity_types) { function dgi_fixity_entity_type_alter(array &$entity_types) {
// XXX: Cannot reference dgi_fixity.fixity_check:fromEntityTypes() due to /** @var \Drupal\dgi_fixity\FixityCheckServiceInterface $fixity */
// circular dependencies, as dgi_fixity.fixity_check makes use of the $fixity = \Drupal::service('dgi_fixity.fixity_check');
// entity_type.manager that we are in the middle of trying to build. $supported_entity_types = $fixity->fromEntityTypes();
foreach (FixityCheckServiceInterface::ENTITY_TYPES as $entity_type_id) { foreach ($supported_entity_types as $entity_type_id) {
$entity_type = &$entity_types[$entity_type_id]; $entity_type = &$entity_types[$entity_type_id];
$entity_type->setLinkTemplate('fixity-audit', "/fixity/$entity_type_id/{{$entity_type_id}}"); $entity_type->setLinkTemplate('fixity-audit', "/fixity/$entity_type_id/{{$entity_type_id}}");
$entity_type->setLinkTemplate('fixity-check', "/fixity/$entity_type_id/{{$entity_type_id}}/check"); $entity_type->setLinkTemplate('fixity-check', "/fixity/$entity_type_id/{{$entity_type_id}}/check");
$entity_type->setFormClass('fixity-check', 'Drupal\dgi_fixity\Form\CheckForm'); $entity_type->setFormClass('fixity-check', 'Drupal\dgi_fixity\Form\CheckForm');
} }
unset($entity_type);
} }
/** /**

5
dgi_fixity.services.yml

@ -1,10 +1,11 @@
services: services:
logger.channel.dgi_fixity: logger.channel.dgi_fixity:
parent: logger.channel_base class: Drupal\Core\Logger\LoggerChannel
factory: logger.factory:get
arguments: ['dgi_fixity'] arguments: ['dgi_fixity']
dgi_fixity.fixity_check: dgi_fixity.fixity_check:
class: Drupal\dgi_fixity\FixityCheckService class: Drupal\dgi_fixity\FixityCheckService
arguments: ['@string_translation', '@config.factory', '@entity_type.manager', '@datetime.time', '@logger.channel.dgi_fixity', '@filehash'] arguments: ['@string_translation', '@config.factory', '@entity_type.manager', '@datetime.time', '@plugin.manager.mail', '@logger.channel.dgi_fixity', '@filehash']
dgi_fixity.route_subscriber: dgi_fixity.route_subscriber:
class: Drupal\dgi_fixity\Routing\FixityCheckRouteSubscriber class: Drupal\dgi_fixity\Routing\FixityCheckRouteSubscriber
arguments: ['@entity_type.manager', '@dgi_fixity.fixity_check'] arguments: ['@entity_type.manager', '@dgi_fixity.fixity_check']

9
src/Commands/FixityCheck.php

@ -17,6 +17,13 @@ class FixityCheck extends DrushCommands {
use StringTranslationTrait; use StringTranslationTrait;
/**
* A logger instance.
*
* @var \Psr\Log\LoggerInterface
*/
protected $logger;
/** /**
* The entity type manager. * The entity type manager.
* *
@ -50,7 +57,7 @@ class FixityCheck extends DrushCommands {
/** @var \Drupal\dgi_fixity\FixityCheckStorageInterface $storage */ /** @var \Drupal\dgi_fixity\FixityCheckStorageInterface $storage */
$storage = $this->entityTypeManager->getStorage('fixity_check'); $storage = $this->entityTypeManager->getStorage('fixity_check');
$count = $storage->countPeriodic(); $count = $storage->countPeriodic();
if ($this->io()->confirm("This will remove periodic checks on {$count} files, are you sure?", FALSE)) { if ($this->io()->confirm("This will remove periodic checks on ${count} files, are you sure?", FALSE)) {
$storage->clearPeriodic(); $storage->clearPeriodic();
} }
} }

1
src/Controller/FixityCheckController.php

@ -235,7 +235,6 @@ class FixityCheckController extends ControllerBase {
->condition('id', $fixity_check->id()) ->condition('id', $fixity_check->id())
->sort('performed', 'DESC') ->sort('performed', 'DESC')
->pager(50) ->pager(50)
->accessCheck()
->execute(); ->execute();
return array_keys($result); return array_keys($result);
} }

8
src/Entity/FixityCheck.php

@ -208,13 +208,7 @@ class FixityCheck extends ContentEntityBase implements FixityCheckInterface {
public function getFile(): ?File { public function getFile(): ?File {
/** @var \Drupal\Core\Field\EntityReferenceFieldItemList $file */ /** @var \Drupal\Core\Field\EntityReferenceFieldItemList $file */
$file = $this->file; $file = $this->file;
return $file->isEmpty() ? NULL : $file->referencedEntities()[0];
if ($file->isEmpty()) {
return NULL;
}
$referenced_entities = $file->referencedEntities();
return !empty($referenced_entities) ? reset($referenced_entities) : NULL;
} }
/** /**

12
src/FixityCheckBatchCheck.php

@ -170,7 +170,7 @@ class FixityCheckBatchCheck {
$results = &$context['results']; $results = &$context['results'];
if (!isset($sandbox['offset'])) { if (!isset($sandbox['offset'])) {
$sandbox['offset'] = 0; $sandbox['offset'] = 0;
$sandbox['count'] = $storage->countPeriodic(); $sandbox['remaining'] = $storage->countPeriodic();
$results['successful'] = 0; $results['successful'] = 0;
$results['ignored'] = 0; $results['ignored'] = 0;
$results['skipped'] = 0; $results['skipped'] = 0;
@ -179,8 +179,7 @@ class FixityCheckBatchCheck {
} }
$files = $storage->getPeriodic($sandbox['offset'], $batch_size); $files = $storage->getPeriodic($sandbox['offset'], $batch_size);
$end = min($sandbox['remaining'], $sandbox['offset'] + count($files));
$end = min($sandbox['count'], $sandbox['offset'] + count($files));
$context['message'] = \t('Processing @start to @end', [ $context['message'] = \t('Processing @start to @end', [
'@start' => $sandbox['offset'], '@start' => $sandbox['offset'],
'@end' => $end, '@end' => $end,
@ -188,7 +187,12 @@ class FixityCheckBatchCheck {
static::check($files, $force, $results); static::check($files, $force, $results);
$sandbox['offset'] = $end; $sandbox['offset'] = $end;
$context['finished'] = ($sandbox['count'] <= $end); $remaining = $storage->countPeriodic();
$progress_halted = $sandbox['remaining'] == $remaining;
$sandbox['remaining'] = $remaining;
// End when we have exhausted all inputs or progress has halted.
$context['finished'] = empty($files) || $progress_halted;
} }
/** /**

31
src/FixityCheckService.php

@ -7,6 +7,7 @@ use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Link; use Drupal\Core\Link;
use Drupal\Core\Mail\MailManagerInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait; use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\StringTranslation\TranslationInterface; use Drupal\Core\StringTranslation\TranslationInterface;
use Drupal\dgi_fixity\Entity\FixityCheck; use Drupal\dgi_fixity\Entity\FixityCheck;
@ -47,10 +48,17 @@ class FixityCheckService implements FixityCheckServiceInterface {
*/ */
protected $time; protected $time;
/**
* The mail manager service.
*
* @var \Drupal\Core\Mail\MailManagerInterface
*/
protected $mailManager;
/** /**
* The logger for this service. * The logger for this service.
* *
* @var \Psr\Log\LoggerInterface * @var Psr\Log\LoggerInterface
*/ */
protected $logger; protected $logger;
@ -64,18 +72,12 @@ class FixityCheckService implements FixityCheckServiceInterface {
/** /**
* Constructor. * Constructor.
*/ */
public function __construct( public function __construct(TranslationInterface $string_translation, ConfigFactoryInterface $config, EntityTypeManagerInterface $entity_type_manager, TimeInterface $time, MailManagerInterface $mail_manager, LoggerInterface $logger, FileHash $filehash) {
TranslationInterface $string_translation,
ConfigFactoryInterface $config,
EntityTypeManagerInterface $entity_type_manager,
TimeInterface $time,
LoggerInterface $logger,
FileHash $filehash,
) {
$this->stringTranslation = $string_translation; $this->stringTranslation = $string_translation;
$this->config = $config; $this->config = $config;
$this->entityTypeManager = $entity_type_manager; $this->entityTypeManager = $entity_type_manager;
$this->time = $time; $this->time = $time;
$this->mailManager = $mail_manager;
$this->logger = $logger; $this->logger = $logger;
$this->filehash = $filehash; $this->filehash = $filehash;
} }
@ -84,7 +86,10 @@ class FixityCheckService implements FixityCheckServiceInterface {
* {@inheritdoc} * {@inheritdoc}
*/ */
public function fromEntityTypes(): array { public function fromEntityTypes(): array {
return static::ENTITY_TYPES; return [
'media',
'file',
];
} }
/** /**
@ -218,7 +223,7 @@ class FixityCheckService implements FixityCheckServiceInterface {
// Assume success until proven untrue. // Assume success until proven untrue.
$state = FixityCheck::STATE_MATCHES; $state = FixityCheck::STATE_MATCHES;
// If column is set, only generate that hash. // If column is set, only generate that hash.
foreach ($this->filehash->getEnabledAlgorithms() as $column => $algo) { foreach ($this->filehash->algos() as $column => $algo) {
// Nothing to do if the previous checksum value is not known. // Nothing to do if the previous checksum value is not known.
if (!isset($file->{$column})) { if (!isset($file->{$column})) {
$state = FixityCheck::STATE_NO_CHECKSUM; $state = FixityCheck::STATE_NO_CHECKSUM;
@ -288,7 +293,6 @@ class FixityCheckService implements FixityCheckServiceInterface {
->condition('performed', 0, '!=') ->condition('performed', 0, '!=')
->groupBy('state') ->groupBy('state')
->aggregate('id', 'COUNT') ->aggregate('id', 'COUNT')
->accessCheck(FALSE)
->execute(); ->execute();
$failed = 0; $failed = 0;
@ -308,14 +312,12 @@ class FixityCheckService implements FixityCheckServiceInterface {
$periodic = (int) $storage->getQuery('AND') $periodic = (int) $storage->getQuery('AND')
->count('id') ->count('id')
->condition('periodic', TRUE) ->condition('periodic', TRUE)
->accessCheck(FALSE)
->execute(); ->execute();
// All checks performed ever. // All checks performed ever.
$revisions = (int) $storage->getQuery('AND') $revisions = (int) $storage->getQuery('AND')
->allRevisions() ->allRevisions()
->count('id') ->count('id')
->accessCheck(FALSE)
->execute(); ->execute();
// Checks which have exceeded the threshold and should be performed again. // Checks which have exceeded the threshold and should be performed again.
@ -324,7 +326,6 @@ class FixityCheckService implements FixityCheckServiceInterface {
->condition('periodic', TRUE) ->condition('periodic', TRUE)
->condition('performed', $threshold, '>=') ->condition('performed', $threshold, '>=')
->count('id') ->count('id')
->accessCheck(FALSE)
->execute(); ->execute();
// Up to date checks. // Up to date checks.

5
src/FixityCheckServiceInterface.php

@ -12,11 +12,6 @@ use Drupal\views\ViewExecutable;
*/ */
interface FixityCheckServiceInterface { interface FixityCheckServiceInterface {
const ENTITY_TYPES = [
'media',
'file',
];
/** /**
* A list of entity types which be converted into a fixity_check entity. * A list of entity types which be converted into a fixity_check entity.
* *

6
src/Form/SettingsForm.php

@ -141,7 +141,7 @@ class SettingsForm extends ConfigFormBase {
'#title' => $this->t('Time elapsed'), '#title' => $this->t('Time elapsed'),
'#description' => $this->t(' '#description' => $this->t('
<p>Time threshold is relative to "<em>now</em>". For example "<em>-1 month</em>" would prevent any checks that occurred less than a month ago.</p> <p>Time threshold is relative to "<em>now</em>". For example "<em>-1 month</em>" would prevent any checks that occurred less than a month ago.</p>
<p>Check <a href="https://www.php.net/manual/en/datetime.formats.php#datetime.formats.relative">Relative Formats</a> for acceptable values</p> <p>Check <a href="https://www.php.net/manual/en/datetime.formats.relative.php">Relative Formats</a> for acceptable values</p>
'), '),
'#default_value' => $config->get(static::THRESHOLD) ?: '-1 month', '#default_value' => $config->get(static::THRESHOLD) ?: '-1 month',
'#element_validate' => [ '#element_validate' => [
@ -155,7 +155,7 @@ class SettingsForm extends ConfigFormBase {
'#description' => $this->t(' '#description' => $this->t('
<p>Set how many files will be processed at once when performing a batch / cron job</p> <p>Set how many files will be processed at once when performing a batch / cron job</p>
'), '),
'#default_value' => $config->get(static::BATCH_SIZE) ?: 100, '#default_value' => 100,
], ],
]; ];
@ -209,7 +209,7 @@ class SettingsForm extends ConfigFormBase {
'#title' => $this->t('Time elapsed'), '#title' => $this->t('Time elapsed'),
'#description' => $this->t(' '#description' => $this->t('
<p>Time threshold is relative to "<em>now</em>". For example "<em>-1 week</em>" would mean a week must pass between notifications.</p> <p>Time threshold is relative to "<em>now</em>". For example "<em>-1 week</em>" would mean a week must pass between notifications.</p>
<p>Check <a href="https://www.php.net/manual/en/datetime.formats.php#datetime.formats.relative">Relative Formats</a> for acceptable values</p> <p>Check <a href="https://www.php.net/manual/en/datetime.formats.relative.php">Relative Formats</a> for acceptable values</p>
'), '),
'#default_value' => $notification_threshold, '#default_value' => $notification_threshold,
'#element_validate' => [ '#element_validate' => [

8
src/Plugin/QueueWorker/FixityCheckWorker.php

@ -4,9 +4,8 @@ namespace Drupal\dgi_fixity\Plugin\QueueWorker;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface; use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Queue\QueueWorkerBase; use Drupal\Core\Queue\QueueWorkerBase;
use Drupal\dgi_fixity\FixityCheckInterface;
use Drupal\dgi_fixity\FixityCheckServiceInterface; use Drupal\dgi_fixity\FixityCheckServiceInterface;
use Drupal\file\FileInterface; use Drupal\dgi_fixity\FixityCheckInterface;
use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\ContainerInterface;
/** /**
@ -62,13 +61,8 @@ class FixityCheckWorker extends QueueWorkerBase implements ContainerFactoryPlugi
public function processItem($data) { public function processItem($data) {
if ($data instanceof FixityCheckInterface) { if ($data instanceof FixityCheckInterface) {
/** @var \Drupal\dgi_fixity\FixityCheckInterface $data */ /** @var \Drupal\dgi_fixity\FixityCheckInterface $data */
if ($data->getFile() instanceof FileInterface) {
$this->fixity->check($data->getFile()); $this->fixity->check($data->getFile());
} }
else {
$data->setState(FixityCheckInterface::STATE_MISSING);
}
}
} }
} }

38
src/Plugin/QueueWorker/ProcessSourceWorker.php

@ -2,12 +2,9 @@
namespace Drupal\dgi_fixity\Plugin\QueueWorker; namespace Drupal\dgi_fixity\Plugin\QueueWorker;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface; use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Queue\QueueWorkerBase; use Drupal\Core\Queue\QueueWorkerBase;
use Drupal\Core\Queue\RequeueException; use Drupal\Core\Queue\RequeueException;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Session\AccountSwitcherInterface;
use Drupal\dgi_fixity\FixityCheckServiceInterface; use Drupal\dgi_fixity\FixityCheckServiceInterface;
use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\ContainerInterface;
@ -29,20 +26,6 @@ class ProcessSourceWorker extends QueueWorkerBase implements ContainerFactoryPlu
*/ */
protected $fixity; protected $fixity;
/**
* The account switcher service.
*
* @var \Drupal\Core\Session\AccountSwitcherInterface
*/
protected $accountSwitcher;
/**
* The entity type manager.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;
/** /**
* Constructs a new FixityCheckWorker instance. * Constructs a new FixityCheckWorker instance.
* *
@ -54,16 +37,10 @@ class ProcessSourceWorker extends QueueWorkerBase implements ContainerFactoryPlu
* The plugin implementation definition. * The plugin implementation definition.
* @param \Drupal\dgi_fixity\FixityCheckServiceInterface $fixity * @param \Drupal\dgi_fixity\FixityCheckServiceInterface $fixity
* The fixity check service. * The fixity check service.
* @param \Drupal\Core\Session\AccountSwitcherInterface $account_switcher
* The account switcher service.
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* The entity type manager.
*/ */
public function __construct(array $configuration, $plugin_id, $plugin_definition, FixityCheckServiceInterface $fixity, AccountSwitcherInterface $account_switcher, EntityTypeManagerInterface $entity_type_manager) { public function __construct(array $configuration, $plugin_id, $plugin_definition, FixityCheckServiceInterface $fixity) {
parent::__construct($configuration, $plugin_id, $plugin_definition); parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->fixity = $fixity; $this->fixity = $fixity;
$this->accountSwitcher = $account_switcher;
$this->entityTypeManager = $entity_type_manager;
} }
/** /**
@ -75,8 +52,6 @@ class ProcessSourceWorker extends QueueWorkerBase implements ContainerFactoryPlu
$plugin_id, $plugin_id,
$plugin_definition, $plugin_definition,
$container->get('dgi_fixity.fixity_check'), $container->get('dgi_fixity.fixity_check'),
$container->get('account_switcher'),
$container->get('entity_type.manager'),
); );
} }
@ -84,13 +59,6 @@ class ProcessSourceWorker extends QueueWorkerBase implements ContainerFactoryPlu
* {@inheritdoc} * {@inheritdoc}
*/ */
public function processItem($data) { public function processItem($data) {
// To avoid expensive access calls.
$user_storage = $this->entityTypeManager->getStorage('user');
$account = $user_storage->load(1);
if ($account instanceof AccountInterface) {
$this->accountSwitcher->switchTo($account);
/** @var \Drupal\dgi_fixity\FixityCheckServiceInterface $fixity */ /** @var \Drupal\dgi_fixity\FixityCheckServiceInterface $fixity */
$fixity = \Drupal::service('dgi_fixity.fixity_check'); $fixity = \Drupal::service('dgi_fixity.fixity_check');
$view = $fixity->source($data, 1000); $view = $fixity->source($data, 1000);
@ -104,12 +72,8 @@ class ProcessSourceWorker extends QueueWorkerBase implements ContainerFactoryPlu
} }
// Not finished processing. // Not finished processing.
if (count($view->result) !== 0) { if (count($view->result) !== 0) {
$this->accountSwitcher->switchBack();
throw new RequeueException(); throw new RequeueException();
} }
$this->accountSwitcher->switchBack();
}
} }
} }

7
src/Routing/FixityCheckRouteSubscriber.php

@ -21,13 +21,6 @@ class FixityCheckRouteSubscriber extends RouteSubscriberBase {
*/ */
protected $fixity; protected $fixity;
/**
* The entity type manager service.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected EntityTypeManagerInterface $entityTypeManager;
/** /**
* Subscriber for Fixity Check routes. * Subscriber for Fixity Check routes.
* *

Loading…
Cancel
Save