src/Core/Framework/Update/Api/UpdateController.php line 53

  1. <?php declare(strict_types=1);
  2. namespace Shopware\Core\Framework\Update\Api;
  3. use Shopware\Core\Framework\Context;
  4. use Shopware\Core\Framework\Log\Package;
  5. use Shopware\Core\Framework\Plugin\KernelPluginLoader\StaticKernelPluginLoader;
  6. use Shopware\Core\Framework\Store\Services\AbstractExtensionLifecycle;
  7. use Shopware\Core\Framework\Update\Checkers\LicenseCheck;
  8. use Shopware\Core\Framework\Update\Checkers\WriteableCheck;
  9. use Shopware\Core\Framework\Update\Event\UpdatePostPrepareEvent;
  10. use Shopware\Core\Framework\Update\Event\UpdatePrePrepareEvent;
  11. use Shopware\Core\Framework\Update\Services\ApiClient;
  12. use Shopware\Core\Framework\Update\Services\ExtensionCompatibility;
  13. use Shopware\Core\Framework\Update\Steps\DeactivateExtensionsStep;
  14. use Shopware\Core\Kernel;
  15. use Shopware\Core\System\SalesChannel\NoContentResponse;
  16. use Shopware\Core\System\SystemConfig\SystemConfigService;
  17. use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
  18. use Symfony\Component\DependencyInjection\ContainerInterface;
  19. use Symfony\Component\EventDispatcher\EventDispatcherInterface;
  20. use Symfony\Component\HttpFoundation\JsonResponse;
  21. use Symfony\Component\HttpFoundation\Request;
  22. use Symfony\Component\HttpFoundation\Response;
  23. use Symfony\Component\Routing\Annotation\Route;
  24. /**
  25.  * @internal
  26.  */
  27. #[Route(defaults: ['_routeScope' => ['api']])]
  28. #[Package('system-settings')]
  29. class UpdateController extends AbstractController
  30. {
  31.     public const UPDATE_PREVIOUS_VERSION_KEY 'core.update.previousVersion';
  32.     /**
  33.      * @internal
  34.      */
  35.     public function __construct(
  36.         private readonly ApiClient $apiClient,
  37.         private readonly WriteableCheck $writeableCheck,
  38.         private readonly LicenseCheck $licenseCheck,
  39.         private readonly ExtensionCompatibility $extensionCompatibility,
  40.         private readonly EventDispatcherInterface $eventDispatcher,
  41.         private readonly SystemConfigService $systemConfig,
  42.         private readonly AbstractExtensionLifecycle $extensionLifecycleService,
  43.         private readonly string $shopwareVersion,
  44.         private readonly bool $disableUpdateCheck false
  45.     ) {
  46.     }
  47.     #[Route(path'/api/_action/update/check'name'api.custom.updateapi.check'defaults: ['_acl' => ['system:core:update']], methods: ['GET'])]
  48.     public function updateApiCheck(): JsonResponse
  49.     {
  50.         if ($this->disableUpdateCheck) {
  51.             return new JsonResponse();
  52.         }
  53.         $updates $this->apiClient->checkForUpdates();
  54.         if (version_compare($this->shopwareVersion$updates->version'>')) {
  55.             return new JsonResponse();
  56.         }
  57.         return new JsonResponse($updates);
  58.     }
  59.     #[Route(path'/api/_action/update/check-requirements'name'api.custom.update.check_requirements'defaults: ['_acl' => ['system:core:update']], methods: ['GET'])]
  60.     public function checkRequirements(): JsonResponse
  61.     {
  62.         return new JsonResponse([
  63.             $this->writeableCheck->check(),
  64.             $this->licenseCheck->check(),
  65.         ]);
  66.     }
  67.     #[Route('/api/_action/update/extension-compatibility'name'api.custom.updateapi.extension_compatibility'defaults: ['_acl' => ['system:core:update''system_config:read']], methods: ['GET'])]
  68.     public function extensionCompatibility(Context $context): JsonResponse
  69.     {
  70.         $update $this->apiClient->checkForUpdates();
  71.         return new JsonResponse($this->extensionCompatibility->getExtensionCompatibilities($update$context));
  72.     }
  73.     #[Route(path'/api/_action/update/download-recovery'name'api.custom.updateapi.download-recovery'defaults: ['_acl' => ['system:core:update''system_config:read']], methods: ['GET'])]
  74.     public function downloadLatestRecovery(): Response
  75.     {
  76.         $this->apiClient->downloadRecoveryTool();
  77.         return new NoContentResponse();
  78.     }
  79.     #[Route(path'/api/_action/update/deactivate-plugins'name'api.custom.updateapi.deactivate-plugins'defaults: ['_acl' => ['system:core:update''system_config:read']], methods: ['GET'])]
  80.     public function deactivatePlugins(Request $requestContext $context): JsonResponse
  81.     {
  82.         $update $this->apiClient->checkForUpdates();
  83.         $offset $request->query->getInt('offset');
  84.         if ($offset === 0) {
  85.             // plugins can subscribe to these events, check compatibility and throw exceptions to prevent the update
  86.             $this->eventDispatcher->dispatch(
  87.                 new UpdatePrePrepareEvent($context$this->shopwareVersion$update->version)
  88.             );
  89.         }
  90.         // disable plugins - save active plugins
  91.         $deactivationFilter = (string) $request->query->get(
  92.             'deactivationFilter',
  93.             ExtensionCompatibility::PLUGIN_DEACTIVATION_FILTER_NOT_COMPATIBLE
  94.         );
  95.         $deactivatePluginStep = new DeactivateExtensionsStep(
  96.             $update,
  97.             $deactivationFilter,
  98.             $this->extensionCompatibility,
  99.             $this->extensionLifecycleService,
  100.             $this->systemConfig,
  101.             $context
  102.         );
  103.         $result $deactivatePluginStep->run($offset);
  104.         if ($result->getOffset() === $result->getTotal()) {
  105.             $containerWithoutPlugins $this->rebootKernelWithoutPlugins();
  106.             // @internal plugins are deactivated
  107.             $containerWithoutPlugins->get('event_dispatcher')->dispatch(
  108.                 new UpdatePostPrepareEvent($context$this->shopwareVersion$update->version)
  109.             );
  110.         }
  111.         return new JsonResponse([
  112.             'offset' => $result->getOffset(),
  113.             'total' => $result->getTotal(),
  114.         ]);
  115.     }
  116.     private function rebootKernelWithoutPlugins(): ContainerInterface
  117.     {
  118.         /** @var Kernel $kernel */
  119.         $kernel $this->container->get('kernel');
  120.         $classLoad $kernel->getPluginLoader()->getClassLoader();
  121.         $kernel->reboot(null, new StaticKernelPluginLoader($classLoad));
  122.         return $kernel->getContainer();
  123.     }
  124. }