Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
strategy:
matrix:
php: [ '8.2']
TYPO3: [ '12', '13' ]
TYPO3: [ '13', '14' ]
steps:
- name: Checkout repository
uses: actions/checkout@v2
Expand Down
14 changes: 7 additions & 7 deletions Build/Scripts/runTests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ Options:
- phpstan: phpstan analyze
- unit (default): PHP unit tests

-t <10|11>
-t <13|14>
Only with -s composerUpdate|acceptance|functional
TYPO3 core major version the extension is embedded in for testing.

Expand All @@ -69,9 +69,9 @@ Options:
- postgres: use postgres
- sqlite: use sqlite

-p <8.0|8.1|8.2|8.3>
-p <8.2|8.3|8.4|8.5>
Specifies the PHP minor version to be used
- 8.0 (default): use PHP 8.0
- 8.2 (default): use PHP 8.2

-e "<phpunit or codeception options>"
Only with -s acceptance|functional|unit
Expand Down Expand Up @@ -107,11 +107,11 @@ Options:
Show this help.

Examples:
# Run unit tests using PHP 8.0
# Run unit tests using PHP 8.2
./Build/Scripts/runTests.sh

# Run unit tests using PHP 8.0
./Build/Scripts/runTests.sh -p 8.0
./Build/Scripts/runTests.sh -p 8.2
EOF

# Go to the directory this script is located, so everything else is relative
Expand Down Expand Up @@ -139,7 +139,7 @@ PHP_XDEBUG_PORT=9003
EXTRA_TEST_OPTIONS=""
SCRIPT_VERBOSE=0
CGLCHECK_DRY_RUN=""
TYPO3="10"
TYPO3="13"

# Option parsing
# Reset in case getopts has been used previously in the shell
Expand All @@ -157,7 +157,7 @@ while getopts ":s:d:p:e:t:xy:nhuv" OPT; do
;;
p)
PHP_VERSION=${OPTARG}
if ! [[ ${PHP_VERSION} =~ ^(8.0|8.1|8.2|8.3)$ ]]; then
if ! [[ ${PHP_VERSION} =~ ^(8.2|8.3|8.4|8.5)$ ]]; then
INVALID_OPTIONS+=("${OPTARG}")
fi
;;
Expand Down
4 changes: 0 additions & 4 deletions Build/phpstan-baseline.neon
Original file line number Diff line number Diff line change
@@ -1,6 +1,2 @@
parameters:
ignoreErrors:
-
message: "#^Call to an undefined method TYPO3\\\\CMS\\\\Core\\\\Database\\\\Connection\\:\\:getSchemaManager\\(\\)\\.$#"
count: 1
path: ../Classes/Domain/Service/DatabaseParameterBuilder.php
64 changes: 29 additions & 35 deletions Classes/Backend/Controller/Ajax/JobController.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,54 +18,53 @@
use B13\ContentSync\Domain\Validation\ConfigurationValidator;
use B13\ContentSync\Exception;
use Psr\Http\Message\ServerRequestInterface;
use TYPO3\CMS\Backend\Attribute\AsController;
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
use TYPO3\CMS\Core\Configuration\ExtensionConfiguration;
use TYPO3\CMS\Core\Http\JsonResponse;
use TYPO3\CMS\Core\Http\Response;
use TYPO3\CMS\Core\SingletonInterface;
use TYPO3\CMS\Core\Type\ContextualFeedbackSeverity;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\View\ViewFactoryData;
use TYPO3\CMS\Core\View\ViewFactoryInterface;
use TYPO3\CMS\Extbase\Utility\LocalizationUtility;
use TYPO3\CMS\Fluid\View\StandaloneView;

class JobController implements SingletonInterface
#[AsController]
final readonly class JobController
{
protected ExtensionConfiguration $extensionConfiguration;
protected ConfigurationValidator $validator;
protected JobRepository $jobRepository;

public function __construct(
ExtensionConfiguration $extensionConfiguration,
ConfigurationValidator $validator,
JobRepository $jobRepository
) {
$this->extensionConfiguration = $extensionConfiguration;
$this->validator = $validator;
$this->jobRepository = $jobRepository;
}
private ViewFactoryInterface $viewFactory,
private ExtensionConfiguration $extensionConfiguration,
private ConfigurationValidator $validator,
private JobRepository $jobRepository,
) {}

