-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[WIP][FEATURE] Synchronization status
- Loading branch information
1 parent
c0c70f2
commit a908f80
Showing
9 changed files
with
371 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace AawTeam\BackendRoles\Imaging; | ||
|
||
/* | ||
* Copyright by Agentur am Wasser | Maeder & Partner AG | ||
* | ||
* For the full copyright and license information, please read the | ||
* LICENSE file that was distributed with this source code. | ||
* | ||
* The TYPO3 project - inspiring people to share! | ||
*/ | ||
|
||
use AawTeam\BackendRoles\Role\SynchronizationStatus; | ||
use AawTeam\BackendRoles\Role\SynchronizationStatusFactoryInterface; | ||
use TYPO3\CMS\Core\Utility\MathUtility; | ||
|
||
/** | ||
* IconHandler | ||
*/ | ||
class IconHandler | ||
{ | ||
public function __construct( | ||
protected SynchronizationStatusFactoryInterface $synchronizationStatusFactory | ||
) {} | ||
|
||
/** | ||
* Implementation of IconFactory hook: | ||
* | ||
* $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS'][\TYPO3\CMS\Core\Imaging\IconFactory::class]['overrideIconOverlay'] | ||
* | ||
* @param string $table | ||
* @param array $row | ||
* @param array $status | ||
* @param string $iconName | ||
* @return string | ||
*/ | ||
public function postOverlayPriorityLookup(string $table, array $row, array $status, string $iconName): string | ||
{ | ||
if ($table !== 'be_groups') { | ||
return $iconName; | ||
} | ||
if (!array_key_exists('uid', $row) || !MathUtility::canBeInterpretedAsInteger($row['uid'])) { | ||
return $iconName; | ||
} | ||
|
||
$syncStatus = $this->synchronizationStatusFactory->createFromBackendGroupUid((int)$row['uid']); | ||
|
||
// Return incoming value ($iconName) when null was returned by mapping | ||
return $this->mapSynchronizationStatusToIconOverlayName($syncStatus) ?? $iconName; | ||
} | ||
|
||
/** | ||
* @todo register our own icons | ||
*/ | ||
protected function mapSynchronizationStatusToIconOverlayName(SynchronizationStatus $syncStatus): ?string | ||
{ | ||
if ($syncStatus->isOutOfSync()) { | ||
return 'overlay-warning'; | ||
} | ||
if ($syncStatus->isSyncOk()) { | ||
return 'overlay-approved'; | ||
} | ||
|
||
// Default: return null | ||
return null; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace AawTeam\BackendRoles\Role; | ||
|
||
/* | ||
* Copyright by Agentur am Wasser | Maeder & Partner AG | ||
* | ||
* For the full copyright and license information, please read the | ||
* LICENSE file that was distributed with this source code. | ||
* | ||
* The TYPO3 project - inspiring people to share! | ||
*/ | ||
|
||
/** | ||
* SynchronizationStatus | ||
*/ | ||
final class SynchronizationStatus | ||
{ | ||
public const NOT_SYNCED = 0; | ||
public const OUT_OF_SYNC = 1; | ||
public const SYNC_OK = 2; | ||
|
||
public function __construct(public readonly int $status) | ||
{ | ||
if (!in_array($status, [self::NOT_SYNCED, self::OUT_OF_SYNC, self::SYNC_OK])) { | ||
throw new \InvalidArgumentException('Invalid status: ' . $status); | ||
} | ||
} | ||
|
||
public function isSynced(): bool | ||
{ | ||
return $this->status !== self::NOT_SYNCED; | ||
} | ||
|
||
public function isOutOfSync(): bool | ||
{ | ||
return $this->status === self::OUT_OF_SYNC; | ||
} | ||
|
||
public function isSyncOk(): bool | ||
{ | ||
return $this->status === self::SYNC_OK; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace AawTeam\BackendRoles\Role; | ||
|
||
/* | ||
* Copyright by Agentur am Wasser | Maeder & Partner AG | ||
* | ||
* For the full copyright and license information, please read the | ||
* LICENSE file that was distributed with this source code. | ||
* | ||
* The TYPO3 project - inspiring people to share! | ||
*/ | ||
|
||
use AawTeam\BackendRoles\Role\Definition\Formatter; | ||
use AawTeam\BackendRoles\Role\Definition\Loader; | ||
use TYPO3\CMS\Backend\Utility\BackendUtility; | ||
|
||
/** | ||
* SynchronizationStatusFactory | ||
*/ | ||
class SynchronizationStatusFactory implements SynchronizationStatusFactoryInterface | ||
{ | ||
public function __construct( | ||
protected Loader $loader, | ||
protected readonly Formatter $formatter, | ||
protected DefinitionFactory $definitionFactory | ||
) {} | ||
|
||
public function createFromBackendGroupUid(int $backendGroupuid): SynchronizationStatus | ||
{ | ||
$backendGroupRecord = BackendUtility::getRecord('be_groups', $backendGroupuid); | ||
if ($backendGroupRecord === null) { | ||
throw new \RuntimeException('Cannot load be_groups record with uid ' . $backendGroupuid, 1708168047); | ||
} | ||
return $this->createFromBackendGroupRecord($backendGroupRecord); | ||
} | ||
|
||
/** | ||
* @param mixed[] $backendGroupRecord | ||
* @return SynchronizationStatus | ||
*/ | ||
public function createFromBackendGroupRecord(array $backendGroupRecord): SynchronizationStatus | ||
{ | ||
$identifier = $this->getIdentifierFromBackendGroupRecord($backendGroupRecord); | ||
|
||
// Not synchronized | ||
if ($identifier === '') { | ||
return new SynchronizationStatus(SynchronizationStatus::NOT_SYNCED); | ||
} | ||
$definitions = $this->loader->getRoleDefinitions(); | ||
if (!$definitions->offsetExists($identifier)) { | ||
return new SynchronizationStatus(SynchronizationStatus::NOT_SYNCED); | ||
} | ||
|
||
// be_groups record is synchronized: load definitions | ||
/** @var Definition $definitionFromConfiguration */ | ||
$definitionFromConfiguration = $definitions->offsetGet($identifier); | ||
$definitionFromDatabase = $this->definitionFactory->create( | ||
array_merge( | ||
[ | ||
'identifier' => $definitionFromConfiguration->getIdentifier(), | ||
'title' => $definitionFromConfiguration->getTitle(), | ||
], | ||
$this->formatter->formatFromDbToArray($backendGroupRecord) | ||
) | ||
); | ||
|
||
// Compare the definitions | ||
$status = SynchronizationStatus::OUT_OF_SYNC; | ||
if ($this->areRoleDefinitionsEqual($definitionFromConfiguration, $definitionFromDatabase)) { | ||
$status = SynchronizationStatus::SYNC_OK; | ||
} | ||
|
||
return new SynchronizationStatus($status); | ||
} | ||
|
||
protected function areRoleDefinitionsEqual(Definition $definition1, Definition $definition2): bool | ||
{ | ||
$a = $this->formatter->formatForDatabase($definition1); | ||
$b = $this->formatter->formatForDatabase($definition2); | ||
return array_diff_assoc($a, $b) === []; | ||
} | ||
|
||
/** | ||
* @param mixed[] $backendGroupRecord | ||
* @return string | ||
*/ | ||
protected function getIdentifierFromBackendGroupRecord(array $backendGroupRecord): string | ||
{ | ||
// Input validation | ||
if (!array_key_exists('tx_backendroles_role_identifier', $backendGroupRecord)) { | ||
throw new \InvalidArgumentException('No field "tx_backendroles_role_identifier" found in $backendGroupRecord', 1708168285); | ||
} | ||
if (!is_string($backendGroupRecord['tx_backendroles_role_identifier'])) { | ||
throw new \InvalidArgumentException('Field "tx_backendroles_role_identifier" in $backendGroupRecord must be string', 1708168332); | ||
} | ||
|
||
return $backendGroupRecord['tx_backendroles_role_identifier']; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace AawTeam\BackendRoles\Role; | ||
|
||
/* | ||
* Copyright by Agentur am Wasser | Maeder & Partner AG | ||
* | ||
* For the full copyright and license information, please read the | ||
* LICENSE file that was distributed with this source code. | ||
* | ||
* The TYPO3 project - inspiring people to share! | ||
*/ | ||
|
||
/** | ||
* SynchronizationStatusFactoryInterface | ||
*/ | ||
interface SynchronizationStatusFactoryInterface | ||
{ | ||
/** | ||
* @param int $backendGroupuid | ||
* @return SynchronizationStatus | ||
*/ | ||
public function createFromBackendGroupUid(int $backendGroupuid): SynchronizationStatus; | ||
|
||
/** | ||
* @param array $backendGroupRecord | ||
* @return SynchronizationStatus | ||
*/ | ||
public function createFromBackendGroupRecord(array $backendGroupRecord): SynchronizationStatus; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace AawTeam\BackendRoles\Tests\Unit\Role; | ||
|
||
/* | ||
* Copyright by Agentur am Wasser | Maeder & Partner AG | ||
* | ||
* For the full copyright and license information, please read the | ||
* LICENSE file that was distributed with this source code. | ||
* | ||
* The TYPO3 project - inspiring people to share! | ||
*/ | ||
|
||
use AawTeam\BackendRoles\Role\SynchronizationStatus; | ||
use PHPUnit\Framework\Attributes\DataProvider; | ||
use PHPUnit\Framework\Attributes\Test; | ||
use TYPO3\TestingFramework\Core\Unit\UnitTestCase; | ||
|
||
/** | ||
* SynchronizationStatusTest | ||
*/ | ||
class SynchronizationStatusTest extends UnitTestCase | ||
{ | ||
#[Test] | ||
public function constructorAcceptsKnownStatusValue(): void | ||
{ | ||
self::assertInstanceOf( | ||
SynchronizationStatus::class, | ||
new SynchronizationStatus(SynchronizationStatus::NOT_SYNCED) | ||
); | ||
self::assertInstanceOf( | ||
SynchronizationStatus::class, | ||
new SynchronizationStatus(SynchronizationStatus::OUT_OF_SYNC) | ||
); | ||
self::assertInstanceOf( | ||
SynchronizationStatus::class, | ||
new SynchronizationStatus(SynchronizationStatus::SYNC_OK) | ||
); | ||
} | ||
|
||
#[Test] | ||
#[DataProvider('unknownConstructorStatesDataProvider')] | ||
public function constructorFailsWithUnknownStatusValue(int $status): void | ||
{ | ||
self::expectException(\InvalidArgumentException::class); | ||
new SynchronizationStatus($status); | ||
} | ||
|
||
#[Test] | ||
public function statusConstantIsInterpretedCorrectly(): void | ||
{ | ||
// NOT_SYNCED | ||
self::assertFalse( | ||
(new SynchronizationStatus(SynchronizationStatus::NOT_SYNCED))->isSynced() | ||
); | ||
self::assertFalse( | ||
(new SynchronizationStatus(SynchronizationStatus::NOT_SYNCED))->isOutOfSync() | ||
); | ||
self::assertFalse( | ||
(new SynchronizationStatus(SynchronizationStatus::NOT_SYNCED))->isSyncOk() | ||
); | ||
|
||
// OUT_OF_SYNC | ||
self::assertTrue( | ||
(new SynchronizationStatus(SynchronizationStatus::OUT_OF_SYNC))->isSynced() | ||
); | ||
self::assertTrue( | ||
(new SynchronizationStatus(SynchronizationStatus::OUT_OF_SYNC))->isOutOfSync() | ||
); | ||
self::assertFalse( | ||
(new SynchronizationStatus(SynchronizationStatus::OUT_OF_SYNC))->isSyncOk() | ||
); | ||
|
||
// SYNC_OK | ||
self::assertTrue( | ||
(new SynchronizationStatus(SynchronizationStatus::SYNC_OK))->isSynced() | ||
); | ||
self::assertFalse( | ||
(new SynchronizationStatus(SynchronizationStatus::SYNC_OK))->isOutOfSync() | ||
); | ||
self::assertTrue( | ||
(new SynchronizationStatus(SynchronizationStatus::SYNC_OK))->isSyncOk() | ||
); | ||
} | ||
|
||
/** | ||
* @return mixed[] | ||
*/ | ||
public static function unknownConstructorStatesDataProvider(): array | ||
{ | ||
return [ | ||
'negative-status' => [-2], | ||
'one-below-known' => [-1], | ||
'one-above-known' => [3], | ||
'positive-status' => [4], | ||
]; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,5 @@ | ||
# cat=basic/enable/001; type=boolean; label=Hide managed columns of be_groups records in TCA | ||
hideManagedBackendUserGroupColumnns = 1 | ||
|
||
# cat=basic/enable/002; type=boolean; label=Show/visualize the synchronization status of be_groups records | ||
showSynchronizationStatus = 1 |
Oops, something went wrong.