diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..2c184f7 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,20 @@ +root = true + +[{*.rst,*.rst.txt}] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true +indent_style = space +indent_size = 3 +max_line_length = 80 + +# MD-Files +[*.md] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true +indent_style = space +indent_size = 4 +max_line_length = 80 \ No newline at end of file diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..baf94d3 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,10 @@ +/.github/ export-ignore +/Build/ export-ignore +/Tests/ export-ignore +/.editorconfig export-ignore +/.gitattributes export-ignore +/.github/ export-ignore +/.gitignore export-ignore +/.scrutinizer export-ignore +/.styleci.yml export-ignore +/.travis.yml export-ignore \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8059388 --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +/.Build +/Build/Local/.phpunit.result.cache +/composer.lock +/.php_cs.cache +/Documentation-GENERATED-temp +/Tests/Build/.phpunit.result.cache +.DS_Store +/.idea \ No newline at end of file diff --git a/Classes/Command/CountCommand.php b/Classes/Command/CountCommand.php index ea1f1a1..09957ac 100644 --- a/Classes/Command/CountCommand.php +++ b/Classes/Command/CountCommand.php @@ -15,43 +15,26 @@ class CountCommand extends Command { - private ?LocalizationUtility $localizationUtility; - - private ?FileRepository $fileRepository; - protected function configure(): void { - $this->setDescription('Counts all videos of a media extension'); + $this->setDescription('Counts all videos of a media extension, e.g. YouTube'); $this->addOption( 'extension', null, InputOption::VALUE_REQUIRED, - 'e.g. Youtube', - '' + 'Media Extension (e.g. YouTube)', + 'YouTube' ); } - /** - * @param LocalizationUtility|null $localizationUtility - * @param FileRepository|null $fileRepository - */ public function __construct( - LocalizationUtility $localizationUtility = null, - FileRepository $fileRepository = null + protected LocalizationUtility $localizationUtility, + protected FileRepository $fileRepository ) { - $this->localizationUtility = $localizationUtility; - $this->fileRepository = $fileRepository; parent::__construct(); } - /** - * @param InputInterface $input - * @param OutputInterface $output - * @return int - * @throws \Doctrine\DBAL\Driver\Exception - * @throws \TYPO3\CMS\Core\Resource\Exception\FileDoesNotExistException - */ protected function execute(InputInterface $input, OutputInterface $output): int { $io = new SymfonyStyle($input, $output); diff --git a/Classes/Command/ReportCommand.php b/Classes/Command/ReportCommand.php index 2718115..78d75d3 100644 --- a/Classes/Command/ReportCommand.php +++ b/Classes/Command/ReportCommand.php @@ -22,17 +22,9 @@ class ReportCommand extends Command { - private ?ResourceFactory $resourceFactory; - - private ?FileRepository $fileRepository; - - private ?LocalizationUtility $localizationUtility; - - private ?EventDispatcherInterface $eventDispatcher; - protected function configure(): void { - $this->setDescription('Make a video report'); + $this->setDescription('Send report of video validation for a defined media extension (e.g. YouTube)'); $this->addOption( 'recipients', null, @@ -44,8 +36,8 @@ protected function configure(): void 'extension', null, InputOption::VALUE_REQUIRED, - 'Name of the video extension', - '' + 'Name of the media extension', + 'YouTube' ); $this->addOption( 'days', @@ -58,8 +50,8 @@ protected function configure(): void 'referencedOnly', null, InputOption::VALUE_OPTIONAL, - 'Whether to only fetch records that are referenced on visible pages and content elements (true/false)', - false + 'Whether to only fetch records that are referenced on visible pages and content elements (1/0)', + 0 ); $this->addOption( 'referenceRoot', @@ -70,36 +62,16 @@ protected function configure(): void ); } - /** - * @param ResourceFactory|null $resourceFactory - * @param FileRepository|null $fileRepository - * @param LocalizationUtility|null $localizationUtility - */ public function __construct( - ResourceFactory $resourceFactory = null, - FileRepository $fileRepository = null, - LocalizationUtility $localizationUtility = null, - EventDispatcherInterface $eventDispatcher = null + protected ResourceFactory $resourceFactory, + protected FileRepository $fileRepository, + protected LocalizationUtility $localizationUtility, + protected EventDispatcherInterface $eventDispatcher ) { - $this->resourceFactory = $resourceFactory; - $this->fileRepository = $fileRepository; - $this->localizationUtility = $localizationUtility; - $this->eventDispatcher = $eventDispatcher; parent::__construct(); } - /** - * Generates video report - * - * @param InputInterface $input - * @param OutputInterface $output - * - * @return int - * @throws FileDoesNotExistException - * @throws \Doctrine\DBAL\Driver\Exception - * @throws \Doctrine\DBAL\Exception - */ protected function execute(InputInterface $input, OutputInterface $output): int { $io = new SymfonyStyle($input, $output); @@ -164,14 +136,6 @@ protected function execute(InputInterface $input, OutputInterface $output): int return Command::SUCCESS; } - /** - * @param ValidatorDemand $validatorDemand - * @param int $status - * @return array - * @throws \Doctrine\DBAL\Driver\Exception - * @throws \TYPO3\CMS\Core\Resource\Exception\FileDoesNotExistException - * @throws \Doctrine\DBAL\Exception - */ protected function getVideosByStatus(ValidatorDemand $validatorDemand, int $status): array { $videos = []; @@ -180,7 +144,7 @@ protected function getVideosByStatus(ValidatorDemand $validatorDemand, int $stat try { $file = $this->resourceFactory->getFileObject($video['uid']); $videos[] = $file; - } catch (FileDoesNotExistException $e) { + } catch (FileDoesNotExistException) { } } diff --git a/Classes/Command/ResetCommand.php b/Classes/Command/ResetCommand.php index 610eb0c..c8988ae 100644 --- a/Classes/Command/ResetCommand.php +++ b/Classes/Command/ResetCommand.php @@ -15,43 +15,26 @@ class ResetCommand extends Command { - private ?LocalizationUtility $localizationUtility; - - private ?FileRepository $fileRepository; - protected function configure(): void { - $this->setDescription('Resets all video states of a media extension'); + $this->setDescription('Resets all videos of a media extension, e.g. YouTube'); $this->addOption( 'extension', null, InputOption::VALUE_REQUIRED, - 'e.g. Youtube', - '' + 'Media Extension (e.g. YouTube)', + 'YouTube' ); } - /** - * @param LocalizationUtility|null $localizationUtility - * @param FileRepository|null $fileRepository - */ public function __construct( - LocalizationUtility $localizationUtility = null, - FileRepository $fileRepository = null + protected LocalizationUtility $localizationUtility, + protected FileRepository $fileRepository ) { - $this->localizationUtility = $localizationUtility; - $this->fileRepository = $fileRepository; parent::__construct(); } - /** - * @param InputInterface $input - * @param OutputInterface $output - * @return int - * @throws \Doctrine\DBAL\Driver\Exception - * @throws \TYPO3\CMS\Core\Resource\Exception\FileDoesNotExistException - */ protected function execute(InputInterface $input, OutputInterface $output): int { $io = new SymfonyStyle($input, $output); diff --git a/Classes/Command/ValidatorCommand.php b/Classes/Command/ValidatorCommand.php index 1ace3fa..98138f0 100644 --- a/Classes/Command/ValidatorCommand.php +++ b/Classes/Command/ValidatorCommand.php @@ -15,19 +15,15 @@ class ValidatorCommand extends Command { - private ?LocalizationUtility $localizationUtility; - - private ?VideoService $videoService; - protected function configure(): void { - $this->setDescription('Checks online videos in TYPO3 backend for accessibility like Youtube and Vimeo'); + $this->setDescription('Video validation of a defined media extension (e.g. YouTube)'); $this->addOption( 'extension', null, InputOption::VALUE_REQUIRED, - 'e.g. Youtube', - '' + 'Media Extension (e.g. YouTube)', + 'YouTube' ); $this->addOption( 'limit', @@ -40,8 +36,8 @@ protected function configure(): void 'referencedOnly', null, InputOption::VALUE_OPTIONAL, - 'Whether to only fetch records that are referenced on visible pages and content elements (true/false)', - false + 'Whether to only fetch records that are referenced on visible pages and content elements (1/0)', + 0 ); $this->addOption( 'referenceRoot', @@ -52,30 +48,13 @@ protected function configure(): void ); } - /** - * @param LocalizationUtility|null $localizationUtility - * @param VideoService|null $videoService - */ public function __construct( - LocalizationUtility $localizationUtility = null, - VideoService $videoService = null + protected LocalizationUtility $localizationUtility, + protected VideoService $videoService ) { - $this->localizationUtility = $localizationUtility; - $this->videoService = $videoService; parent::__construct(); } - - /** - * Executes validator - * - * @param InputInterface $input - * @param OutputInterface $output - * @return int - * @throws \Doctrine\DBAL\Driver\Exception - * @throws \TYPO3\CMS\Core\Resource\Exception\FileDoesNotExistException - * @throws \Doctrine\DBAL\Exception - */ protected function execute(InputInterface $input, OutputInterface $output): int { $io = new SymfonyStyle($input, $output); diff --git a/Classes/Domain/Repository/FileRepository.php b/Classes/Domain/Repository/FileRepository.php index 665410a..524ed28 100644 --- a/Classes/Domain/Repository/FileRepository.php +++ b/Classes/Domain/Repository/FileRepository.php @@ -17,24 +17,14 @@ class FileRepository private const SYS_FILE_TABLE = 'sys_file'; private const SYS_FILE_REFERENCE_TABLE = 'sys_file_reference'; private const PAGES_TABLE = 'pages'; - private ?SiteFinder $siteFinder; private int $maxBindParameters = 999; - /** - * @param SiteFinder|null $siteFinder - */ - public function __construct(SiteFinder $siteFinder = null) + public function __construct( + protected SiteFinder $siteFinder + ) { - $this->siteFinder = $siteFinder; } - /** - * @param ValidatorDemand $validatorDemand - * @param int $validationDate - * @return array - * @throws \Doctrine\DBAL\Driver\Exception - * @throws \Doctrine\DBAL\Exception - */ public function getVideosByExtension(ValidatorDemand $validatorDemand, int $validationDate = 0): array { $referencedOnly = $validatorDemand->isReferencedOnly(); @@ -71,13 +61,6 @@ public function getVideosByExtension(ValidatorDemand $validatorDemand, int $vali return $videos; } - /** - * @param ValidatorDemand $validatorDemand - * @param int $validationStatus - * @return array - * @throws \Doctrine\DBAL\Driver\Exception - * @throws \Doctrine\DBAL\Exception - */ public function getVideosForReport(ValidatorDemand $validatorDemand, int $validationStatus = 200): array { $queryBuilder = $this->getQueryBuilder(self::SYS_FILE_TABLE); @@ -115,11 +98,7 @@ public function getVideosForReport(ValidatorDemand $validatorDemand, int $valida return $videos; } - /** - * @param string $extension - * @throws \Doctrine\DBAL\Exception - */ - public function resetValidationState(string $extension) + public function resetValidationState(string $extension): void { $queryBuilder = $this->getQueryBuilder(self::SYS_FILE_TABLE); @@ -133,15 +112,10 @@ public function resetValidationState(string $extension) ); $queryBuilder->set('validation_date', 0); $queryBuilder->set('validation_status', 0); - $queryBuilder->execute(); + $queryBuilder->executeQuery(); } - /** - * @param int $fileUid - * @param array $properties - * @throws \Doctrine\DBAL\Exception - */ - public function updatePropertiesByFile(int $fileUid, array $properties = []) + public function updatePropertiesByFile(int $fileUid, array $properties = []): void { $queryBuilder = $this->getQueryBuilder(self::SYS_FILE_TABLE); @@ -156,14 +130,9 @@ public function updatePropertiesByFile(int $fileUid, array $properties = []) foreach ($properties as $key => $value) { $queryBuilder->set($key, $value); } - $queryBuilder->execute(); + $queryBuilder->executeQuery(); } - /** - * @param string $tableName - * @return QueryBuilder - * @throws \Doctrine\DBAL\Exception - */ protected function getQueryBuilder(string $tableName = ''): QueryBuilder { $connectionPool = GeneralUtility::makeInstance(ConnectionPool::class); @@ -174,11 +143,6 @@ protected function getQueryBuilder(string $tableName = ''): QueryBuilder return $connection->createQueryBuilder(); } - /** - * @param QueryBuilder $queryBuilder - * @param string $extension - * @return array - */ protected function getDefaultWhereConstraints(QueryBuilder $queryBuilder, string $extension): array { $whereConstraints = []; @@ -193,13 +157,6 @@ protected function getDefaultWhereConstraints(QueryBuilder $queryBuilder, string return $whereConstraints; } - /** - * @param QueryBuilder $queryBuilder - * @param int $limit - * @param bool $referencedOnly - * @param array $pidList - * @return QueryBuilder - */ protected function getStatementForRepository( QueryBuilder &$queryBuilder, int $limit, @@ -240,13 +197,7 @@ protected function getStatementForRepository( return $statement; } - /** - * @param array $videos - * @param array $pidList - * @return void - * @throws \Doctrine\DBAL\Exception - */ - protected function parseVideosForReferences(array &$videos, array $pidList) + protected function parseVideosForReferences(array &$videos, array $pidList): void { // See above. We need to iterate each referenced file and check the referenced content-element to check its active state. // First query: Resolve all table names to fetch. Note that we can get multiple results for one sys_file record, i.e. @@ -269,7 +220,7 @@ protected function parseVideosForReferences(array &$videos, array $pidList) ->select('*') ->from(self::SYS_FILE_REFERENCE_TABLE) ->where(...$subConstraints); - $contentElements = $subStatement->execute()->fetchAllAssociative() ?? []; + $contentElements = $subStatement->executeQuery()->fetchAllAssociative() ?? []; $hasAnyValidReference = false; foreach ($contentElements as $contentElement) { @@ -290,7 +241,7 @@ protected function parseVideosForReferences(array &$videos, array $pidList) ->from($contentElement['tablenames']) ->where(...$ceConstraints); - $contentElementReferences = $ceStatement->execute()->fetchAllAssociative() ?? []; + $contentElementReferences = $ceStatement->executeQuery()->fetchAllAssociative() ?? []; if (count($contentElementReferences) > 0) { $hasAnyValidReference = true; // On first hit of a video reference we don't need to check any others. @@ -303,11 +254,6 @@ protected function parseVideosForReferences(array &$videos, array $pidList) } } - /** - * @param int $root - * @return array - * @throws \Doctrine\DBAL\Exception - */ protected function getPidList(int $root = 0): array { $roots = []; @@ -335,6 +281,7 @@ protected function getPidList(int $root = 0): array * @param int $depth Depth to traverse down the page tree. * @param int $begin Determines at which level in the tree to start collecting uid's. Zero means 'start right away', 1 = 'next level and out' * @return array Returns the list of pages + * * @throws \Doctrine\DBAL\Exception */ protected function getPageTreeIds(int $id, int $depth, int $begin): array @@ -349,7 +296,7 @@ protected function getPageTreeIds(int $id, int $depth, int $begin): array ->where( $queryBuilder->expr()->eq('pid', $queryBuilder->createNamedParameter($id, \PDO::PARAM_INT)) ) - ->execute(); + ->executeQuery(); $pageIds = []; while ($row = $result->fetchAssociative()) { @@ -363,14 +310,6 @@ protected function getPageTreeIds(int $id, int $depth, int $begin): array return $pageIds; } - /** - * @param QueryBuilder $queryBuilder - * @param bool $referencedOnly - * @param array $pidListChunk - * @param array $whereConstraints - * @return array - * @throws \Doctrine\DBAL\Exception - */ protected function getVideosForReportChunk( QueryBuilder $queryBuilder, bool $referencedOnly, @@ -383,7 +322,7 @@ protected function getVideosForReportChunk( ...$whereConstraints ); } - $videos = $statement->execute()->fetchAllAssociative(); + $videos = $statement->executeQuery()->fetchAllAssociative(); if ($referencedOnly && count($videos) > 0) { $this->parseVideosForReferences($videos, $pidListChunk); } @@ -391,15 +330,6 @@ protected function getVideosForReportChunk( return $videos; } - /** - * @param QueryBuilder $queryBuilder - * @param ValidatorDemand $validatorDemand - * @param bool $referencedOnly - * @param array $pidListChunk - * @param array $whereConstraints - * @return array - * @throws \Doctrine\DBAL\Exception - */ protected function getVideosByExtensionChunk( QueryBuilder $queryBuilder, ValidatorDemand $validatorDemand, @@ -418,7 +348,7 @@ protected function getVideosByExtensionChunk( ...$whereConstraints ); } - $videos = $statement->execute()->fetchAllAssociative(); + $videos = $statement->executeQuery()->fetchAllAssociative(); if ($referencedOnly && count($videos) > 0) { $this->parseVideosForReferences($videos, $pidListChunk); } diff --git a/Classes/Event/ModifyReportServiceEvent.php b/Classes/Event/ModifyReportServiceEvent.php index 8b5cd15..7023b06 100644 --- a/Classes/Event/ModifyReportServiceEvent.php +++ b/Classes/Event/ModifyReportServiceEvent.php @@ -5,27 +5,17 @@ final class ModifyReportServiceEvent { - private array $reportServices; - - /** - * @param array $reportServices - */ - public function __construct(array $reportServices = []) + public function __construct( + private array $reportServices = [] + ) { - $this->reportServices = $reportServices; } - /** - * @return array - */ public function getReportServices(): array { return $this->reportServices; } - /** - * @param array $reportServices - */ public function setReportServices(array $reportServices): void { $this->reportServices = $reportServices; diff --git a/Classes/Event/ModifyValidatorEvent.php b/Classes/Event/ModifyValidatorEvent.php index 20513cf..de2a19b 100644 --- a/Classes/Event/ModifyValidatorEvent.php +++ b/Classes/Event/ModifyValidatorEvent.php @@ -7,49 +7,28 @@ final class ModifyValidatorEvent { - private ?AbstractVideoValidatorInterface $validator; - - private string $extension; - - /** - * @param AbstractVideoValidatorInterface|null $validator - */ public function __construct( - AbstractVideoValidatorInterface $validator = null, - string $extension = '' + private ?AbstractVideoValidatorInterface $validator, + private string $extension = '' ) { - $this->validator = $validator; - $this->extension = $extension; } - /** - * @return AbstractVideoValidatorInterface|null - */ public function getValidator(): ?AbstractVideoValidatorInterface { return $this->validator; } - /** - * @param AbstractVideoValidatorInterface|null $validator - */ public function setValidator(?AbstractVideoValidatorInterface $validator): void { $this->validator = $validator; } - /** - * @return string - */ public function getExtension(): string { return $this->extension; } - /** - * @param string $extension - */ public function setExtension(string $extension): void { $this->extension = $extension; diff --git a/Classes/Service/Report/EmailReportService.php b/Classes/Service/Report/EmailReportService.php index bc9c2b3..b4b184a 100644 --- a/Classes/Service/Report/EmailReportService.php +++ b/Classes/Service/Report/EmailReportService.php @@ -18,9 +18,6 @@ class EmailReportService implements AbstractReportServiceInterface protected array $invalidVideos = []; - /** - * @throws \Symfony\Component\Mailer\Exception\TransportExceptionInterface - */ public function makeReport(): void { $subject = 'TYPO3 ' . $this->settings['extension'] . ' validation report'; @@ -37,7 +34,7 @@ public function makeReport(): void ->assign('referenceRoot', $this->settings['referenceRoot']) ->assign('invalidVideos', $this->getInvalidVideos()) ->assign('validVideos', $this->getValidVideos()); - // Fix for scheduler + // Fix for EXT:scheduler if ($GLOBALS['TYPO3_REQUEST'] ?? '' instanceof ServerRequestInterface) { $email->setRequest($GLOBALS['TYPO3_REQUEST']); } @@ -47,49 +44,31 @@ public function makeReport(): void } } - /** - * @return array - */ public function getSettings(): array { return $this->settings; } - /** - * @param array $settings - */ public function setSettings(array $settings): void { $this->settings = $settings; } - /** - * @return array - */ public function getValidVideos(): array { return $this->validVideos; } - /** - * @param array $validVideos - */ public function setValidVideos(array $validVideos): void { $this->validVideos = $validVideos; } - /** - * @return array - */ public function getInvalidVideos(): array { return $this->invalidVideos; } - /** - * @param array $invalidVideos - */ public function setInvalidVideos(array $invalidVideos): void { $this->invalidVideos = $invalidVideos; diff --git a/Classes/Service/Validator/VimeoValidator.php b/Classes/Service/Validator/VimeoValidator.php index c166d91..835f87c 100644 --- a/Classes/Service/Validator/VimeoValidator.php +++ b/Classes/Service/Validator/VimeoValidator.php @@ -12,12 +12,9 @@ class VimeoValidator extends AbstractVideoValidator implements AbstractVideoVali { private VimeoHelper $vimeoHelper; - /** - * @param string $extension - */ public function __construct(string $extension = '') { - $this->vimeoHelper = GeneralUtility::makeInstance(VimeoHelper::class, strtolower($extension)); + $this->vimeoHelper = GeneralUtility::makeInstance(VimeoHelper::class, $extension); } /** @@ -37,19 +34,11 @@ public function getOEmbedUrl(string $mediaId, string $format = 'json'): string ); } - /** - * @param File $file - * @return string - */ public function getOnlineMediaId(File $file): string { return $this->vimeoHelper->getOnlineMediaId($file); } - /** - * @param string $mediaId - * @return string - */ public function buildUrl(string $mediaId): string { return 'https://vimeo.com/' . $mediaId; diff --git a/Classes/Service/Validator/YoutubeValidator.php b/Classes/Service/Validator/YoutubeValidator.php index 4a201da..15193c1 100644 --- a/Classes/Service/Validator/YoutubeValidator.php +++ b/Classes/Service/Validator/YoutubeValidator.php @@ -8,16 +8,13 @@ use TYPO3\CMS\Core\Resource\OnlineMedia\Helpers\YouTubeHelper; use TYPO3\CMS\Core\Utility\GeneralUtility; -class YoutubeValidator extends AbstractVideoValidator implements AbstractVideoValidatorInterface +class YouTubeValidator extends AbstractVideoValidator implements AbstractVideoValidatorInterface { private YouTubeHelper $youtubeHelper; - /** - * @param string $extension - */ public function __construct(string $extension = '') { - $this->youtubeHelper = GeneralUtility::makeInstance(YouTubeHelper::class, strtolower($extension)); + $this->youtubeHelper = GeneralUtility::makeInstance(YouTubeHelper::class, $extension); } /** @@ -37,19 +34,11 @@ public function getOEmbedUrl(string $mediaId, string $format = 'json'): string ); } - /** - * @param File $file - * @return string - */ public function getOnlineMediaId(File $file): string { return $this->youtubeHelper->getOnlineMediaId($file); } - /** - * @param string $mediaId - * @return string - */ public function buildUrl(string $mediaId): string { return 'https://www.youtube.com/watch?v=' . $mediaId; diff --git a/Classes/Service/VideoService.php b/Classes/Service/VideoService.php index 6346483..57e5a45 100644 --- a/Classes/Service/VideoService.php +++ b/Classes/Service/VideoService.php @@ -9,7 +9,7 @@ use Ayacoo\VideoValidator\Event\ModifyValidatorEvent; use Ayacoo\VideoValidator\Service\Validator\AbstractVideoValidatorInterface; use Ayacoo\VideoValidator\Service\Validator\VimeoValidator; -use Ayacoo\VideoValidator\Service\Validator\YoutubeValidator; +use Ayacoo\VideoValidator\Service\Validator\YouTubeValidator; use Psr\EventDispatcher\EventDispatcherInterface; use Symfony\Component\Console\Style\SymfonyStyle; use TYPO3\CMS\Core\Resource\ResourceFactory; @@ -18,66 +18,32 @@ class VideoService { - public const STATUS_SUCCESS = 200; + final public const STATUS_SUCCESS = 200; - public const STATUS_SKIP = 410; + final public const STATUS_SKIP = 410; - public const STATUS_ERROR = 404; + final public const STATUS_ERROR = 404; - private ?SymfonyStyle $io; - - private ?EventDispatcherInterface $eventDispatcher; - - private ?FileRepository $fileRepository; - - private ?ResourceFactory $resourceFactory; - - private ?LocalizationUtility $localizationUtility; - - /** - * @param SymfonyStyle|null $symfonyStyle - * @param EventDispatcherInterface|null $eventDispatcher - * @param FileRepository|null $fileRepository - * @param ResourceFactory|null $resourceFactory - * @param LocalizationUtility|null $localizationUtility - */ public function __construct( - SymfonyStyle $symfonyStyle = null, - EventDispatcherInterface $eventDispatcher = null, - FileRepository $fileRepository = null, - ResourceFactory $resourceFactory = null, - LocalizationUtility $localizationUtility = null + private readonly EventDispatcherInterface $eventDispatcher, + private readonly FileRepository $fileRepository, + private readonly ResourceFactory $resourceFactory, + private readonly LocalizationUtility $localizationUtility, + private ?SymfonyStyle $io = null, ) { - $this->io = $symfonyStyle; - $this->eventDispatcher = $eventDispatcher; - $this->fileRepository = $fileRepository; - $this->resourceFactory = $resourceFactory; - $this->localizationUtility = $localizationUtility; } - /** - * @return SymfonyStyle|null - */ public function getIo(): ?SymfonyStyle { return $this->io; } - /** - * @param SymfonyStyle|null $io - */ public function setIo(?SymfonyStyle $io): void { $this->io = $io; } - /** - * @param ValidatorDemand $validatorDemand - * @throws \Doctrine\DBAL\Driver\Exception - * @throws \Doctrine\DBAL\Exception - * @throws \TYPO3\CMS\Core\Resource\Exception\FileDoesNotExistException - */ public function validate(ValidatorDemand $validatorDemand) { $validator = $this->getValidator($validatorDemand); @@ -91,14 +57,18 @@ public function validate(ValidatorDemand $validatorDemand) if ($numberOfVideos < 1) { $this->io->warning( sprintf( - $this->localizationUtility::translate('videoService.noVideoValidation', 'video_validator'), + $this->localizationUtility::translate( + 'videoService.noVideoValidation', 'video_validator' + ), $validatorDemand->getExtension() ) ); } elseif ($validator === null) { $this->io->error( sprintf( - $this->localizationUtility::translate('videoService.noValidatorFound', 'video_validator'), + $this->localizationUtility::translate( + 'videoService.noValidatorFound', 'video_validator' + ), $validatorDemand->getExtension() ) ); @@ -164,7 +134,7 @@ public function validate(ValidatorDemand $validatorDemand) } /** - * There is direct support for the core media extensions Youtube and Vimeo. Other media extensions can be overwritten + * There is direct support for the core media extensions YouTube and Vimeo. Other media extensions can be overwritten * via event. More about this in the README.md * * @param ValidatorDemand $validatorDemand @@ -172,17 +142,12 @@ public function validate(ValidatorDemand $validatorDemand) */ protected function getValidator(ValidatorDemand $validatorDemand): ?AbstractVideoValidatorInterface { - $extension = $validatorDemand->getExtension(); - - $validator = null; - switch ($extension) { - case 'Youtube': - $validator = GeneralUtility::makeInstance(YoutubeValidator::class, $extension); - break; - case 'Vimeo': - $validator = GeneralUtility::makeInstance(VimeoValidator::class, $extension); - break; - } + $extension = strtolower($validatorDemand->getExtension()); + $validator = match ($extension) { + 'youtube' => GeneralUtility::makeInstance(YouTubeValidator::class, $extension), + 'vimeo' => GeneralUtility::makeInstance(VimeoValidator::class, $extension) + }; + $modifyValidatorEvent = $this->eventDispatcher->dispatch( new ModifyValidatorEvent($validator, $extension) ); diff --git a/Configuration/Services.yaml b/Configuration/Services.yaml index 132b635..78c14b9 100644 --- a/Configuration/Services.yaml +++ b/Configuration/Services.yaml @@ -2,33 +2,38 @@ services: _defaults: autowire: true autoconfigure: true - public: true + public: false Ayacoo\VideoValidator\: + public: true resource: '../Classes/*' + exclude: + - '../Classes/Domain/Model/*' Ayacoo\VideoValidator\Command\ValidatorCommand: tags: - name: 'console.command' command: 'videoValidator:validate' - description: 'Video validation of a defined online service (e.g. Youtube)' + description: 'Video validation of a defined media extension (e.g. YouTube)' schedulable: true Ayacoo\VideoValidator\Command\ReportCommand: tags: - name: 'console.command' command: 'videoValidator:report' - description: 'Send report of video validation for a defined online service (e.g. Youtube)' + description: 'Send report of video validation for a defined media extension (e.g. YouTube)' schedulable: true + Ayacoo\VideoValidator\Command\ResetCommand: tags: - name: 'console.command' command: 'videoValidator:reset' - description: 'Resets all videos of a media extension, e.g. Youtube' + description: 'Resets all videos of a media extension, e.g. YouTube' schedulable: true + Ayacoo\VideoValidator\Command\CountCommand: tags: - name: 'console.command' command: 'videoValidator:count' - description: 'Counts all videos of a media extension, e.g. Youtube' - schedulable: true + description: 'Counts all videos of a media extension, e.g. YouTube' + schedulable: false diff --git a/README.md b/README.md index 54ff620..98c0267 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,8 @@ ## 1 Features -* Checks if your Youtube or Vimeo videos are still available in the TYPO3 project +* Checks if your YouTube or Vimeo videos are still available in the TYPO3 + project * Can send you reports by email * Can use a custom report service * Can also check more media extensions through flexible extensibility @@ -21,73 +22,92 @@ Run the following command within your [Composer][1] based TYPO3 project: composer require ayacoo/video-validator ``` -Do not forget to activate the extension in the extension manager and to update the database once, so that the two new +Do not forget to activate the extension in the extension manager and to update +the database once, so that the two new fields are added. -### 2.2 How it works +### 2.2 Setting up tasks / How do the tasks work together? (TYPO3 Integrator stuff) -The extension fetches all files from the sys_file that are linked to this extension based on the extension, e.g. -Youtube. Thereby a validation_date is considered. +The tasks can be set up via CLI (see Developer Corner) or directly in the +scheduler. -If the list is through, a new run begins after 7 days. +In the scheduler we create a new task `Execute console commands`. The +schedulable command `videoValidator:validate` should be set up first. +This command regularly checks YouTube and Vimeo videos and stores information +about the status and crawl time in the data set. -Using the oEmbed API of the providers, you can read the status of a video without an API key. Private videos are marked -as faulty, but cannot be saved in TYPO3 anyway. Note the difference between private videos and unlisted videos. +TODO Screenshot -### 2.3 Supported media extensions +Afterwards, we create a task 'videoValidator:report' in the same scheme. Note +the settings here and define at least one recipient. -- Youtube -- Vimeo -- Custom media extension, see doc below +TODO Screenshot -### 2.4 What do I do if a video is not accessible? +## 3 FAQ -It may happen that at some point videos are no longer accessible. These are listed in the report mail as invalid videos. -TYPO3 offers a number next to the file in the file list. If you click on it, all references to this file are listed. Now -you can take care of the corresponding corrections. +### 3.1 How it works -## 3 Administration corner +EXT:video_validator fetches all files from the sys_file table that are linked to +this media extension, e.g. +YouTube. Thereby a validation_date is considered. -### 3.1 Versions and support +If the video list has been worked through, the videos are checked again by +default after 7 days. +These settings can be overwritten for the respective task. -| Version | TYPO3 | PHP | Support / Development | -| ----------- | ---------- | ----------|---------------------------------------- | -| 2.x | 10.x - 11.x| 7.4 - 8.0 | features, bugfixes, security updates | +Using the oEmbed API of the providers, you can read the status of a video +without an API key. Private videos are marked +as faulty, but cannot be saved in TYPO3 anyway. Note the difference between +private videos and unlisted videos! -Hint: Version 1 users should update to version 2 +### 3.2 Supported media extensions -### 3.2 Release Management +- YouTube +- Vimeo +- Custom media extension, see developer doc -video_validator uses [**semantic versioning**][2], which means, that +### 3.3 What do I do if a video is not accessible? -* **bugfix updates** (e.g. 1.0.0 => 1.0.1) just includes small bugfixes or security relevant stuff without breaking - changes, -* **minor updates** (e.g. 1.0.0 => 1.1.0) includes new features and smaller tasks without breaking changes, -* and **major updates** (e.g. 1.0.0 => 2.0.0) breaking changes which can be refactorings, features or bugfixes. +It may happen that at some point videos are no longer accessible. These are +listed in the report mail as invalid videos. +TYPO3 offers a number next to the file in the file list. If you click on it, all +references to this file are listed. Now +you can take care of the corresponding corrections. -### 3.3 Contribution +## 4 Administration corner -**Pull Requests** are gladly welcome! Nevertheless please don't forget to add an issue and connect it to your pull -requests. This is very helpful to understand what kind of issue the **PR** is going to solve. +### 4.1 Versions and support -**Bugfixes**: Please describe what kind of bug your fix solve and give us feedback how to reproduce the issue. We're -going to accept only bugfixes if we can reproduce the issue. +| Version | TYPO3 | PHP | Support / Development | +|---------|-------------|-----------|---------------------------------------- | +| 3.x | 12.x | 8.1 | features, bugfixes, security updates | +| 2.x | 10.x - 11.x | 7.4 - 8.0 | bugfixes, security updates | -### 3.4 Email settings +Hint: Version 1 users should update to version 2 -To define a sender for the email, the configuration ```$GLOBALS['TYPO3_CONF_VARS']['MAIL']['defaultMailFromAddress']``` -from the Install Tool is used. +### 4.2 Release Management -Because the FluidEmail is used by TYPO3, you can of course also easily overwrite the template for the status email. +video_validator uses [**semantic versioning**][2], which means, that -## 4 Developer corner +* **bugfix updates** (e.g. 1.0.0 => 1.0.1) just includes small bugfixes or + security relevant stuff without breaking + changes, +* **minor updates** (e.g. 1.0.0 => 1.1.0) includes new features and smaller + tasks without breaking changes, +* and **major updates** (e.g. 1.0.0 => 2.0.0) breaking changes which can be + refactorings, features or bugfixes. + +## 5 Developer corner (TYPO3 Developer stuff) -### 4.1 CLI calls +### 5.1 CLI calls -The calls can be retrieved directly via CLI or you can set up corresponding jobs in the scheduler. Advantage of the -scheduler: The TYPO3 is displayed correctly in the mail: https://forge.typo3.org/issues/93940 +The calls can be retrieved directly via CLI or you can set up corresponding jobs +in the scheduler. Advantage of the +scheduler: The TYPO3 is displayed correctly in the +mail: https://forge.typo3.org/issues/93940 -It is unclear how many fast accesses in a row the oEmbed API allows as a maximum. Therefore it is better to think small +It is unclear how many fast accesses in a row the oEmbed API allows as a +maximum. Therefore, it is better to think small limits. #### videoValidator:validate @@ -104,19 +124,23 @@ Example: vendor/bin/typo3 videoValidator:validate --extension=Vimeo --limit=10 ``` -Example for fetching only videos that are referenced on visible, non-deleted pages within visible, non-deleted references: +Example for fetching only videos that are referenced on visible, non-deleted +pages within visible, non-deleted references: ``` vendor/bin/typo3 videoValidator:validate --extension=Vimeo --limit=10 --referencedOnly=1 ``` -You can specify the `--referenceRoot` option to specify a PageRoot UID where to search for references. `0` by default means all available roots. +You can specify the `--referenceRoot` option to specify a PageRoot UID where to +search for references. `0` by default means all available roots. -Pay attention to using the right upper/lowercase media extension names (`Youtube` instead of `youtube`), which are defined by the name of the Validator instance. +Pay attention to using the right upper/lowercase media extension +names (`YouTube` instead of `YouTube`), which are defined by the name of the +Validator instance. #### videoValidator:report -Create an email report of Youtube videos from the last 7 days +Create an email report of YouTube videos from the last 7 days ``` vendor/bin/typo3 videoValidator:report --days --recipients --extension --referencedOnly=0(default)|1 --referenceRoot=0(default) @@ -125,10 +149,11 @@ vendor/bin/typo3 videoValidator:report --days --recipients --extension --referen Example: ``` -vendor/bin/typo3 videoValidator:report --days=7 --recipients=receiver@example.com,receiver2@example.com --extension=Youtube +vendor/bin/typo3 videoValidator:report --days=7 --recipients=receiver@example.com,receiver2@example.com --extension=YouTube ``` -The same `referencedOnly` and `referenceRoot` options like in `videoValidator:validate` are available. +The same `referencedOnly` and `referenceRoot` options like +in `videoValidator:validate` are available. #### videoValidator:reset @@ -141,12 +166,13 @@ vendor/bin/typo3 videoValidator:reset --extension Example: ``` -vendor/bin/typo3 videoValidator:reset --extension=Youtube +vendor/bin/typo3 videoValidator:reset --extension=YouTube ``` #### videoValidator:count -Counts all videos of a media extension. This will help you to decide which limits you can work with. +Counts all videos of a media extension. This will help you to decide which +limits you can work with. ``` vendor/bin/typo3 videoValidator:count --extension @@ -155,13 +181,16 @@ vendor/bin/typo3 videoValidator:count --extension Example: ``` -vendor/bin/typo3 videoValidator:count --extension=Youtube +vendor/bin/typo3 videoValidator:count --extension=YouTube ``` -### 4.2 Register your custom validator +### 5.2 Register your custom validator -This TYPO3 extension is built in such a way that other media extensions can also be checked. For this, the media -extension must be registered in ```$GLOBALS['TYPO3_CONF_VARS']['SYS']['fal']['onlineMediaHelpers']``` and you must +EXT:video_validator is built in such a way that other media extensions can also +be checked. For this, the media +extension must be registered +in ```$GLOBALS['TYPO3_CONF_VARS']['SYS']['fal']['onlineMediaHelpers']``` and you +must register a validator via an event. As example you can use the [EXT:tiktok][4]. @@ -226,19 +255,11 @@ class TiktokValidator extends AbstractVideoValidator implements AbstractVideoVal private string $username; - /** - * @param string $extension - */ public function __construct(string $extension) { $this->tiktokHelper = GeneralUtility::makeInstance(TiktokHelper::class, $extension); } - /** - * @param string $mediaId - * @param string $format - * @return string - */ public function getOEmbedUrl(string $mediaId, string $format = 'json'): string { return sprintf( @@ -247,20 +268,12 @@ class TiktokValidator extends AbstractVideoValidator implements AbstractVideoVal ); } - /** - * @param File $file - * @return string - */ public function getOnlineMediaId(File $file): string { $this->username = $file->getProperty('tiktok_username') ?? ''; return $this->tiktokHelper->getOnlineMediaId($file); } - /** - * @param string $mediaId - * @return string - */ public function buildUrl(string $mediaId): string { return 'https://www.tiktok.com/@' . $this->username . '/' . $mediaId; @@ -269,12 +282,14 @@ class TiktokValidator extends AbstractVideoValidator implements AbstractVideoVal ``` -With the custom validator you have to pay attention to the interface, so that you have a correct structure for the +With the custom validator you have to pay attention to the interface, so that +you have a correct structure for the checks. -### 4.3 Register your custom report +### 5.3 Register your custom report -There is also the possibility to register your own report services. For example, you can export +There is also the possibility to register your own report services. For example, +you can export the video list to a XML or CSV file. Or maybe sending a slack message? ### EventListener registration @@ -351,49 +366,31 @@ class YourReportService implements AbstractReportServiceInterface // Have a look for the necessary functions // The ReportCommand gives you the video array - /** - * @return array - */ public function getSettings(): array { return $this->settings; } - /** - * @param array $settings - */ public function setSettings(array $settings): void { $this->settings = $settings; } - /** - * @return array - */ public function getValidVideos(): array { return $this->validVideos; } - /** - * @param array $validVideos - */ public function setValidVideos(array $validVideos): void { $this->validVideos = $validVideos; } - /** - * @return array - */ public function getInvalidVideos(): array { return $this->invalidVideos; } - /** - * @param array $invalidVideos - */ public function setInvalidVideos(array $invalidVideos): void { $this->invalidVideos = $invalidVideos; @@ -402,12 +399,29 @@ class YourReportService implements AbstractReportServiceInterface ``` -## 5 Thanks / Notices +### 5.4 Email settings + +To define a sender for the email, the +configuration ```$GLOBALS['TYPO3_CONF_VARS']['MAIL']['defaultMailFromAddress']``` +from the Install Tool is used. + +Because the FluidEmail is used by TYPO3, you can of course also easily overwrite +the template for the status email. -Special thanks to Georg Ringer and his [news][3] extension. A good template to build a TYPO3 extension. Here, for +## 6 Support + +If you are happy with the extension and would like to support it in any way, I would appreciate the support of social institutions. + +## 7 Thanks / Notices + +Special thanks to Georg Ringer and his [news][3] extension. A good template to +build a TYPO3 extension. Here, for example, the structure of README.md is used. -Thanks to [Garvin Hicking][5] for adding ReferencedOnly/ReferenceRoot functionality. +Thanks to [Garvin Hicking][5] for adding ReferencedOnly/ReferenceRoot +functionality. + +And thanks to all who have tested and improved this extension. [1]: https://getcomposer.org/ diff --git a/Resources/Private/Templates/Email/VideoReport.html b/Resources/Private/Templates/Email/VideoReport.html index 1fd65ee..0646843 100644 --- a/Resources/Private/Templates/Email/VideoReport.html +++ b/Resources/Private/Templates/Email/VideoReport.html @@ -20,9 +20,6 @@