<?php
declare(strict_types=1);
namespace VioB2BLogin\Core\Framework\Subscriber;
use Shopware\Core\Framework\Context;
use Shopware\Core\Framework\DataAbstractionLayer\EntityRepository;
use Shopware\Core\Framework\DataAbstractionLayer\EntityRepositoryInterface;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
use Shopware\Core\Framework\Plugin\Event\PluginPostActivateEvent;
use Shopware\Core\Framework\Plugin\Event\PluginPostUninstallEvent;
use Shopware\Core\Framework\Plugin\Event\PluginPostUpdateEvent;
use Shopware\Core\Framework\Plugin\Event\PluginPreDeactivateEvent;
use Shopware\Core\Framework\Plugin\PluginEntity;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use VioB2BLogin\Entity\Employee\EmployeeEntity;
use VioB2BLogin\Entity\Privilege\AbstractPrivilegeProvider;
class PluginLifecycleSubscriber implements EventSubscriberInterface
{
/** @var iterable<AbstractPrivilegeProvider> */
private iterable $priviligeProviders;
private EntityRepository $privilegeRepository;
private EntityRepository $employeePrivilegeRepository;
private EntityRepositoryInterface $employeeRepository;
public function __construct(
iterable $priviligeProviders,
EntityRepository $privilegeRepository,
EntityRepositoryInterface $employeeRepository,
EntityRepository $employeePrivilegeRepository
)
{
$this->priviligeProviders = $priviligeProviders;
$this->privilegeRepository = $privilegeRepository;
$this->employeePrivilegeRepository = $employeePrivilegeRepository;
$this->employeeRepository = $employeeRepository;
}
/**
* @inheritDoc
*/
public static function getSubscribedEvents(): array
{
return [
PluginPostActivateEvent::class => 'onPostPluginActivate',
PluginPostUpdateEvent::class => 'onPostPluginUpdate',
PluginPreDeactivateEvent::class => 'onPrePluginDeactivate',
PluginPostUninstallEvent::class => 'onPostPluginUninstall',
];
}
public function onPostPluginActivate(PluginPostActivateEvent $event): void
{
$this->installACLRules($event->getContext()->getContext());
$this->activateACLRules($event->getContext()->getContext(), $event->getPlugin());
}
public function onPrePluginDeactivate(PluginPreDeactivateEvent $event): void
{
$this->deactivateACLRules($event->getContext()->getContext(), $event->getPlugin());
}
public function onPostPluginUninstall(PluginPostUninstallEvent $event): void
{
if (!$event->getContext()->keepUserData()) {
$this->deleteACLRules($event->getContext()->getContext(), $event->getPlugin());
}
}
public function onPostPluginUpdate(PluginPostUpdateEvent $event): void
{
$this->installACLRules($event->getContext()->getContext());
if( $event->getPlugin()->getActive() ) {
$this->activateACLRules($event->getContext()->getContext(), $event->getPlugin());
}
}
protected function activateACLRules(Context $context, PluginEntity $plugin): void
{
foreach ($this->priviligeProviders as $privilegeProvider) {
if ($privilegeProvider->getPlugin($context)->getId() === $plugin->getId()) {
$this->setRuleActive($context, $privilegeProvider, true);
}
}
}
protected function deactivateACLRules(Context $context, PluginEntity $plugin): void
{
foreach ($this->priviligeProviders as $privilegeProvider) {
if ($privilegeProvider->getPlugin($context)->getId() === $plugin->getId()) {
$this->setRuleActive($context, $privilegeProvider, false);
}
}
}
protected function deleteACLRules(Context $context, PluginEntity $plugin): void
{
$deletePrivileges = [];
$criteria = (new Criteria())->addFilter(new EqualsFilter('pluginId', $plugin->getId()));
$matchingPrivilege = $this->privilegeRepository->search($criteria, $context);
if ($matchingPrivilege->getTotal() > 0) {
foreach ($matchingPrivilege->getIds() as $id) {
$deletePrivileges[] = ['id' => $id];
}
}
if (!empty($deletePrivileges)) {
$this->privilegeRepository->delete($deletePrivileges, $context);
}
}
/** Install or Update all available ACL-Rules */
protected function installACLRules(Context $context): void
{
foreach ($this->priviligeProviders as $privilegeProvider) {
if ($this->checkACLRules($privilegeProvider)) {
$upsertPrivileges = [];
foreach ($privilegeProvider->getACLRules() as $namespace => $actions) {
foreach ($actions as $action) {
$privilegeData = [
'active' => false,
'namespace' => $namespace,
'name' => $action,
'pluginId' => $privilegeProvider->getPlugin($context)->getId()
];
$criteria = (new Criteria())
->addFilter(new EqualsFilter('namespace', $namespace))
->addFilter(new EqualsFilter('name', $action));
$matchingPrivilege = $this->privilegeRepository->searchIds($criteria, $context);
if ($matchingPrivilege->getTotal() !== 0) {
$privilegeData['id'] = $matchingPrivilege->firstId();
unset($privilegeData['active']);
}
$upsertPrivileges[] = $privilegeData;
}
}
if (!empty($upsertPrivileges)) {
$this->privilegeRepository->upsert($upsertPrivileges, $context);
// search in the upsert data for all entries without ID to determine the new privileges
$newPrivileges = array_filter($upsertPrivileges, function ($privilege) {
return !isset($privilege['id']);
});
if (!empty($newPrivileges)) {
$this->assignNewPrivilegesToAllEmployees($context, $newPrivileges);
}
}
}
}
}
protected function setRuleActive(Context $context, AbstractPrivilegeProvider $privilegeProvider, bool $active): void
{
if ($this->checkACLRules($privilegeProvider)) {
$activatePrivileges = [];
foreach ($privilegeProvider->getACLRules() as $namespace => $actions) {
foreach ($actions as $action) {
$criteria = (new Criteria())
->addFilter(new EqualsFilter('namespace', $namespace))
->addFilter(new EqualsFilter('name', $action));
$matchingPrivilege = $this->privilegeRepository->search($criteria, $context);
if ($matchingPrivilege->getTotal() > 0) {
foreach ($matchingPrivilege->getIds() as $id) {
$activatePrivileges[] = [
'active' => $active,
'id' => $id
];
}
}
}
}
if (!empty($activatePrivileges)) {
$this->privilegeRepository->update($activatePrivileges, $context);
}
}
}
protected function checkACLRules(AbstractPrivilegeProvider $privilegeProvider): bool
{
$aclRules = $privilegeProvider->getACLRules();
if (is_array($aclRules)) {
foreach ($aclRules as $namespace => $actions) {
if (is_string($namespace)) {
if (is_array($actions)) {
foreach ($actions as $action) {
if (!is_string($action)) {
return false;
}
}
}
} else {
return false;
}
}
} else {
return false;
}
return true;
}
private function assignNewPrivilegesToAllEmployees(Context $context, array $newPrivileges)
{
// get all employees with assigned privileges
$criteria = (new Criteria())->addAssociation('privileges');
$employees = $this->employeeRepository->search($criteria, $context);
// filter out employees without privileges
$employees = $employees->filter(function (EmployeeEntity $employee) {
return $employee->getPrivileges()->count() > 0;
});
$employeeIds = $employees->getIds();
if(empty($employeeIds)) {
return;
}
$upsertData = [];
foreach ( $newPrivileges as $newPrivilege ) {
// get the new privilege
$criteria = (new Criteria())
->addFilter(new EqualsFilter('namespace', $newPrivilege['namespace']))
->addFilter(new EqualsFilter('name', $newPrivilege['name']));
$privilegeId = $this->privilegeRepository->searchIds($criteria, $context)->firstId();
if ($privilegeId) {
// assign the new privilege to all employees
$employeePrivileges = array_values(array_map(function ($employeeId) use ($privilegeId) {
return [
'employeeId' => $employeeId,
'privilegeId' => $privilegeId
];
}, $employeeIds));
$upsertData = array_merge($upsertData, $employeePrivileges);
}
}
if (!empty($upsertData)) {
$this->employeePrivilegeRepository->upsert($upsertData, $context);
}
}
}