public function create(ServerRequestInterface $request): Response
{
if (!$this->checkAccess()) {
return (new Response())->withStatus(403);
}
$configuration = (new Configuration())->fromExtensionConfiguration($this->extensionConfiguration->get('content_sync'));
$view = $this->getFluidTemplateObject('Create');
try {
$this->validator->assertValid($configuration);
$job = new Job();
$job->setConfiguration($configuration);
$this->jobRepository->add($job);
$viewFactoryData = new ViewFactoryData(
templateRootPaths: ['EXT:content_sync/Resources/Private/Templates/'],
partialRootPaths: [['EXT:content_sync/Resources/Private/Partials/']],
request: $request,
);
$view = $this->viewFactory->create($viewFactoryData);
$view->assign('job', $job);
$return = [
'flashMessage' => [
'title' => 'OK',
'message' => LocalizationUtility::translate('LLL:EXT:content_sync/Resources/Private/Language/locallang.xlf:flashMessage.job-created'),
'severity' => ContextualFeedbackSeverity::OK,
],
'content' => $view->render(),
'content' => $view->render('Ajax/Job/Create'),
];
} catch (Exception $e) {
} catch (Exception) {
$return = [
'flashMessage' => [
'title' => 'ERROR',
Expand All @@ -83,8 +82,13 @@ public function kill(ServerRequestInterface $request): Response
if (!$this->checkAccess()) {
return (new Response())->withStatus(403);
}
$view = $this->getFluidTemplateObject('Kill');
$job = $this->jobRepository->findOneLast();
$viewFactoryData = new ViewFactoryData(
templateRootPaths: ['EXT:content_sync/Resources/Private/Templates/'],
partialRootPaths: [['EXT:content_sync/Resources/Private/Partials/']],
request: $request,
);
$view = $this->viewFactory->create($viewFactoryData);
$view->assign('job', $job);
try {
$job->kill();
Expand All @@ -95,7 +99,7 @@ public function kill(ServerRequestInterface $request): Response
'message' => LocalizationUtility::translate('LLL:EXT:content_sync/Resources/Private/Language/locallang.xlf:flashMessage.job-killed'),
'severity' => ContextualFeedbackSeverity::OK,
],
'content' => $view->render(),
'content' => $view->render('Ajax/Job/Kill'),
];
} catch (Exception $e) {
$return = [
Expand All @@ -104,30 +108,20 @@ public function kill(ServerRequestInterface $request): Response
'message' => LocalizationUtility::translate('LLL:EXT:content_sync/Resources/Private/Language/locallang.xlf:flashMessage.job-not-killed'),
'severity' => ContextualFeedbackSeverity::ERROR,
],
'content' => $view->render(),
'content' => $view->render('Ajax/Job/Kill'),
];
}

return new JsonResponse($return);
}

protected function getFluidTemplateObject(string $filename): StandaloneView
private function checkAccess(): bool
{
$view = GeneralUtility::makeInstance(StandaloneView::class);
$view->setPartialRootPaths(['EXT:content_sync/Resources/Private/Partials']);
$view->setTemplateRootPaths(['EXT:content_sync/Resources/Private/Templates/Ajax/Job']);
$view->setTemplate($filename);

return $view;
return (bool)($this->getBackendUser()->getTSConfig()['options.']['enableContentSync'] ?? $this->getBackendUser()->isAdmin());
}

protected function getBackendUser(): BackendUserAuthentication
private function getBackendUser(): BackendUserAuthentication
{
return $GLOBALS['BE_USER'];
}

protected function checkAccess(): bool
{
return (bool)($this->getBackendUser()->getTSConfig()['options.']['enableContentSync'] ?? $this->getBackendUser()->isAdmin());
}
}
56 changes: 23 additions & 33 deletions Classes/Backend/ToolbarItems/JobStatusToolbarItem.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,16 @@
use TYPO3\CMS\Backend\Toolbar\ToolbarItemInterface;
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
use TYPO3\CMS\Core\Page\PageRenderer;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Fluid\View\StandaloneView;
use TYPO3\CMS\Core\View\ViewFactoryData;
use TYPO3\CMS\Core\View\ViewFactoryInterface;

class JobStatusToolbarItem implements ToolbarItemInterface
final readonly class JobStatusToolbarItem implements ToolbarItemInterface
{
protected StatusReportFactory $statusReportFactory;

public function __construct(StatusReportFactory $statusReportFactory, PageRenderer $pageRenderer)
{
$this->statusReportFactory = $statusReportFactory;
public function __construct(
private ViewFactoryInterface $viewFactory,
private StatusReportFactory $statusReportFactory,
PageRenderer $pageRenderer
) {
$pageRenderer->loadJavaScriptModule('@b13/content-sync/content-sync.js');
}

Expand All @@ -36,8 +36,13 @@ public function checkAccess(): bool

public function getItem(): string
{
$view = $this->getFluidTemplateObject('JobStatusToolbarItem.html');
return $view->render();
$viewFactoryData = new ViewFactoryData(
templateRootPaths: ['EXT:content_sync/Resources/Private/Templates/'],
partialRootPaths: ['EXT:backend/Resources/Private/Partials/ToolbarItems', 'EXT:content_sync/Resources/Private/Partials'],
layoutRootPaths: ['EXT:backend/Resources/Private/Layouts'],
);
$view = $this->viewFactory->create($viewFactoryData);
return $view->render('ToolbarItems/JobStatusToolbarItem');
}
public function hasDropDown(): bool
{
Expand All @@ -46,10 +51,15 @@ public function hasDropDown(): bool

public function getDropDown(): string
{
$view = $this->getFluidTemplateObject('JobStatusToolbarItemDropDown.html');
$statusReport = $this->statusReportFactory->build();
$viewFactoryData = new ViewFactoryData(
templateRootPaths: ['EXT:content_sync/Resources/Private/Templates/'],
partialRootPaths: ['EXT:backend/Resources/Private/Partials/ToolbarItems', 'EXT:content_sync/Resources/Private/Partials'],
layoutRootPaths: ['EXT:backend/Resources/Private/Layouts'],
);
$view = $this->viewFactory->create($viewFactoryData);
$view->assign('statusReport', $statusReport);
return $view->render();
return $view->render('ToolbarItems/JobStatusToolbarItemDropDown');
}

public function getAdditionalAttributes(): array
Expand All @@ -62,27 +72,7 @@ public function getIndex(): int
return 50;
}

/**
* Returns a new standalone view, shorthand function
*
* @param string $filename Which templateFile should be used.
* @return StandaloneView
*/
protected function getFluidTemplateObject(string $filename): StandaloneView
{
$view = GeneralUtility::makeInstance(StandaloneView::class);
$view->setLayoutRootPaths(['EXT:backend/Resources/Private/Layouts']);
$view->setPartialRootPaths(['EXT:backend/Resources/Private/Partials/ToolbarItems', 'EXT:content_sync/Resources/Private/Partials']);

$templateRootPaths = ['EXT:content_sync/Resources/Private/Templates/ToolbarItems'];

$view->setTemplateRootPaths($templateRootPaths);
$view->setTemplate($filename);

return $view;
}

protected function getBackendUser(): BackendUserAuthentication
private function getBackendUser(): BackendUserAuthentication
{
return $GLOBALS['BE_USER'];
}
Expand Down
14 changes: 2 additions & 12 deletions Classes/Command/CollectGarbageCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,32 +17,22 @@
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

class CollectGarbageCommand extends Command
final class CollectGarbageCommand extends Command
{
protected JobRepository $jobRepository;

public function __construct(
JobRepository $jobRepository,
private readonly JobRepository $jobRepository,
string $name = null
) {
parent::__construct($name);
$this->jobRepository = $jobRepository;
}

/**
* @param InputInterface $input
* @param OutputInterface $output
* @return int
*/
public function execute(InputInterface $input, OutputInterface $output): int
{
$staleJobs = $this->jobRepository->findStaleJobs();

foreach ($staleJobs as $job) {
$job->fail('job too old');
$this->jobRepository->updateJob($job);
}

return 0;
}
}
20 changes: 4 additions & 16 deletions Classes/Command/JobCreatorCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,29 +21,17 @@
use Symfony\Component\Console\Output\OutputInterface;
use TYPO3\CMS\Core\Configuration\ExtensionConfiguration;

class JobCreatorCommand extends Command
final class JobCreatorCommand extends Command
{
protected ExtensionConfiguration $extensionConfiguration;
protected ConfigurationValidator $validator;
protected JobRepository $jobRepository;

public function __construct(
ExtensionConfiguration $extensionConfiguration,
ConfigurationValidator $validator,
JobRepository $jobRepository,
private readonly ExtensionConfiguration $extensionConfiguration,
private readonly ConfigurationValidator $validator,
private readonly JobRepository $jobRepository,
string $name = null
) {
parent::__construct($name);
$this->extensionConfiguration = $extensionConfiguration;
$this->validator = $validator;
$this->jobRepository = $jobRepository;
}

/**
* @param InputInterface $input
* @param OutputInterface $output
* @return int
*/
public function execute(InputInterface $input, OutputInterface $output): int
{
$configuration = (new Configuration())->fromExtensionConfiguration($this->extensionConfiguration->get('content_sync'));
Expand Down
12 changes: 2 additions & 10 deletions Classes/Command/JobKillerCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,23 +17,15 @@
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

class JobKillerCommand extends Command
final class JobKillerCommand extends Command
{
protected JobRepository $jobRepository;

public function __construct(
JobRepository $jobRepository,
private readonly JobRepository $jobRepository,
string $name = null
) {
parent::__construct($name);
$this->jobRepository = $jobRepository;
}

/**
* @param InputInterface $input
* @param OutputInterface $output
* @return int
*/
public function execute(InputInterface $input, OutputInterface $output): int
{
$job = $this->jobRepository->findOneLast();
Expand Down
Loading