Subida del módulo y tema de PrestaShop

This commit is contained in:
Kaloyan
2026-04-09 18:31:51 +02:00
parent 12c253296f
commit 16b3ff9424
39262 changed files with 7418797 additions and 0 deletions

3
modules/ps_mbo/.env Normal file
View File

@@ -0,0 +1,3 @@
MBO_CDC_URL="https://assets.prestashop3.com/dst/mbo/v1/mbo-cdc.umd.js"
DISTRIBUTION_API_URL="https://mbo-api.prestashop.com"
ADDONS_API_URL="https://api-addons.prestashop.com"

28
modules/ps_mbo/.htaccess Normal file
View File

@@ -0,0 +1,28 @@
# Disable index view
Options -Indexes
# Hide a specific file
<Files .env>
Order allow,deny
Deny from all
</Files>
<Files ~ "^\.env(.*)$">
Order allow,deny
Deny from all
</Files>
# Apache 2.2
<IfModule !mod_authz_core.c>
<Files *.php>
order allow,deny
deny from all
</Files>
</IfModule>
# Apache 2.4
<IfModule mod_authz_core.c>
<Files *.php>
Require all denied
</Files>
</IfModule>

View File

@@ -0,0 +1,33 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 3.0
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/
if (!defined('_PS_VERSION_')) {
exit;
}
$rootDir = defined('_PS_ROOT_DIR_') ? _PS_ROOT_DIR_ : (function_exists('putenv') ? getenv('_PS_ROOT_DIR_') : $_ENV['_PS_ROOT_DIR_']);
if (!$rootDir) {
$rootDir = __DIR__ . '/../../';
}
$rootAutoload = $rootDir . '/vendor/autoload.php';
if (file_exists($rootAutoload)) {
require_once $rootAutoload;
}
\PrestaShop\Module\Mbo\Helpers\EnvHelper::loadEnv(__DIR__ . '/.env');

12
modules/ps_mbo/config.xml Normal file
View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8" ?>
<module>
<name>ps_mbo</name>
<displayName><![CDATA[PrestaShop Marketplace in your Back Office]]></displayName>
<version><![CDATA[5.2.2]]></version>
<description><![CDATA[Browse the Addons marketplace directly from your back office to better meet your needs.]]></description>
<author><![CDATA[PrestaShop]]></author>
<tab><![CDATA[administration]]></tab>
<is_configurable>0</is_configurable>
<need_instance>0</need_instance>
<limited_countries></limited_countries>
</module>

View File

@@ -0,0 +1,28 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 3.0
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
header('Location: ../');
exit;

View File

@@ -0,0 +1,15 @@
modules_catalog:
resource: './routes/modules_catalog.yml'
prefix: /mbo/modules/catalog
recommended_modules:
resource: './routes/recommended_modules.yml'
prefix: /mbo/modules/recommended
themes_catalog:
resource: './routes/theme_catalog.yml'
prefix: /mbo/themes/catalog
addons:
resource: './routes/addons.yml'
prefix: /mbo/addons

View File

@@ -0,0 +1,5 @@
admin_mbo_addons_module_upgrade:
path: /modules/upgrade
methods: [ POST ]
defaults:
_controller: 'PrestaShop\Module\Mbo\Controller\Admin\AddonsController::upgradeModuleAction'

View File

@@ -0,0 +1,28 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 3.0
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
header('Location: ../');
exit;

View File

@@ -0,0 +1,15 @@
admin_mbo_catalog_module:
path: /
methods: [GET]
defaults:
_controller: 'PrestaShop\Module\Mbo\Controller\Admin\ModuleCatalogController::indexAction'
_legacy_controller: AdminPsMboModule
_legacy_link:
- AdminPsMboModuleParent
- AdminPsMboModule
admin_mbo_module_cdc_error:
path: /cdc_error
methods: [ GET ]
defaults:
_controller: 'PrestaShop\Module\Mbo\Controller\Admin\ModuleCatalogController::cdcErrorAction'

View File

@@ -0,0 +1,7 @@
admin_mbo_recommended_modules:
path: /
methods: [GET]
defaults:
_controller: 'PrestaShop\Module\Mbo\Controller\Admin\ModuleRecommendedController::indexAction'
_legacy_controller: AdminPsMboRecommended
_legacy_link: AdminPsMboRecommended

View File

@@ -0,0 +1,7 @@
admin_mbo_catalog_theme:
path: /
methods: [GET]
defaults:
_controller: 'PrestaShop\Module\Mbo\Controller\Admin\ThemeCatalogController::indexAction'
_legacy_controller: AdminPsMboTheme
_legacy_link: AdminPsMboTheme

View File

@@ -0,0 +1,38 @@
imports:
- { resource: services/*.yml }
- { resource: services/addons.php }
services:
_defaults:
public: true
Symfony\Contracts\Translation\TranslatorInterface: '@PrestaShopBundle\Translation\TranslatorInterface'
PrestaShop\Module\Mbo\Service\ExternalContentProvider\ExternalContentProviderInterface: '@PrestaShop\Module\Mbo\Service\ExternalContentProvider\ExternalContentProvider'
PrestaShop\Module\Mbo\Service\ExternalContentProvider\ExternalContentProvider:
autowire: true
PrestaShop\Module\Mbo\Addons\Provider\LinksProvider:
autowire: true
arguments:
$router: '@prestashop.router'
$categoriesProvider: '@prestashop.categories_provider'
PrestaShop\Module\Mbo\Controller\Admin\ModuleCatalogController:
autowire: true
autoconfigure: true
PrestaShop\Module\Mbo\Controller\Admin\ModuleSelectionController:
autowire: true
PrestaShop\Module\Mbo\Controller\Admin\ModuleRecommendedController:
autowire: true
autoconfigure: true
PrestaShop\Module\Mbo\Controller\Admin\ThemeCatalogController:
autowire: true
autoconfigure: true
PrestaShop\Module\Mbo\Controller\Admin\AddonsController:
autowire: true

View File

@@ -0,0 +1,17 @@
services:
_defaults:
public: true
PrestaShop\PsAccountsInstaller\Installer\Installer:
autowire: true
arguments:
$psAccountsVersion: "7.1.2"
PrestaShop\PsAccountsInstaller\Installer\Facade\PsAccounts:
autowire: true
PrestaShop\Module\Mbo\Accounts\Provider\AccountsDataProvider:
autowire: true
autoconfigure: true
arguments:
$psAccountsVersion: "7.1.2"

View File

@@ -0,0 +1,40 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 3.0
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/
declare(strict_types=1);
namespace Symfony\Component\DependencyInjection\Loader\Configurator;
use PrestaShop\Module\Mbo\Addons\Subscriber\ModuleManagementEventSubscriber;
if (!defined('_PS_VERSION_')) {
exit;
}
return static function (ContainerConfigurator $container) {
$services = $container->services();
// Only load event subscriber when module is enabled to avoid logging events if disabled
if (\ps_mbo::checkModuleStatus()) {
$services->set(ModuleManagementEventSubscriber::class)
->autowire()
->public()
->tag('kernel.event_subscriber');
}
};

View File

@@ -0,0 +1,34 @@
services:
_defaults:
public: true
PrestaShop\Module\Mbo\Addons\User\AddonsUser:
autowire: true
autoconfigure: true
PrestaShop\Module\Mbo\Addons\User\AddonsUserProvider:
autowire: true
autoconfigure: true
PrestaShop\Module\Mbo\Addons\Provider\AddonsDataProvider:
autowire: true
properties:
cacheDir: "%kernel.cache_dir%"
# Addons API Client
PrestaShop\Module\Mbo\Addons\ApiClient:
autowire: true
arguments:
$apiUrl: "%env(ADDONS_API_URL)%"
$httpClient: '@Psr\Http\Client\ClientInterface'
$requestFactory: '@Psr\Http\Message\ServerRequestFactoryInterface'
calls:
- method: setDefaultParams
arguments:
- "@=service('translator').getLocale()"
- "@=service('prestashop.adapter.data_provider.country').getIsoCodebyId()"
- "@=service('prestashop.adapter.legacy.configuration').get('_PS_BASE_URL_')"
- "@=service('prestashop.core.foundation.version').getSemVersion()"
PrestaShop\Module\Mbo\Addons\Toolbar:
autowire: true

View File

@@ -0,0 +1,33 @@
imports:
- { resource: api/repository.yml }
services:
_defaults:
public: true
ps_mbo.db:
class: Db
factory: [ 'Db', 'getInstance' ]
ps_mbo:
class: 'ps_mbo'
factory: [ 'Module', 'getInstanceByName' ]
arguments:
- 'ps_mbo'
PrestaShop\Module\Mbo\Api\Config\Env:
autowire: true
PrestaShop\Module\Mbo\Api\Service\ModuleTransitionExecutor:
autowire: true
PrestaShop\Module\Mbo\Api\Service\ConfigApplyExecutor:
autowire: true
PrestaShop\Module\Mbo\Api\Service\Factory:
autowire: true
arguments:
- [
'@PrestaShop\Module\Mbo\Api\Service\ModuleTransitionExecutor',
'@PrestaShop\Module\Mbo\Api\Service\ConfigApplyExecutor'
]

View File

@@ -0,0 +1,30 @@
services:
_defaults:
public: true
PrestaShop\Module\Mbo\Distribution\Config\Factory:
autowire: true
PrestaShop\Module\Mbo\Distribution\Config\Applier:
autowire: true
PrestaShop\Module\Mbo\Distribution\Config\Appliers\ThemeCatalogMenuConfigApplier:
autowire: true
PrestaShop\Module\Mbo\Distribution\Config\Appliers\ModuleSelectionMenuConfigApplier:
autowire: true
PrestaShop\Module\Mbo\Distribution\Config\Appliers\Factory:
autowire: true
arguments:
- [
'@PrestaShop\Module\Mbo\Distribution\Config\Appliers\ThemeCatalogMenuConfigApplier',
'@PrestaShop\Module\Mbo\Distribution\Config\Appliers\ModuleSelectionMenuConfigApplier'
]
PrestaShop\Module\Mbo\Distribution\Config\CommandHandler\ConfigChangeCommandHandler:
autowire: true
PrestaShop\Module\Mbo\Distribution\Config\CommandHandler\VersionChangeApplyConfigCommandHandler:
autowire: true

View File

@@ -0,0 +1,28 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 3.0
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
header('Location: ../');
exit;

View File

@@ -0,0 +1,26 @@
services:
_defaults:
public: true
PrestaShop\Module\Mbo\Module\SourceRetriever\AddonsUrlSourceRetriever:
autowire: true
arguments:
$addonsDataProvider: '@PrestaShop\Module\Mbo\Addons\Provider\AddonsDataProvider'
$translator: '@translator'
$httpClient: '@Psr\Http\Client\ClientInterface'
$requestFactory: '@Psr\Http\Message\ServerRequestFactoryInterface'
$configuration: '@PrestaShop\PrestaShop\Core\ConfigurationInterface'
properties:
cacheDir: "%kernel.cache_dir%"
PrestaShop\Module\Mbo\Module\Workflow\TransitionsManager:
autowire: true
PrestaShop\Module\Mbo\Module\Workflow\TransitionBuilder:
autowire: true
PrestaShop\Module\Mbo\Module\Workflow\TransitionApplier:
autowire: true
PrestaShop\Module\Mbo\Module\CommandHandler\ModuleStatusTransitionCommandHandler:
autowire: true

View File

@@ -0,0 +1,6 @@
services:
_defaults:
public: true
PrestaShop\Module\Mbo\Api\Repository\ModuleRepository:
autowire: true

View File

@@ -0,0 +1,4 @@
imports:
- { resource: modules.yml }
- { resource: api/modules.yml }
- { resource: api/distribution.yml }

View File

@@ -0,0 +1,12 @@
services:
_defaults:
public: true
PrestaShop\Module\Mbo\Service\View\ContextBuilder:
autowire: true
arguments:
$router: '@prestashop.router'
$cacheProvider: '@Doctrine\Common\Cache\Psr6\DoctrineProvider'
mbo.cdc.context_builder:
alias: PrestaShop\Module\Mbo\Service\View\ContextBuilder

View File

@@ -0,0 +1,40 @@
parameters:
ps_cache_dir: !php/const _PS_CACHE_DIR_
services:
_defaults:
public: true
mbo_doctrine_cache_provider: '@Doctrine\Common\Cache\Psr6\DoctrineProvider'
Symfony\Component\Cache\Adapter\FilesystemAdapter:
autowire: true
arguments:
$namespace: ''
$defaultLifetime: 0
$directory: "%ps_cache_dir%%/ps_mbo"
Doctrine\Common\Cache\Psr6\DoctrineProvider:
autowire: true
autoconfigure: true
factory: [ Doctrine\Common\Cache\Psr6\DoctrineProvider, wrap ]
arguments:
$pool: '@Symfony\Component\Cache\Adapter\FilesystemAdapter'
# Distribution API Client
PrestaShop\Module\Mbo\Distribution\Client:
autowire: true
arguments:
$apiUrl: "%env(DISTRIBUTION_API_URL)%"
$httpClient: '@Psr\Http\Client\ClientInterface'
$requestFactory: '@Psr\Http\Message\ServerRequestFactoryInterface'
$cacheProvider: '@Doctrine\Common\Cache\Psr6\DoctrineProvider'
# Distribution API Client with addons user
PrestaShop\Module\Mbo\Distribution\ConnectedClient:
autowire: true
arguments:
$apiUrl: "%env(DISTRIBUTION_API_URL)%"
$httpClient: '@Psr\Http\Client\ClientInterface'
$requestFactory: '@Psr\Http\Message\ServerRequestFactoryInterface'
$cacheProvider: '@Doctrine\Common\Cache\Psr6\DoctrineProvider'

View File

@@ -0,0 +1,9 @@
services:
_defaults:
public: true
PrestaShop\Module\Mbo\Handler\ErrorHandler\ErrorHandlerInterface: '@PrestaShop\Module\Mbo\Handler\ErrorHandler\ErrorHandler'
PrestaShop\Module\Mbo\Handler\ErrorHandler\ErrorHandler:
class: PrestaShop\Module\Mbo\Handler\ErrorHandler\ErrorHandler

View File

@@ -0,0 +1,28 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 3.0
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
header('Location: ../');
exit;

View File

@@ -0,0 +1,48 @@
---
services:
_defaults:
public: true
PrestaShop\Module\Mbo\Module\Repository:
autowire: true
arguments:
$cacheProvider: '@Doctrine\Common\Cache\Psr6\DoctrineProvider'
$localeCode: "@=service('translator').getLocale()"
$dbPrefix: "@=service('prestashop.adapter.legacy.configuration').get('_DB_PREFIX_')"
PrestaShop\Module\Mbo\Module\FiltersFactory:
autowire: true
PrestaShop\Module\Mbo\Module\CollectionFactory:
autowire: true
PrestaShop\Module\Mbo\Module\ModuleBuilder:
autowire: true
arguments:
$moduleDirectory: "@=service('prestashop.adapter.legacy.configuration').get('_PS_MODULE_DIR_')"
$router: '@prestashop.router'
PrestaShop\Module\Mbo\Module\FilesManager:
autowire: true
PrestaShop\Module\Mbo\Module\ActionsManager:
autowire: true
mbo.modules.actions_manager:
alias: PrestaShop\Module\Mbo\Module\ActionsManager
PrestaShop\Module\Mbo\Module\SourceHandler\AddonsUrlSourceHandler:
autowire: true
autoconfigure: true
tags: [ core.module.source_handler ]
PrestaShop\Module\Mbo\Service\ModulesHelper:
autowire: true
arguments:
$router: '@prestashop.router'
PrestaShop\Module\Mbo\Service\HookExceptionHolder:
autowire: true
mbo.hook_exception_holder:
alias: PrestaShop\Module\Mbo\Service\HookExceptionHolder

View File

@@ -0,0 +1,20 @@
services:
_defaults:
public: true
# security.authorization_checker: '@Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface'
PrestaShop\Module\Mbo\Api\Security\AdminAuthenticationProvider:
autowire: true
arguments:
$cacheProvider: '@Doctrine\Common\Cache\Psr6\DoctrineProvider'
PrestaShop\Module\Mbo\Api\Security\AuthorizationChecker:
autowire: true
arguments:
$cacheProvider: '@Doctrine\Common\Cache\Psr6\DoctrineProvider'
PrestaShop\Module\Mbo\Security\PermissionCheckerInterface: '@PrestaShop\Module\Mbo\Security\PermissionChecker'
PrestaShop\Module\Mbo\Security\PermissionChecker:
autowire: true

View File

View File

@@ -0,0 +1,90 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 3.0
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/
use PrestaShop\Module\Mbo\Api\Controller\AbstractAdminApiController;
use PrestaShop\Module\Mbo\Api\Exception\IncompleteSignatureParamsException;
use PrestaShop\Module\Mbo\Api\Exception\QueryParamsException;
use PrestaShop\Module\Mbo\Api\Service\Factory as ExecutorsFactory;
use PrestaShop\Module\Mbo\Helpers\ErrorHelper;
if (!defined('_PS_VERSION_')) {
exit;
}
/**
* This controller is responsible to execute actions on modules installed on the current shop.
* Caller have to be fully authenticated to perform actions given.
*/
class apiPsMboController extends AbstractAdminApiController
{
/**
* @return void
*/
public function postProcess(): void
{
$response = null;
try {
$service = Tools::getValue('service');
if (empty($service)) {
throw new QueryParamsException('[service] parameter is required');
}
/** @var ExecutorsFactory $executorsFactory */
$executorsFactory = $this->module->get('mbo.api.service.factory');
$response = $executorsFactory->build($service)->execute($this->module);
} catch (Exception $exception) {
ErrorHelper::reportError($exception);
$this->exitWithExceptionMessage($exception);
}
$this->exitWithResponse($response);
}
/**
* {@inheritdoc}
*/
protected function buildSignatureMessage(): string
{
// Payload elements
$action = Tools::getValue('action', '');
$module = Tools::getValue('module', '');
$adminToken = Tools::getValue('admin_token', '');
$actionUuid = Tools::getValue('action_uuid');
if (
!$action
|| !$module
|| !$adminToken
|| !$actionUuid
) {
throw new IncompleteSignatureParamsException('Expected signature elements are not given');
}
$keyVersion = Tools::getValue('version');
return json_encode([
'action' => $action,
'module' => $module,
'admin_token' => $adminToken,
'action_uuid' => $actionUuid,
'version' => $keyVersion,
]);
}
}

View File

@@ -0,0 +1,49 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 3.0
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/
use PrestaShop\Module\Mbo\Api\Config\Config;
use PrestaShop\Module\Mbo\Api\Controller\AbstractAdminApiController;
if (!defined('_PS_VERSION_')) {
exit;
}
/**
* This controller only checks if the user is connected using the token given in parameter.
* Note that if the token is valid, the user session is extended.
*/
class apiSecurityPsMboController extends AbstractAdminApiController
{
public $type = Config::SECURITY_ME;
/**
* @return void
*/
public function postProcess(): void
{
$this->exitWithResponse([
'message' => 'User still connected',
]);
}
protected function authorize(): void
{
}
}

View File

@@ -0,0 +1,28 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 3.0
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
header('Location: ../');
exit;

View File

@@ -0,0 +1,28 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 3.0
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
header('Location: ../');
exit;

View File

@@ -0,0 +1,28 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 3.0
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
header('Location: ../');
exit;

28
modules/ps_mbo/index.php Normal file
View File

@@ -0,0 +1,28 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 3.0
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
header('Location: ../');
exit;

BIN
modules/ps_mbo/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

385
modules/ps_mbo/ps_mbo.php Normal file
View File

@@ -0,0 +1,385 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 3.0
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/
declare(strict_types=1);
if (!defined('_PS_VERSION_') || version_compare(_PS_VERSION_, '8.0.2', '<')) {
return;
}
$autoloadPath = __DIR__ . '/vendor/autoload.php';
if (file_exists($autoloadPath)) {
require_once $autoloadPath;
}
use PrestaShop\Module\Mbo\Accounts\Provider\AccountsDataProvider;
use PrestaShop\Module\Mbo\Addons\Subscriber\ModuleManagementEventSubscriber;
use PrestaShop\Module\Mbo\Helpers\Config;
use PrestaShop\Module\Mbo\Helpers\EnvHelper;
use PrestaShop\Module\Mbo\Helpers\ErrorHelper;
use PrestaShop\PrestaShop\Core\Module\ModuleRepository;
use PrestaShopBundle\Event\ModuleManagementEvent;
class ps_mbo extends Module
{
use PrestaShop\Module\Mbo\Traits\HaveTabs;
use PrestaShop\Module\Mbo\Traits\UseHooks;
public const VERSION = '5.2.2';
public array $configurationList = [
'PS_MBO_SHOP_ADMIN_UUID' => '', // 'ADMIN' because there will be only one for all shops in a multishop context
'PS_MBO_LAST_PS_VERSION_API_CONFIG' => '',
];
/**
* @var PrestaShop\Module\Mbo\DependencyInjection\ServiceContainer
*/
private $serviceContainer;
/**
* @var string
*/
public $imgPath;
/**
* @var string
*/
public $moduleCacheDir;
/**
* Constructor.
*/
public function __construct()
{
$this->name = 'ps_mbo';
// This value must be hard-coded to respect Addons rules, so we must make sure that the const value is always synced with this one
$this->version = '5.2.2';
$this->author = 'PrestaShop';
$this->tab = 'administration';
$this->module_key = '6cad5414354fbef755c7df4ef1ab74eb';
$this->need_instance = 0;
$this->ps_versions_compliancy = [
'min' => '9.0.0',
'max' => '9.99.99',
];
parent::__construct();
$this->imgPath = $this->_path . 'views/img/';
$this->moduleCacheDir = sprintf('%s/var/modules/%s/', rtrim(_PS_ROOT_DIR_, '/'), $this->name);
$this->displayName = $this->trans('PrestaShop Marketplace in your Back Office', [], 'Modules.Mbo.Global');
$this->description = $this
->trans('Browse the Addons marketplace directly from your back office to better meet your needs.',
[],
'Modules.Mbo.Global'
);
if (self::checkModuleStatus()) {
$this->bootHooks();
}
$this->loadEnv();
}
/**
* Install Module.
*
* @return bool
*/
public function install(): bool
{
try {
/** @var PrestaShop\PsAccountsInstaller\Installer\Installer|null $installer */
$installer = $this->get(PrestaShop\PsAccountsInstaller\Installer\Installer::class);
if ($installer) {
$installer->install();
}
} catch (Exception $e) {
ErrorHelper::reportError($e);
}
$this->installTables();
if (parent::install() && $this->registerHook($this->getHooksNames())) {
// Do come extra operations on modules' registration like modifying orders
$this->installHooks();
$this->postponeTabsTranslations();
return true;
}
// If installation fails, we remove the tables previously created
$this->uninstallTables();
return false;
}
/**
* @inerhitDoc
*/
public function uninstall()
{
if (!parent::uninstall()) {
return false;
}
foreach (array_keys($this->configurationList) as $name) {
Configuration::deleteByName($name);
}
// This will reset cached configuration values (uuid, mail, ...) to avoid reusing them
Config::resetConfigValues();
$this->uninstallTables();
/** @var Symfony\Component\EventDispatcher\EventDispatcher $eventDispatcher */
$eventDispatcher = $this->get('event_dispatcher');
if (!$eventDispatcher->hasListeners(ModuleManagementEvent::UNINSTALL)) {
return true;
}
// Execute them first
foreach ($eventDispatcher->getListeners(ModuleManagementEvent::UNINSTALL) as $listener) {
if ($listener[0] instanceof ModuleManagementEventSubscriber) {
/** @var ModuleRepository $moduleRepository */
$moduleRepository = $this->get(ModuleRepository::class);
$legacyModule = $moduleRepository->getModule('ps_mbo');
$listener[0]->{(string) $listener[1]}(new ModuleManagementEvent($legacyModule));
}
}
// And then remove them
foreach ($eventDispatcher->getListeners(ModuleManagementEvent::UNINSTALL) as $listener) {
if ($listener[0] instanceof ModuleManagementEventSubscriber) {
$eventDispatcher->removeSubscriber($listener[0]);
break;
}
}
return true;
}
/**
* Enable Module.
*
* @param bool $force_all
*
* @return bool
*/
public function enable($force_all = false): bool
{
if (self::checkModuleStatus()) {
return true;
}
// Store previous context
$previousContextType = Shop::getContext();
$previousContextShopId = Shop::getContextShopID();
$allShops = Shop::getShops(true, null, true);
foreach ($allShops as $shop) {
if (!$this->enableByShop($shop)) {
return false;
}
}
// Restore previous context
Shop::setContext($previousContextType, $previousContextShopId);
// Install tab before registering shop, we need the tab to be active to create the good token
$this->updateTabs();
$this->postponeTabsTranslations();
return true;
}
/**
* Disable Module.
*
* @param bool $force_all
*
* @return bool
*/
public function disable($force_all = false): bool
{
// Store previous context
$previousContextType = Shop::getContext();
$previousContextShopId = Shop::getContextShopID();
$allShops = Shop::getShops(true, null, true);
foreach ($allShops as $shop) {
if (!$this->disableByShop($shop)) {
return false;
}
}
// Restore previous context
Shop::setContext($previousContextType, $previousContextShopId);
return $this->handleTabAction('uninstall');
}
/**
* @param string $serviceName
*
* @return object|null
*/
public function getService($serviceName)
{
if ($this->serviceContainer === null) {
$this->serviceContainer = new PrestaShop\Module\Mbo\DependencyInjection\ServiceContainer(
$this->name . str_replace('.', '', $this->version),
$this->getLocalPath()
);
}
return $this->serviceContainer->getService($serviceName);
}
/**
* @inerhitDoc
*/
public function isUsingNewTranslationSystem(): bool
{
return true;
}
/**
* Used to correctly check if the module is enabled or not whe registering services
*
* @return bool
*/
public static function checkModuleStatus(): bool
{
// First if the module have active=0 in the DB, the module is inactive
$result = Db::getInstance()->getRow('SELECT `active`
FROM `' . _DB_PREFIX_ . 'module`
WHERE `name` = "ps_mbo"');
if ($result && false === (bool) $result['active']) {
return false;
}
// If active = 1
// in the module table, the module must be associated to at least one shop to be considered as active
$result = Db::getInstance()->getRow('SELECT m.`id_module` as `active`, ms.`id_module` as `shop_active`
FROM `' . _DB_PREFIX_ . 'module` m
LEFT JOIN `' . _DB_PREFIX_ . 'module_shop` ms ON m.`id_module` = ms.`id_module`
WHERE `name` = "ps_mbo"');
if ($result) {
return $result['active'] && $result['shop_active'];
} else {
return false;
}
}
public function installTables(string $table = null): bool
{
$sqlQueries = [];
if (null === $table || 'mbo_api_config' === $table) {
$sqlQueries[] = ' CREATE TABLE IF NOT EXISTS `' . _DB_PREFIX_ . 'mbo_api_config` (
`id_mbo_api_config` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`config_key` varchar(255) NULL,
`config_value` varchar(255) NULL,
`ps_version` varchar(255) NULL,
`mbo_version` varchar(255) NULL,
`applied` TINYINT(1) NOT NULL DEFAULT \'0\',
`date_add` datetime NOT NULL,
PRIMARY KEY (`id_mbo_api_config`)
) ENGINE=' . _MYSQL_ENGINE_ . ' DEFAULT CHARSET=UTF8;';
}
foreach ($sqlQueries as $query) {
if (!Db::getInstance()->execute($query)) {
return false;
}
}
return true;
}
public function getAccountsDataProvider(): ?AccountsDataProvider
{
try {
return $this->get(AccountsDataProvider::class);
} catch (Exception $e) {
ErrorHelper::reportError($e);
return null;
}
}
public function postponeTabsTranslations(): void
{
/**it'
* There is an issue for translating tabs during installation :
* Active modules translations files are loaded during the kernel boot.
* So the installing module translations are not known
* So, we postpone the tabs translations for the first time the module's code is executed.
*/
$lockFile = $this->moduleCacheDir . 'translate_tabs.lock';
if (!file_exists($lockFile)) {
if (!is_dir($this->moduleCacheDir)) {
mkdir($this->moduleCacheDir, 0777, true);
}
$f = fopen($lockFile, 'w+');
fclose($f);
}
}
private function enableByShop(int $shopId)
{
// Force context to all shops
Shop::setContext(Shop::CONTEXT_SHOP, $shopId);
return parent::enable(true);
}
private function disableByShop(int $shopId)
{
// Force context to all shops
Shop::setContext(Shop::CONTEXT_SHOP, $shopId);
return parent::disable(true);
}
private function uninstallTables(): bool
{
$sqlQueries[] = 'DROP TABLE IF EXISTS `' . _DB_PREFIX_ . 'mbo_api_config`';
foreach ($sqlQueries as $query) {
if (!Db::getInstance()->execute($query)) {
return false;
}
}
return true;
}
/**
* @return void
*/
private function loadEnv(): void
{
EnvHelper::loadEnv(__DIR__ . '/.env');
}
}

View File

@@ -0,0 +1,200 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 3.0
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/
declare(strict_types=1);
namespace PrestaShop\Module\Mbo\Accounts\Provider;
use PrestaShop\PsAccountsInstaller\Installer\Exception\InstallerException;
use PrestaShop\PsAccountsInstaller\Installer\Facade\PsAccounts;
use PrestaShop\PsAccountsInstaller\Installer\Installer;
if (!defined('_PS_VERSION_')) {
exit;
}
class AccountsDataProvider
{
private $psAccountsService;
private $psAccountsVersion;
private $moduleName;
public function __construct(string $psAccountsVersion)
{
$this->psAccountsVersion = $psAccountsVersion;
$this->moduleName = Installer::PS_ACCOUNTS_MODULE_NAME;
try {
$this->psAccountsService = $this->getService(PsAccounts::PS_ACCOUNTS_SERVICE);
} catch (InstallerException $e) {
$this->psAccountsService = null;
}
}
/**
* Get PsAccounts User Token
*
* @return string
*/
public function getAccountsToken(): string
{
if (!$this->isAccountLinked()) {
return '';
}
if ($this->psAccountsService && method_exists($this->psAccountsService, 'getUserToken')) {
$token = $this->psAccountsService->getUserToken();
return null === $token ? '' : (string) $token;
}
try {
// @phpstan-ignore class.notFound
$accountsUserTokenRepository = $this->getService(\PrestaShop\Module\PsAccounts\Repository\UserTokenRepository::class);
$token = $accountsUserTokenRepository->getOrRefreshToken();
return null === $token ? '' : (string) $token;
} catch (\Exception $e) {
return '';
}
}
/**
* @return string|null
*/
public function getAccountsShopId(): ?string
{
$shopUuid = null;
if ($this->psAccountsService && method_exists($this->psAccountsService, 'getShopUuid')) {
$shopUuid = $this->psAccountsService->getShopUuid();
}
return $shopUuid ?: null;
}
/**
* @return string|null
*/
public function getAccountsUserId(): ?string
{
$userUuid = null;
if ($this->psAccountsService && method_exists($this->psAccountsService, 'getUserUuid')) {
$userUuid = $this->psAccountsService->getUserUuid();
}
return $userUuid ?: null;
}
/**
* @return string|null
*/
public function getAccountsUserEmail(): ?string
{
if (!$this->psAccountsService) {
return null;
}
return $this->psAccountsService->getEmail();
}
/**
* Get Hydra ps_accounts shop token, available since ps_accounts 7.1.1
*
* @return string
*/
public function getShopTokenV7(): string
{
if (!$this->psAccountsService) {
return '';
}
$shopToken = null;
if (method_exists($this->psAccountsService, 'getShopToken')) {
try {
$shopToken = $this->psAccountsService->getShopToken();
} catch (\Exception $e) {
}
}
return $shopToken ?: '';
}
/**
* Get ps_accounts shop token firebase
*
* @return string
*/
public function getAccountsShopToken(): string
{
if (!$this->psAccountsService) {
return '';
}
$shopToken = null;
try {
$shopToken = $this->psAccountsService->getOrRefreshToken();
} catch (\Exception $e) {
}
return $shopToken ?: '';
}
/**
* @return bool
*/
private function isAccountLinked(): bool
{
if (!$this->psAccountsService) {
return false;
}
return $this->psAccountsService->isAccountLinked();
}
/**
* @param string $serviceName
*
* @return mixed|null
*/
private function getService(string $serviceName)
{
$service = null;
$module = null;
if (\Module::isInstalled($this->moduleName) && $this->checkPsAccountsVersion()) {
$module = \Module::getInstanceByName($this->moduleName);
}
if ($module && method_exists($module, 'getService')) {
$service = $module->getService($serviceName);
}
return $service;
}
private function checkPsAccountsVersion(): bool
{
$module = \Module::getInstanceByName($this->moduleName);
return version_compare(
$module->version,
$this->psAccountsVersion,
'>='
);
}
}

View File

@@ -0,0 +1,28 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 3.0
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
header('Location: ../');
exit;

View File

@@ -0,0 +1,28 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 3.0
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
header('Location: ../');
exit;

View File

@@ -0,0 +1,380 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 3.0
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/
declare(strict_types=1);
namespace PrestaShop\Module\Mbo\Addons;
use PrestaShop\Module\Mbo\Addons\Exception\ClientRequestException;
use PrestaShop\Module\Mbo\Helpers\AddonsApiHelper;
use PrestaShop\Module\Mbo\Helpers\ErrorHelper;
use Psr\Http\Client\ClientExceptionInterface;
use Psr\Http\Client\ClientInterface;
use Psr\Http\Message\RequestFactoryInterface;
if (!defined('_PS_VERSION_')) {
exit;
}
class ApiClient
{
public const HTTP_METHOD_GET = 'GET';
public const HTTP_METHOD_POST = 'POST';
protected string $apiUrl;
/**
* @var ClientInterface
*/
protected $httpClient;
/**
* @var RequestFactoryInterface
*/
protected RequestFactoryInterface $requestFactory;
/**
* @var array<string, string>
*/
protected $queryParameters = ['format' => 'json'];
protected $headers = [];
/**
* @var array<string, string>
*/
protected $defaultQueryParameters;
/**
* @var array<int, string>
*/
protected $possibleQueryParameters = [
'format',
'method',
'action',
'iso_lang',
'iso_code',
'version',
'channel',
'id_module',
'module_key',
'module_name',
'shop_url',
'username',
'password',
];
/**
* @param ClientInterface $httpClient
*/
public function __construct(string $apiUrl, ClientInterface $httpClient, RequestFactoryInterface $requestFactory)
{
$this->apiUrl = $apiUrl;
$this->httpClient = $httpClient;
$this->requestFactory = $requestFactory;
}
public function setDefaultParams(string $locale, $isoCode, ?string $domain, string $shopVersion): void
{
list($isoLang) = explode('-', $locale);
$this->setQueryParams([
'iso_lang' => $isoLang,
'iso_code' => $isoCode,
'version' => $shopVersion,
'shop_url' => $domain,
]);
$this->defaultQueryParameters = $this->queryParameters;
}
/**
* In case you reuse the client, you may want to clean the previous parameters.
*/
public function reset(): void
{
$this->queryParameters = $this->defaultQueryParameters;
$this->headers = [];
}
/**
* @return array<string, string>
*/
public function getQueryParameters(): array
{
return $this->queryParameters;
}
public function getHeaders(): array
{
return array_merge($this->headers, AddonsApiHelper::addCustomHeaders());
}
/**
* Check Addons client account credentials.
*
* @param array{username_addons: string, password_addons: string} $params
*
* @return \stdClass
*/
public function getCheckCustomer(array $params): \stdClass
{
return $this->setQueryParams([
'method' => 'check_customer',
] + $params)->processRequestAndReturn(null, self::HTTP_METHOD_POST);
}
/**
* Check if a module is distributed by Addons.
*
* @param array{username_addons: string, password_addons: string, module_name: string, module_key: string} $params
*
* @return \stdClass
*/
public function getCheckModule(array $params): \stdClass
{
return $this->setQueryParams([
'method' => 'check',
] + $params)->processRequestAndReturn();
}
/**
* @param array{iso_code: string} $params
*
* @return array
*/
public function getNativesModules(array $params): array
{
return $this->setQueryParams([
'method' => 'listing',
'action' => 'native',
] + $params)->processRequestAndReturn('modules');
}
/**
* @param array $params
*
* @return array
*/
public function getPreInstalledModules(array $params = []): array
{
return $this->setQueryParams([
'method' => 'listing',
'action' => 'install-modules',
] + $params)->processRequestAndReturn('modules');
}
/**
* @param array $params
*
* @return array
*/
public function getMustHaveModules(array $params = []): array
{
return $this->setQueryParams([
'method' => 'listing',
'action' => 'must-have',
] + $params)->processRequestAndReturn('modules');
}
/**
* @param array $params
*
* @return array
*/
public function getServices(array $params = []): array
{
return $this->setQueryParams([
'method' => 'listing',
'action' => 'service',
] + $params)->processRequestAndReturn('services');
}
/**
* @param array $params
*
* @return array
*/
public function getCategories(array $params = []): array
{
return $this->setQueryParams([
'method' => 'listing',
'action' => 'categories',
] + $params)->processRequestAndReturn('module');
}
/**
* @param array{id_module: int} $params
*
* @return object|null
*/
public function getModule(array $params): ?object
{
$modules = $this->setQueryParams([
'method' => 'listing',
'action' => 'module',
] + $params)->processRequestAndReturn('modules');
return $modules[0] ?? null;
}
/**
* Call API for module ZIP content (= download).
*
* @param array{username_addons: string, password_addons: string, channel: string, id_module: int} $params
*
* @return string binary content (zip format)
*/
public function getModuleZip(array $params): string
{
return $this->setQueryParams([
'method' => 'module',
] + $params)->processRequest(self::HTTP_METHOD_POST);
}
/**
* @param array{username_addons: string, password_addons: string} $params
*
* @return array
*/
public function getCustomerModules(array $params): array
{
return $this->setQueryParams([
'method' => 'listing',
'action' => 'customer',
] + $params)->processRequestAndReturn('modules', self::HTTP_METHOD_POST);
}
/**
* Get list of themes bought by customer.
*
* @param array{username_addons: string, password_addons: string} $params
*
* @return array
*/
public function getCustomerThemes(array $params): array
{
return $this->setQueryParams([
'method' => 'listing',
'action' => 'customer-themes',
] + $params)->processRequestAndReturn('themes', self::HTTP_METHOD_POST, new \stdClass());
}
public function getModuleByName(string $name): ?\stdClass
{
$url = sprintf('/v2/products/%s', $name);
$queryString = !empty($this->queryParameters) ? '?' . http_build_query($this->queryParameters) : '';
$request = $this->requestFactory->createRequest(self::HTTP_METHOD_GET, $this->apiUrl . $url . $queryString);
$headers = $this->getHeaders();
foreach ($headers as $name => $value) {
$request = $request->withHeader($name, $value);
}
try {
$resp = $this->httpClient->sendRequest($request)->getBody()->getContents();
} catch (\Throwable $e) {
ErrorHelper::reportError($e, [
'url' => $request->getUri(),
]);
return null;
}
$response = json_decode((string) $resp);
if (JSON_ERROR_NONE !== json_last_error()) {
return null;
}
return $response;
}
/**
* Process the request with the current parameters, given the $method, and return the $attribute from
* the response body, or the default fallback value $default.
*
* @param string|null $attributeToReturn
* @param string $method
* @param mixed $default
*
* @return mixed
*/
public function processRequestAndReturn(
string $attributeToReturn = null,
string $method = self::HTTP_METHOD_GET,
$default = [],
) {
$response = json_decode($this->processRequest($method));
if (JSON_ERROR_NONE !== json_last_error()) {
return $default;
}
if ($attributeToReturn) {
return $response->{$attributeToReturn} ?? $default;
}
return $response;
}
/**
* Process the request with the current parameters, given the $method, return the body as string
*
* @param string $method
*
* @return string
*
* @throws ClientExceptionInterface
* @throws ClientRequestException
*/
public function processRequest(string $method = self::HTTP_METHOD_GET): string
{
$queryString = !empty($this->queryParameters) ? '?' . http_build_query($this->queryParameters) : '';
$headers = $this->getHeaders();
$request = $this->requestFactory->createRequest($method, $this->apiUrl . $queryString);
foreach ($headers as $name => $value) {
$request = $request->withHeader($name, $value);
}
$response = $this->httpClient->sendRequest($request);
if ($response->getStatusCode() < 200 || $response->getStatusCode() >= 300) {
throw new ClientRequestException($response->getReasonPhrase(), $response->getStatusCode());
}
return $response->getBody()->getContents();
}
/**
* @param array $params
*
* @return $this
*/
public function setQueryParams(array $params): self
{
$filteredParams = array_intersect_key($params, array_flip($this->possibleQueryParameters));
$this->queryParameters = array_merge($this->queryParameters, $filteredParams);
return $this;
}
public function setHeaders(array $headers): self
{
$this->headers = array_merge($this->headers, $headers);
return $this;
}
}

View File

@@ -0,0 +1,30 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 3.0
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/
declare(strict_types=1);
namespace PrestaShop\Module\Mbo\Addons\Exception;
if (!defined('_PS_VERSION_')) {
exit;
}
class ClientRequestException extends \Exception
{
}

View File

@@ -0,0 +1,29 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 3.0
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/
namespace PrestaShop\Module\Mbo\Addons\Exception;
if (!defined('_PS_VERSION_')) {
exit;
}
class DownloadModuleException extends \Exception
{
}

View File

@@ -0,0 +1,28 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 3.0
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
header('Location: ../');
exit;

View File

@@ -0,0 +1,279 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 3.0
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/
declare(strict_types=1);
namespace PrestaShop\Module\Mbo\Addons\Provider;
use PrestaShop\Module\Mbo\Addons\ApiClient;
use PrestaShop\Module\Mbo\Addons\Exception\DownloadModuleException;
use PrestaShop\Module\Mbo\Addons\User\AddonsUser;
use PrestaShop\Module\Mbo\Exception\AddonsDownloadModuleException;
use PrestaShop\Module\Mbo\Helpers\ErrorHelper;
use Symfony\Component\HttpClient\Exception\ClientException;
if (!defined('_PS_VERSION_')) {
exit;
}
/**
* This class will provide data from Addons API
*/
class AddonsDataProvider implements DataProviderInterface
{
/** @var string */
public const ADDONS_API_MODULE_CHANNEL_STABLE = 'stable';
/** @var string */
public const ADDONS_API_MODULE_CHANNEL_BETA = 'beta';
/** @var string */
public const ADDONS_API_MODULE_CHANNEL_ALPHA = 'alpha';
/** @var array<string> */
public const ADDONS_API_MODULE_CHANNELS = [
self::ADDONS_API_MODULE_CHANNEL_STABLE,
self::ADDONS_API_MODULE_CHANNEL_BETA,
self::ADDONS_API_MODULE_CHANNEL_ALPHA,
];
/** @var array<string, string> */
public const ADDONS_API_MODULE_ACTIONS = [
'native' => 'getNativesModules',
'service' => 'getServices',
'native_all' => 'getNativesModules',
'must-have' => 'getMustHaveModules',
'customer' => 'getCustomerModules',
'customer_themes' => 'getCustomerThemes',
'check_customer' => 'getCheckCustomer',
'check_module' => 'getCheckModule',
'module_download' => 'getModuleZip',
'module' => 'getModule',
'install-modules' => 'getPreInstalledModules',
'categories' => 'getCategories',
];
/**
* @var bool
*/
protected static $is_addons_up = true;
/**
* @var ApiClient
*/
protected $marketplaceClient;
/**
* @var string the cache directory location
*/
public $cacheDir;
/**
* @var string
*/
protected $moduleChannel;
/**
* @var AddonsUser
*/
protected $user;
/**
* @param ApiClient $apiClient
* @param AddonsUser $user
* @param string|null $moduleChannel
*/
public function __construct(
ApiClient $apiClient,
AddonsUser $user,
string $moduleChannel = null,
) {
$this->marketplaceClient = $apiClient;
$this->moduleChannel = $moduleChannel ?? self::ADDONS_API_MODULE_CHANNEL_STABLE;
$this->user = $user;
}
/**
* Downloads a module source from addons, store it and returns the file name.
*
* @throws DownloadModuleException
*/
public function downloadModule(int $moduleId): string
{
$params = [
'id_module' => $moduleId,
'format' => 'json',
];
// Module downloading
try {
$moduleData = $this->request('module_download', $params);
} catch (\Exception $e) {
ErrorHelper::reportError($e);
$message = $this->isUserAuthenticated() ?
'Error sent by Addons. You may be not allowed to download this module.'
: 'Error sent by Addons. You may need to be logged.';
if ($e instanceof ClientException) {
throw new AddonsDownloadModuleException($e);
}
throw new DownloadModuleException($message, 0, $e);
}
$temporaryZipFilename = tempnam($this->cacheDir, 'mod');
if (file_put_contents($temporaryZipFilename, $moduleData) !== false) {
return $temporaryZipFilename;
} else {
throw new DownloadModuleException('Cannot store module content in temporary file !');
}
}
/**
* Tells if the user is authenticated to Addons or Account
*/
public function isUserAuthenticated(): bool
{
return $this->user->isAuthenticated();
}
/**
* Tells if the user is authenticated to Addons
*/
public function isUserAuthenticatedOnAccounts(): bool
{
return $this->user->hasAccountsTokenInSession() || $this->user->isConnectedOnPsAccounts();
}
/**
* Returns the user's login if he is authenticated to Addons
*/
public function getAuthenticatedUserEmail(): ?string
{
return $this->isUserAuthenticated() ? (string) $this->user->getEmail()['username'] : null;
}
/**
* {@inheritdoc}
*
* @throws \Exception
*/
public function request(string $action, array $params = [])
{
if (!$this->isServiceUp()) {
throw new \Exception('Previous call failed and disabled client.');
}
if (
!array_key_exists($action, self::ADDONS_API_MODULE_ACTIONS)
|| !method_exists($this->marketplaceClient, self::ADDONS_API_MODULE_ACTIONS[$action])
) {
throw new \Exception("Action '{$action}' not found in actions list.");
}
$this->marketplaceClient->reset();
// We merge the addons credentials
$authParams = $this->getAuthenticationParams();
if (null !== $authParams['bearer'] && is_string($authParams['bearer'])) {
$this->marketplaceClient->setHeaders([
'Authorization' => 'Bearer ' . $authParams['bearer'],
]);
}
if (null !== $authParams['credentials'] && is_array($authParams['credentials'])) {
$params = array_merge($authParams['credentials'], $params);
}
if ($action === 'module_download') {
$params['channel'] = $this->moduleChannel;
} elseif ($action === 'native_all') {
$params['iso_code'] = 'all';
}
try {
return $this->marketplaceClient->{self::ADDONS_API_MODULE_ACTIONS[$action]}($params);
} catch (\Exception $e) {
ErrorHelper::reportError($e);
self::$is_addons_up = false;
throw $e;
}
}
public function getAuthenticationParams(): array
{
$authParams = [
'bearer' => null,
'credentials' => null,
];
// We merge the addons credentials
if ($this->isUserAuthenticated()) {
$credentials = $this->user->getCredentials();
if (null !== $credentials && array_key_exists('accounts_token', $credentials)) {
$authParams['bearer'] = $credentials['accounts_token'];
// This is a bug for now, we need to give a couple of username/password even if a token is given
// It has to be cleaned once the bug fixed
$authParams['credentials'] = [
'username' => 'name@domain.com',
'password' => 'fakepwd',
];
} else {
$authParams['credentials'] = $credentials;
}
}
return $authParams;
}
/**
* Returns authentication parameters for Addons API requests
* @return string|null
*/
public function getAuthenticationToken(): ?string
{
if ($this->isUserAuthenticated()) {
$credentials = $this->user->getCredentials();
if (null !== $credentials && array_key_exists('accounts_token', $credentials)) {
return $credentials['accounts_token'];
}
}
return null;
}
public function getAccountsShopUuid(): ?string
{
if (!$this->isUserAuthenticatedOnAccounts()) {
return null;
}
return $this->user->getAccountsShopUuid();
}
/**
* Check if the previous request to Addons has failed.
*
* @return bool
*/
public function isServiceUp(): bool
{
return self::$is_addons_up;
}
}

View File

@@ -0,0 +1,44 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 3.0
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/
declare(strict_types=1);
namespace PrestaShop\Module\Mbo\Addons\Provider;
if (!defined('_PS_VERSION_')) {
exit;
}
interface DataProviderInterface
{
/**
* Check if a request has already failed.
*
* @return bool
*/
public function isServiceUp(): bool;
/**
* Send a request to addons.prestashop.com to retrieve Modules/Addons data.
*
* @param string $action the query type
* @param array $params the request parameters
*/
public function request(string $action, array $params);
}

View File

@@ -0,0 +1,155 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 3.0
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/
declare(strict_types=1);
namespace PrestaShop\Module\Mbo\Addons\Provider;
use PrestaShop\PrestaShop\Adapter\Configuration;
use PrestaShop\PrestaShop\Adapter\LegacyContext;
use PrestaShop\PrestaShop\Core\Foundation\Version;
use PrestaShopBundle\Service\DataProvider\Admin\CategoriesProvider;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\Routing\Router;
use Symfony\Contracts\Translation\TranslatorInterface;
if (!defined('_PS_VERSION_')) {
exit;
}
class LinksProvider
{
public const DEFAULT_LANGUAGE = 'en';
/**
* @var Version
*/
protected $version;
/**
* @var LegacyContext
*/
protected $context;
/**
* @var Configuration
*/
protected $configuration;
/**
* @var RequestStack
*/
protected $requestStack;
/**
* @var TranslatorInterface
*/
protected $translator;
/**
* @var CategoriesProvider
*/
private $categoriesProvider;
/**
* @var Router
*/
private $router;
/**
* @param Version $version
* @param LegacyContext $context
* @param Configuration $configuration
* @param RequestStack $requestStack
* @param TranslatorInterface $trans
*/
public function __construct(
Version $version,
LegacyContext $context,
Configuration $configuration,
RequestStack $requestStack,
CategoriesProvider $categoriesProvider,
TranslatorInterface $trans,
Router $router,
) {
$this->version = $version;
$this->context = $context;
$this->configuration = $configuration;
$this->requestStack = $requestStack;
$this->categoriesProvider = $categoriesProvider;
$this->translator = $trans;
$this->router = $router;
}
/**
* We cannot use http_build_query() here due to a bug on Addons
*
* @see https://github.com/PrestaShop/PrestaShop/pull/9255/files#r200498010
*
* @return string
*/
public function getSelectionLink(): string
{
$link = 'https://addons.prestashop.com/iframe/search-1.7.php?psVersion=' . $this->version->getSemVersion()
. '&isoLang=' . $this->context->getContext()->language->iso_code
. '&isoCurrency=' . $this->context->getContext()->currency->iso_code
. '&isoCountry=' . $this->context->getContext()->country->iso_code
. '&activity=' . $this->configuration->getInt('PS_SHOP_ACTIVITY')
. '&parentUrl=' . $this->requestStack->getCurrentRequest()->getSchemeAndHttpHost();
if ('AdminPsMboTheme' === $this->requestStack->getCurrentRequest()->attributes->get('_legacy_controller')) {
$link .= '&onlyThemes=1';
}
return $link;
}
public function getCategoryLink(string $categoryName): string
{
$category = $this->getCategoryByName($categoryName);
$routeParams = [];
if ($category && 'other' !== mb_strtolower($categoryName)) {
$routeParams['filterCategoryRef'] = $category->refMenu;
$routeParams['mbo_cdc_path'] = '/#/modules';
}
return $this->router->generate('admin_mbo_catalog_module', $routeParams);
}
/**
* Returns a category object based on its name.
*
* @param string $categoryName
*
* @return \stdClass|null
*/
private function getCategoryByName(string $categoryName): ?\stdClass
{
foreach ($this->categoriesProvider->getCategories() as $parentCategory) {
foreach ($parentCategory->subMenu as $childCategory) {
if ($childCategory->name === $categoryName) {
return $childCategory;
}
}
}
return null;
}
}

View File

@@ -0,0 +1,28 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 3.0
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
header('Location: ../');
exit;

View File

@@ -0,0 +1,181 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 3.0
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/
declare(strict_types=1);
namespace PrestaShop\Module\Mbo\Addons\Subscriber;
use PrestaShop\Module\Mbo\Api\Security\AdminAuthenticationProvider;
use PrestaShop\Module\Mbo\Distribution\Client;
use PrestaShop\Module\Mbo\Distribution\Config\Command\VersionChangeApplyConfigCommand;
use PrestaShop\Module\Mbo\Distribution\Config\CommandHandler\VersionChangeApplyConfigCommandHandler;
use PrestaShop\Module\Mbo\Module\Module;
use PrestaShop\Module\Mbo\Module\Repository;
use PrestaShop\Module\Mbo\Service\View\ContextBuilder;
use PrestaShop\PrestaShop\Core\Module\ModuleInterface;
use PrestaShopBundle\Event\ModuleManagementEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
if (!defined('_PS_VERSION_')) {
exit;
}
/**
* Performs actions on Module lifecycle events
*/
class ModuleManagementEventSubscriber implements EventSubscriberInterface
{
const WATERMARK_FILENAME = '/.info';
public function __construct(
private readonly Repository $moduleRepository,
private readonly ContextBuilder $contextBuilder,
private readonly Client $distributionClient,
private readonly AdminAuthenticationProvider $adminAuthenticationProvider,
private readonly VersionChangeApplyConfigCommandHandler $versionChangeApplyConfigCommandHandler,
) {
}
public static function getSubscribedEvents(): array
{
return [
ModuleManagementEvent::INSTALL => [
['clearCatalogCache'],
['onInstall'],
],
ModuleManagementEvent::UNINSTALL => [
['clearCatalogCache'],
['onUninstall'],
],
ModuleManagementEvent::ENABLE => [
['clearCatalogCache'],
['onEnable'],
],
ModuleManagementEvent::DISABLE => [
['clearCatalogCache'],
['onDisable'],
],
ModuleManagementEvent::UPGRADE => [
['clearCatalogCache'],
['onUpgrade'],
],
ModuleManagementEvent::RESET => [
['clearCatalogCache'],
['onReset'],
],
];
}
public function clearCatalogCache(): void
{
$this->moduleRepository->clearCache();
$this->contextBuilder->clearCache();
}
public function onInstall(ModuleManagementEvent $event): void
{
$this->logEvent(ModuleManagementEvent::INSTALL, $event);
}
public function onUninstall(ModuleManagementEvent $event): void
{
$this->logEvent(ModuleManagementEvent::UNINSTALL, $event);
}
public function onEnable(ModuleManagementEvent $event): void
{
$this->logEvent(ModuleManagementEvent::ENABLE, $event);
}
public function onDisable(ModuleManagementEvent $event): void
{
$this->logEvent(ModuleManagementEvent::DISABLE, $event);
}
public function onUpgrade(ModuleManagementEvent $event): void
{
$this->logEvent(ModuleManagementEvent::UPGRADE, $event);
$module = $event->getModule();
if ('ps_mbo' === $module->get('name')) {
// Apply config dur to PS and MBO version changes
$this->applyConfigOnVersionChange($module);
}
}
public function onReset(ModuleManagementEvent $event): void
{
$this->logEvent(ModuleManagementEvent::RESET, $event);
}
protected function logEvent(string $eventName, ModuleManagementEvent $event): void
{
try {
$data = $this->contextBuilder->getEventContext();
} catch (\Exception $e) {
// Do nothing, we don't want to block the module action
return;
}
$data['event_name'] = $eventName;
$data['module_name'] = $event->getModule()->get('name');
$data['module_version'] = $event->getModule()->get('version');
if (in_array($eventName, [
ModuleManagementEvent::INSTALL,
ModuleManagementEvent::UPGRADE,
])) {
$data['module_watermark'] = $this->getModuleWatermark($event->getModule());
}
try {
$this->distributionClient->setBearer($this->adminAuthenticationProvider->getMboJWT());
$this->distributionClient->trackEvent($data);
} catch (\Exception $e) {
// Do nothing, we don't want to block the module action
}
}
private function getModuleWatermark(ModuleInterface $module): string
{
$fileName = _PS_MODULE_DIR_ . $module->get('name') . self::WATERMARK_FILENAME;
if (!file_exists($fileName)) {
return '';
}
try {
$fileHandle = fopen($fileName, 'r');
$contents = fread($fileHandle, filesize($fileName));
fclose($fileHandle);
} catch (\Exception $e) {
$contents = '';
}
return $contents ?: '';
}
private function applyConfigOnVersionChange(ModuleInterface $module)
{
/** @var Module $module */
$command = new VersionChangeApplyConfigCommand(
_PS_VERSION_,
(string) $module->disk->get('version')
);
$this->versionChangeApplyConfigCommandHandler->handle($command);
}
}

View File

@@ -0,0 +1,28 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 3.0
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
header('Location: ../');
exit;

View File

@@ -0,0 +1,91 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 3.0
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/
declare(strict_types=1);
namespace PrestaShop\Module\Mbo\Addons;
use PrestaShop\Module\Mbo\Controller\Admin\ModuleCatalogController;
use PrestaShop\Module\Mbo\Security\PermissionCheckerInterface;
use PrestaShopBundle\Security\Voter\PageVoter;
use Symfony\Contracts\Translation\TranslatorInterface;
if (!defined('_PS_VERSION_')) {
exit;
}
/**
* This service returns descriptions for the buttons to add into the Module configure toolbar
* (like addons connect, update module CTA, ...)
*/
class Toolbar
{
public function __construct(
private readonly PermissionCheckerInterface $permissionChecker,
private readonly TranslatorInterface $translator,
) {
}
/**
* Common method for all module related controller for getting the header buttons.
*
* @return array
*/
public function getToolbarButtons(): array
{
if (!in_array(
$this->permissionChecker->getAuthorizationLevel(ModuleCatalogController::CONTROLLER_NAME),
[
PageVoter::LEVEL_READ,
PageVoter::LEVEL_UPDATE,
]
)) {
return $this->getAddModuleToolbar();
}
return [];
}
/**
* Get the Add Module button definition for the toolbar
*
* @return array[]
*/
public function getAddModuleToolbar(): array
{
return [
'add_module' => [
'href' => '#',
'desc' => $this->translator->trans(
'Upload a module',
[],
'Modules.Mbo.Modulescatalog',
$this->translator->getLocale()
),
'icon' => 'cloud_upload',
'help' => $this->translator->trans(
'Upload a module',
[],
'Modules.Mbo.Modulescatalog',
$this->translator->getLocale()
),
],
];
}
}

View File

@@ -0,0 +1,141 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 3.0
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/
declare(strict_types=1);
namespace PrestaShop\Module\Mbo\Addons\User;
use PrestaShop\Module\Mbo\Accounts\Provider\AccountsDataProvider;
use Symfony\Component\HttpFoundation\Exception\SessionNotFoundException;
use Symfony\Component\HttpFoundation\RequestStack;
if (!defined('_PS_VERSION_')) {
exit;
}
/**
* This class will read user information stored in cookies
*/
class AddonsUser implements UserInterface
{
/**
* @var AccountsDataProvider
*/
private $accountsDataProvider;
/**
* @var RequestStack
*/
private $requestStack;
public function __construct(
RequestStack $requestStack,
AccountsDataProvider $accountsDataProvider,
) {
$this->requestStack = $requestStack;
$this->accountsDataProvider = $accountsDataProvider;
}
/**
* {@inheritdoc}
*/
public function isAuthenticated(): bool
{
return $this->hasAccountsTokenInSession() || $this->isConnectedOnPsAccounts();
}
/**
* {@inheritdoc}
*/
public function getCredentials(bool $encrypted = false): ?array
{
$accountsToken = $this->getAccountsTokenFromSession();
if (null !== $accountsToken) {
return ['accounts_token' => (string) $accountsToken];
}
// accounts
$accountsToken = $this->accountsDataProvider->getAccountsToken();
if (!empty($accountsToken)) {
return ['accounts_token' => (string) $accountsToken];
}
return null;
}
/**
* {@inheritdoc}
*/
public function getEmail(): array
{
$email = null;
if ($this->isAuthenticated()) {
// Connected on ps_accounts
if ($this->isConnectedOnPsAccounts()) {
$email = $this->accountsDataProvider->getAccountsUserEmail();
} elseif ($this->hasAccountsTokenInSession()) { // Connected on ps_accounts with session
$email = $this->jwtDecode($this->getAccountsTokenFromSession())['email'];
}
}
return [
'username' => $email,
];
}
public function hasAccountsTokenInSession(): bool
{
return null !== $this->getAccountsTokenFromSession();
}
public function isConnectedOnPsAccounts(): bool
{
$accountsToken = $this->accountsDataProvider->getAccountsToken();
return !empty($accountsToken);
}
public function getAccountsShopUuid(): ?string
{
return $this->accountsDataProvider->getAccountsShopId();
}
/**
* @return string|null
*/
private function getAccountsTokenFromSession(): ?string
{
try {
return $this->requestStack->getSession()->get('accounts_token');
} catch (SessionNotFoundException $e) {
return null;
}
}
/**
* {@inheritdoc}
*/
private function jwtDecode(string $token): array
{
$payload = explode('.', $token)[1];
$jsonToken = base64_decode($payload);
return json_decode($jsonToken, true);
}
}

View File

@@ -0,0 +1,45 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 3.0
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/
declare(strict_types=1);
namespace PrestaShop\Module\Mbo\Addons\User;
if (!defined('_PS_VERSION_')) {
exit;
}
class AddonsUserProvider
{
/**
* @var AddonsUser
*/
protected $user;
public function __construct(AddonsUser $user)
{
$this->user = $user;
}
public function getUser(): AddonsUser
{
return $this->user;
}
}

View File

@@ -0,0 +1,46 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 3.0
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/
declare(strict_types=1);
namespace PrestaShop\Module\Mbo\Addons\User;
if (!defined('_PS_VERSION_')) {
exit;
}
interface UserInterface
{
/**
* @return bool
*/
public function isAuthenticated(): bool;
/**
* @pararm bool $encrypted
*
* @return array{username?: string, password?: string, accounts_token?: string}
*/
public function getCredentials(bool $encrypted = false): ?array;
/**
* @return array{username: string}
*/
public function getEmail(): array;
}

View File

@@ -0,0 +1,28 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 3.0
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
header('Location: ../');
exit;

View File

@@ -0,0 +1,28 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 3.0
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
header('Location: ../');
exit;

View File

@@ -0,0 +1,49 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 3.0
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/
declare(strict_types=1);
namespace PrestaShop\Module\Mbo\Api\Config;
if (!defined('_PS_VERSION_')) {
exit;
}
class Config
{
const UNAUTHORIZED_ERROR_CODE = 401;
const DATABASE_QUERY_ERROR_CODE = 454;
const DATABASE_INSERT_ERROR_CODE = 455;
const RETRIEVE_NEW_KEY_ERROR_CODE = 457;
const INVALID_URL_QUERY = 458;
const INCOMPLETE_SIGNATURE_ERROR_CODE = 459;
const HTTP_STATUS_MESSAGES = [
self::DATABASE_QUERY_ERROR_CODE => 'Database syntax error',
self::DATABASE_INSERT_ERROR_CODE => 'Failed to write to database',
self::INVALID_URL_QUERY => 'Invalid URL query',
self::UNAUTHORIZED_ERROR_CODE => 'Not authorized',
self::INCOMPLETE_SIGNATURE_ERROR_CODE => 'Incomplete signature',
self::RETRIEVE_NEW_KEY_ERROR_CODE => 'Failed to retrieve key',
];
const API_CONFIG = 'api_config';
const MODULE_ACTIONS = 'module_actions';
const SECURITY_ME = 'security_me';
}

View File

@@ -0,0 +1,42 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 3.0
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/
declare(strict_types=1);
namespace PrestaShop\Module\Mbo\Api\Config;
if (!defined('_PS_VERSION_')) {
exit;
}
/**
* This class allows retrieving config data that can be overwritten by a .env file.
* Otherwise, it returns by default from the Config class.
*/
class Env
{
public function get(string $key): string
{
if (!empty($_ENV[$key])) {
return $_ENV[$key];
}
return constant(Config::class . '::' . $key);
}
}

View File

@@ -0,0 +1,28 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 3.0
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
header('Location: ../');
exit;

View File

@@ -0,0 +1,209 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 3.0
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/
declare(strict_types=1);
namespace PrestaShop\Module\Mbo\Api\Controller;
use PrestaShop\Module\Mbo\Api\Config\Config;
use PrestaShop\Module\Mbo\Api\Exception\IncompleteSignatureParamsException;
use PrestaShop\Module\Mbo\Api\Exception\QueryParamsException;
use PrestaShop\Module\Mbo\Api\Exception\RetrieveNewKeyException;
use PrestaShop\Module\Mbo\Api\Exception\UnauthorizedException;
use PrestaShop\Module\Mbo\Api\Security\AdminAuthenticationProvider;
use PrestaShop\Module\Mbo\Api\Security\AuthorizationChecker;
use PrestaShop\Module\Mbo\Exception\AddonsDownloadModuleException;
use PrestaShop\Module\Mbo\Helpers\Config as ConfigHelper;
use PrestaShop\Module\Mbo\Helpers\ErrorHelper;
use Psr\Log\LoggerInterface;
if (!defined('_PS_VERSION_')) {
exit;
}
abstract class AbstractAdminApiController extends \ModuleAdminController
{
/**
* Endpoint name
*
* @var string
*/
public $type = '';
/**
* @var AdminAuthenticationProvider
*/
protected $adminAuthenticationProvider;
/**
* @var \ps_mbo
*/
public $module;
/**
* @var AuthorizationChecker
*/
private $authorizationChecker;
/**
* @var LoggerInterface
*/
protected $logger;
public function __construct()
{
parent::__construct();
$this->adminAuthenticationProvider = $this->module->get(AdminAuthenticationProvider::class);
$this->authorizationChecker = $this->module->get(AuthorizationChecker::class);
$this->logger = $this->module->get('logger');
}
public function init(): void
{
try {
$this->logger->info('API Call received = ' . $_SERVER['REQUEST_URI']);
$this->authorize();
} catch (IncompleteSignatureParamsException $exception) {
ErrorHelper::reportError($exception);
$this->exitWithExceptionMessage($exception);
} catch (UnauthorizedException $exception) {
ErrorHelper::reportError($exception);
$this->exitWithExceptionMessage($exception);
} catch (RetrieveNewKeyException $exception) {
ErrorHelper::reportError($exception);
$this->exitWithExceptionMessage($exception);
}
parent::init();
}
protected function exitWithResponse(array $response): void
{
$httpCode = isset($response['httpCode']) ? (int) $response['httpCode'] : 200;
$shopUuid = ConfigHelper::getShopMboUuid();
$response['shop_uuid'] = $shopUuid;
$this->dieWithResponse($response, $httpCode);
}
protected function exitWithExceptionMessage(\Exception $exception): void
{
$code = (int) $exception->getCode() === 0 ? 500 : $exception->getCode();
if ($exception instanceof QueryParamsException) {
$code = Config::INVALID_URL_QUERY;
} elseif ($exception instanceof IncompleteSignatureParamsException) {
$code = Config::INCOMPLETE_SIGNATURE_ERROR_CODE;
} elseif ($exception instanceof UnauthorizedException) {
$code = Config::UNAUTHORIZED_ERROR_CODE;
} elseif ($exception instanceof RetrieveNewKeyException) {
$code = Config::RETRIEVE_NEW_KEY_ERROR_CODE;
}
$response = [
'object_type' => $this->type,
'status' => false,
'httpCode' => $code,
'previous_exception' => get_class($exception),
'message' => $this->translator->trans($exception->getMessage(), [], 'Modules.Mbo.Addons'),
'context' => method_exists($exception, 'getContext') ? $exception->getContext() : [],
];
if ($exception instanceof AddonsDownloadModuleException) {
$response['body']['statusText'] = $exception->getTechnicalErrorMessage();
}
$this->dieWithResponse($response, (int) $code);
}
private function dieWithResponse(array $response, int $code): void
{
$httpStatusText = "HTTP/1.1 $code";
if (array_key_exists($code, Config::HTTP_STATUS_MESSAGES)) {
$httpStatusText .= ' ' . Config::HTTP_STATUS_MESSAGES[$code];
} elseif (isset($response['body']['statusText'])) {
$httpStatusText .= ' ' . $response['body']['statusText'];
}
$response['httpCode'] = $code;
$response['httpStatusText'] = $httpStatusText;
header('Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0');
header('Content-Type: application/json;charset=utf-8');
header($httpStatusText);
echo json_encode($response, JSON_UNESCAPED_SLASHES);
exit;
}
/**
* @throws IncompleteSignatureParamsException
* @throws RetrieveNewKeyException
* @throws UnauthorizedException
*/
protected function authorize()
{
$keyVersion = \Tools::getValue('version');
$signature = isset($_SERVER['HTTP_MBO_SIGNATURE']) ? $_SERVER['HTTP_MBO_SIGNATURE'] : false;
if (!$keyVersion || !$signature) {
throw new IncompleteSignatureParamsException('Expected signature elements are not given');
}
$message = $this->buildSignatureMessage();
$this->authorizationChecker->verify($keyVersion, $signature, $message);
}
/**
* Generate elements composing the signature.
* This is the standard composition.
* Please build your own if other elements are included to the signature.
*
* @return string
*
* @throws IncompleteSignatureParamsException
*/
protected function buildSignatureMessage(): string
{
// Payload elements
$adminToken = \Tools::getValue('admin_token');
$actionUuid = \Tools::getValue('action_uuid');
if (
!$adminToken
|| !$actionUuid
) {
throw new IncompleteSignatureParamsException('Expected signature elements are not given');
}
$keyVersion = \Tools::getValue('version');
return json_encode([
'admin_token' => $adminToken,
'action_uuid' => $actionUuid,
'version' => $keyVersion,
]);
}
}

View File

@@ -0,0 +1,28 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 3.0
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
header('Location: ../');
exit;

View File

@@ -0,0 +1,30 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 3.0
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/
declare(strict_types=1);
namespace PrestaShop\Module\Mbo\Api\Exception;
if (!defined('_PS_VERSION_')) {
exit;
}
class IncompleteSignatureParamsException extends \Exception
{
}

View File

@@ -0,0 +1,30 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 3.0
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/
declare(strict_types=1);
namespace PrestaShop\Module\Mbo\Api\Exception;
if (!defined('_PS_VERSION_')) {
exit;
}
class QueryParamsException extends \Exception
{
}

View File

@@ -0,0 +1,30 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 3.0
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/
declare(strict_types=1);
namespace PrestaShop\Module\Mbo\Api\Exception;
if (!defined('_PS_VERSION_')) {
exit;
}
class RetrieveNewKeyException extends \Exception
{
}

View File

@@ -0,0 +1,30 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 3.0
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/
declare(strict_types=1);
namespace PrestaShop\Module\Mbo\Api\Exception;
if (!defined('_PS_VERSION_')) {
exit;
}
class UnauthorizedException extends \Exception
{
}

View File

@@ -0,0 +1,30 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 3.0
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/
declare(strict_types=1);
namespace PrestaShop\Module\Mbo\Api\Exception;
if (!defined('_PS_VERSION_')) {
exit;
}
class UnknownServiceException extends \Exception
{
}

View File

@@ -0,0 +1,28 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 3.0
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
header('Location: ../');
exit;

View File

@@ -0,0 +1,47 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 3.0
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/
declare(strict_types=1);
namespace PrestaShop\Module\Mbo\Api\Repository;
use Symfony\Component\Finder\Finder;
if (!defined('_PS_VERSION_')) {
exit;
}
class ModuleRepository
{
public function getInstalledModules(): array
{
$modules = [];
$modulesDirsList = (new Finder())->directories()
->in(_PS_MODULE_DIR_)
->depth('== 0')
->exclude(['__MACOSX'])
->ignoreVCS(true);
foreach ($modulesDirsList as $moduleDir) {
$modules[] = $moduleDir->getFilename();
}
return $modules;
}
}

View File

@@ -0,0 +1,28 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 3.0
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
header('Location: ../');
exit;

View File

@@ -0,0 +1,122 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 3.0
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/
declare(strict_types=1);
namespace PrestaShop\Module\Mbo\Api\Security;
use Doctrine\Common\Cache\CacheProvider;
use Firebase\JWT\JWT;
use PrestaShop\Module\Mbo\Api\Exception\UnauthorizedException;
use PrestaShop\Module\Mbo\Helpers\Config;
use PrestaShop\PrestaShop\Core\Context\ApiClientContext;
use PrestaShop\PrestaShop\Core\Context\EmployeeContext;
use PrestaShop\PrestaShop\Core\Domain\Employee\Exception\EmployeeException;
use PrestaShop\PrestaShop\Core\Exception\CoreException;
if (!defined('_PS_VERSION_')) {
exit;
}
class AdminAuthenticationProvider
{
public function __construct(
private readonly EmployeeContext $employeeContext,
private readonly ApiClientContext $apiClientContext,
private readonly CacheProvider $cacheProvider,
) {
}
/**
* @param \Employee $apiUser
*
* @return \Cookie
*
* @throws CoreException
*/
public function apiUserLogin(\Employee $apiUser): \Cookie
{
$cookie = new \Cookie('apiPsMbo');
$cookie->id_employee = (int) $apiUser->id;
// @phpstan-ignore-next-line
$cookie->email = $apiUser->email;
// @phpstan-ignore-next-line
$cookie->profile = $apiUser->id_profile;
$cookie->passwd = $apiUser->passwd;
// @phpstan-ignore-next-line
$cookie->remote_addr = $apiUser->remote_addr;
$cookie->registerSession(new \EmployeeSession());
if (!\Tools::getValue('stay_logged_in')) {
$cookie->last_activity = time();
}
$cookie->write();
return $cookie;
}
/**
* @throws EmployeeException
* @throws \Doctrine\DBAL\Exception
*/
public function getMboJWT(): string
{
$shopUrl = Config::getShopUrl();
$cacheKey = $this->getJwtTokenCacheKey();
if (!($jwtToken = $this->cacheProvider->fetch($cacheKey))) {
$mboToken = $this->getMboToken();
$jwtToken = JWT::encode([
'shop_url' => $shopUrl,
'mbo_version' => \ps_mbo::VERSION,
'ps_version' => _PS_VERSION_,
], $mboToken, 'HS256');
// Lifetime infinite, will be purged when MBO is uninstalled
$this->cacheProvider->save($cacheKey, $jwtToken, 0);
}
return $jwtToken;
}
public function clearCache(): void
{
$this->cacheProvider->delete($this->getJwtTokenCacheKey());
}
private function getMboToken(): string
{
if ($this->employeeContext->getEmployee()) {
$salt = $this->employeeContext->getEmployee()->getId();
} elseif ($this->apiClientContext->getApiClient()) {
$salt = $this->apiClientContext->getApiClient()->getId();
} else {
throw new UnauthorizedException('No employee or api client found');
}
return \Tools::getAdminToken('apiPsMbo' . \Tab::getIdFromClassName('apiPsMbo') . $salt);
}
private function getJwtTokenCacheKey(): string
{
return sprintf('mbo_jwt_token_%s', $this->getMboToken());
}
}

View File

@@ -0,0 +1,123 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 3.0
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/
declare(strict_types=1);
namespace PrestaShop\Module\Mbo\Api\Security;
use Doctrine\Common\Cache\CacheProvider;
use PrestaShop\Module\Mbo\Api\Exception\RetrieveNewKeyException;
use PrestaShop\Module\Mbo\Api\Exception\UnauthorizedException;
use PrestaShop\Module\Mbo\Distribution\Client;
use PrestaShop\Module\Mbo\Helpers\Config;
use PrestaShop\Module\Mbo\Helpers\ErrorHelper;
if (!defined('_PS_VERSION_')) {
exit;
}
class AuthorizationChecker
{
/**
* @var CacheProvider
*/
private $cacheProvider;
/**
* @var Client
*/
private $distributionClient;
/**
* @var string
*/
private $keyVersionCacheIndex;
/**
* @var AdminAuthenticationProvider
*/
private $adminAuthenticationProvider;
/**
* @var string
*/
private $keyCacheIndex;
public function __construct(
CacheProvider $cacheProvider,
Client $distributionClient,
AdminAuthenticationProvider $adminAuthenticationProvider,
) {
$this->cacheProvider = $cacheProvider;
$this->distributionClient = $distributionClient;
$this->adminAuthenticationProvider = $adminAuthenticationProvider;
$shopUuid = Config::getShopMboUuid();
$this->keyVersionCacheIndex = 'api_key_version_' . $shopUuid;
$this->keyCacheIndex = 'api_key_' . $shopUuid;
}
/**
* @throws UnauthorizedException
* @throws RetrieveNewKeyException
*/
public function verify(string $keyVersion, string $signature, string $message): void
{
$storedKeyVersion = null;
if ($this->cacheProvider->contains($this->keyVersionCacheIndex)) {
$storedKeyVersion = $this->cacheProvider->fetch($this->keyVersionCacheIndex);
}
if (
null === $storedKeyVersion
|| $storedKeyVersion !== $keyVersion
) {
// Ask for a new key and store keyVersion and Key
$this->retrieveNewKey();
}
$key = $this->cacheProvider->fetch($this->keyCacheIndex);
$verified = openssl_verify($message, base64_decode($signature), $key, OPENSSL_ALGO_SHA256);
if (1 !== $verified) {
throw new UnauthorizedException('Caller authorization failed');
}
}
/**
* @throws RetrieveNewKeyException
*/
private function retrieveNewKey()
{
try {
$this->distributionClient->setBearer($this->adminAuthenticationProvider->getMboJWT());
$response = $this->distributionClient->retrieveNewKey();
$key = $response->key;
$keyVersion = $response->version;
$this->cacheProvider->save($this->keyVersionCacheIndex, $keyVersion, 0);
$this->cacheProvider->save($this->keyCacheIndex, $key, 0);
} catch (\Throwable $e) {
ErrorHelper::reportError($e);
throw new RetrieveNewKeyException('Unable to retrieve signing key');
}
}
}

View File

@@ -0,0 +1,28 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 3.0
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
header('Location: ../');
exit;

View File

@@ -0,0 +1,89 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 3.0
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/
namespace PrestaShop\Module\Mbo\Api\Service;
use http\Exception\InvalidArgumentException;
use PrestaShop\Module\Mbo\Distribution\Config\Command\ConfigChangeCommand;
use PrestaShop\Module\Mbo\Distribution\Config\CommandHandler\ConfigChangeCommandHandler;
use PrestaShop\Module\Mbo\Distribution\Config\Exception\InvalidConfigException;
use PrestaShop\Module\Mbo\Helpers\ErrorHelper;
if (!defined('_PS_VERSION_')) {
exit;
}
class ConfigApplyExecutor implements ServiceExecutorInterface
{
const SERVICE = 'config';
/**
* @var ConfigChangeCommandHandler
*/
private ConfigChangeCommandHandler $configChangeCommandHandler;
public function __construct(ConfigChangeCommandHandler $configChangeCommandHandler)
{
$this->configChangeCommandHandler = $configChangeCommandHandler;
}
/**
* {@inheritDoc}
*/
public function canExecute(string $service): bool
{
return self::SERVICE === $service;
}
/**
* {@inheritDoc}
*/
public function execute(...$parameters): array
{
if (!$parameters[0] instanceof \Module) {
throw new InvalidArgumentException();
}
$module = $parameters[0];
try {
$config = json_decode(\Tools::getValue('conf'), true);
} catch (\JsonException $exception) {
ErrorHelper::reportError($exception);
throw new InvalidConfigException($exception->getMessage());
}
if ($config === null && json_last_error() !== JSON_ERROR_NONE) {
throw new InvalidConfigException('Config given is invalid. Please check the structure.');
}
$command = new ConfigChangeCommand(
$config,
_PS_VERSION_,
$module->version
);
$this->configChangeCommandHandler->handle($command);
return [
'message' => 'Config successfully applied',
];
}
}

View File

@@ -0,0 +1,69 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 3.0
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/
declare(strict_types=1);
namespace PrestaShop\Module\Mbo\Api\Service;
use PrestaShop\Module\Mbo\Api\Exception\UnknownServiceException;
if (!defined('_PS_VERSION_')) {
exit;
}
final class Factory
{
private const ALLOWED_SERVICES = [
ModuleTransitionExecutor::SERVICE,
ConfigApplyExecutor::SERVICE,
];
/**
* @param ServiceExecutorInterface[] $executors
*/
public function __construct(array $executors)
{
$this->executors = $executors;
}
/**
* @var ServiceExecutorInterface[]
*/
private $executors;
public function build(string $service): ServiceExecutorInterface
{
$this->assertServiceIsAllowed($service);
foreach ($this->executors as $executor) {
if ($executor->canExecute($service)) {
return $executor;
}
}
throw new UnknownServiceException('No executor have been found for that service');
}
private function assertServiceIsAllowed(string $service): void
{
if (!in_array($service, self::ALLOWED_SERVICES)) {
throw new UnknownServiceException(sprintf('Unknown service given : %s', $service));
}
}
}

View File

@@ -0,0 +1,202 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 3.0
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/
namespace PrestaShop\Module\Mbo\Api\Service;
use http\Exception\InvalidArgumentException;
use PrestaShop\Module\Mbo\Api\Exception\QueryParamsException;
use PrestaShop\Module\Mbo\Helpers\ErrorHelper;
use PrestaShop\Module\Mbo\Module\Command\ModuleStatusTransitionCommand;
use PrestaShop\Module\Mbo\Module\CommandHandler\ModuleStatusTransitionCommandHandler;
use PrestaShop\Module\Mbo\Module\Exception\ModuleNewVersionNotFoundException;
use PrestaShop\Module\Mbo\Module\Exception\ModuleNotFoundException;
use PrestaShop\Module\Mbo\Module\Exception\TransitionCommandToModuleStatusException;
use PrestaShop\Module\Mbo\Module\Exception\TransitionFailedException;
use PrestaShop\Module\Mbo\Module\Exception\UnauthorizedModuleTransitionException;
use PrestaShop\Module\Mbo\Module\Exception\UnexpectedModuleSourceContentException;
use PrestaShop\Module\Mbo\Module\Exception\UnknownModuleTransitionCommandException;
use PrestaShop\Module\Mbo\Module\Module;
use PrestaShop\Module\Mbo\Module\ValueObject\ModuleTransitionCommand;
use PrestaShop\PrestaShop\Adapter\Cache\Clearer\SymfonyCacheClearer;
use PrestaShop\PrestaShop\Core\Cache\Clearer\CacheClearerInterface;
use Symfony\Component\HttpFoundation\Session\Session;
if (!defined('_PS_VERSION_')) {
exit;
}
class ModuleTransitionExecutor implements ServiceExecutorInterface
{
const SERVICE = 'module';
/**
* @var ModuleStatusTransitionCommandHandler
*/
private $moduleStatusTransitionCommandHandler;
public function __construct(ModuleStatusTransitionCommandHandler $moduleStatusTransitionCommandHandler)
{
$this->moduleStatusTransitionCommandHandler = $moduleStatusTransitionCommandHandler;
}
/**
* {@inheritDoc}
*/
public function canExecute(string $service): bool
{
return self::SERVICE === $service;
}
/**
* {@inheritDoc}
*
* @throws UnknownModuleTransitionCommandException
* @throws QueryParamsException
* @throws ModuleNewVersionNotFoundException
* @throws ModuleNotFoundException
* @throws TransitionCommandToModuleStatusException
* @throws TransitionFailedException
* @throws UnauthorizedModuleTransitionException
* @throws UnexpectedModuleSourceContentException
* @throws \Exception
*/
public function execute(...$parameters): array
{
if (!$parameters[0] instanceof \Module) {
throw new InvalidArgumentException();
}
$psMbo = $parameters[0];
$transition = \Tools::getValue('action');
$moduleName = \Tools::getValue('module');
$moduleId = (int) \Tools::getValue('module_id');
$moduleVersion = \Tools::getValue('module_version');
$source = \Tools::getValue('source', null);
if (empty($transition) || empty($moduleName)) {
throw new QueryParamsException('You need transition and module parameters');
}
try {
$session = $psMbo->get('session');
if (!$session instanceof Session) {
throw new \Exception('ModuleTransitionExecutor : Session not found');
}
} catch (\Exception $e) {
ErrorHelper::reportError($e);
throw $e;
}
// Authenticate user to addons if credentials are provided
$this->authenticateAddonsUser($session);
$command = new ModuleStatusTransitionCommand($transition, $moduleName, $moduleId, $moduleVersion, $source);
$module = $this->moduleStatusTransitionCommandHandler->handle($command);
$moduleUrls = $module->get('urls');
$configUrl = $module->get('is_configurable') && isset($moduleUrls['configure'])
? $this->generateTokenizedModuleActionUrl($moduleUrls['configure'])
: null;
if (ModuleTransitionCommand::MODULE_COMMAND_DOWNLOAD === $transition) {
// Clear the cache after download to force reload module services
try {
/** @var CacheClearerInterface|false $cacheClearer */
$cacheClearer = $psMbo->get(SymfonyCacheClearer::class);
} catch (\Exception $e) {
ErrorHelper::reportError($e);
$cacheClearer = false;
}
if ($cacheClearer) {
$cacheClearer->clear();
}
}
return [
'message' => 'Transition successfully executed',
'module_status' => $module->getStatus(),
'version' => $module->get('version'),
'config_url' => $configUrl,
];
}
private function generateTokenizedModuleActionUrl($url): string
{
$components = parse_url($url);
$baseUrl = ($components['path'] ?? '');
$composedUrl = '';
if (!empty($components['scheme'])) {
$scheme = $components['scheme'];
$composedUrl .= $scheme . ':';
}
if (!empty($components['host'])) {
$composedUrl .= '//';
if (isset($components['user'])) {
$composedUrl .= $components['user'];
if (isset($components['pass'])) {
$composedUrl .= ':' . $components['pass'];
}
$composedUrl .= '@';
}
$composedUrl .= $components['host'];
// Only include the port if it is not the default port of the scheme
if (isset($components['port'])) {
$composedUrl .= ':' . $components['port'];
}
}
$composedUrl .= $baseUrl;
$queryParams = [];
if (is_array($components) && isset($components['query'])) {
parse_str($components['query'], $queryParams);
}
if (!isset($queryParams['_token'])) {
return $composedUrl;
}
$adminToken = \Tools::getValue('admin_token');
$queryParams['_token'] = $adminToken;
$composedUrl .= '?' . http_build_query($queryParams, '', '&');
if (isset($components['fragment']) && $components['fragment'] !== '') {
/* This copy-paste from Symfony's UrlGenerator */
$composedUrl .= '#' . strtr(rawurlencode($components['fragment']), ['%2F' => '/', '%3F' => '?']);
}
return $composedUrl;
}
private function authenticateAddonsUser(Session $session): void
{
// If we receive an accounts_token, we use it to connect to addons
$accountsToken = \Tools::getValue('accounts_token', null);
if (null !== $accountsToken) {
$session->set('accounts_token', $accountsToken);
}
}
}

View File

@@ -0,0 +1,46 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 3.0
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/
namespace PrestaShop\Module\Mbo\Api\Service;
if (!defined('_PS_VERSION_')) {
exit;
}
interface ServiceExecutorInterface
{
/**
* Checks whether a class can execute the given service.
*
* @param string $service
*
* @return bool
*/
public function canExecute(string $service): bool;
/**
* Executes the service with the parameters given.
*
* @param ...$parameters
*
* @return array
*/
public function execute(...$parameters): array;
}

View File

@@ -0,0 +1,28 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 3.0
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
header('Location: ../');
exit;

View File

@@ -0,0 +1,28 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 3.0
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
header('Location: ../');
exit;

View File

@@ -0,0 +1,109 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 3.0
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/
declare(strict_types=1);
namespace PrestaShop\Module\Mbo\Controller\Admin;
use PrestaShop\Module\Mbo\Helpers\ErrorHelper;
use PrestaShop\Module\Mbo\Module\Exception\ModuleUpgradeNotNeededException;
use PrestaShop\PrestaShop\Core\Module\ModuleManager;
use PrestaShop\PrestaShop\Core\Module\ModuleRepository;
use PrestaShopBundle\Controller\Admin\PrestaShopAdminController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
if (!defined('_PS_VERSION_')) {
exit;
}
class AddonsController extends PrestaShopAdminController
{
public function upgradeModuleAction(
Request $request,
ModuleManager $moduleManager,
ModuleRepository $moduleRepository,
): JsonResponse {
$moduleName = $request->request->get('moduleName');
if (null === $moduleName) {
return new JsonResponse(null, Response::HTTP_BAD_REQUEST);
}
try {
$upgradeResponse = [
'status' => $moduleManager->upgrade($moduleName),
'msg' => '',
'module_name' => $moduleName,
];
if ($upgradeResponse['status'] === true) {
$upgradeResponse['msg'] = $this->trans(
'Module %module% successfully upgraded.',
['%module%' => $moduleName],
'Modules.Mbo.Modulescatalog',
);
$upgradeResponse['is_configurable'] = (bool) $moduleRepository
->getModule($moduleName)
->attributes
->get('is_configurable');
} else {
$error = $moduleManager->getError($moduleName);
$upgradeResponse['msg'] = $this->trans(
'Upgrade of module %module% failed. %error%',
[
'%module%' => $moduleName,
'%error%' => $error,
],
'Modules.Mbo.Modulescatalog',
);
}
} catch (\Exception $e) {
ErrorHelper::reportError($e);
if ($e->getPrevious() instanceof ModuleUpgradeNotNeededException) {
$upgradeResponse['status'] = true;
$upgradeResponse['msg'] = $this->trans(
'Module %module% is already up to date',
[
'%module%' => $moduleName,
],
'Modules.Mbo.Modulescatalog',
);
} else {
try {
$moduleManager->disable($moduleName);
} catch (\Exception $subE) {
ErrorHelper::reportError($subE);
}
$upgradeResponse['msg'] = $this->trans(
'Upgrade of module %module% failed. %error%',
[
'%module%' => $moduleName,
'%error%' => $e->getMessage(),
],
'Modules.Mbo.Modulescatalog',
);
}
}
return new JsonResponse($upgradeResponse);
}
}

View File

@@ -0,0 +1,181 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 3.0
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/
declare(strict_types=1);
namespace PrestaShop\Module\Mbo\Controller\Admin;
use PrestaShop\Module\Mbo\Addons\Toolbar;
use PrestaShop\Module\Mbo\Helpers\ErrorHelper;
use PrestaShop\Module\Mbo\Service\View\ContextBuilder;
use PrestaShop\PrestaShop\Core\Security\Permission;
use PrestaShop\PsAccountsInstaller\Installer\Facade\PsAccounts;
use PrestaShop\PsAccountsInstaller\Installer\Installer;
use PrestaShopBundle\Controller\Admin\PrestaShopAdminController;
use PrestaShopBundle\Security\Attribute\AdminSecurity;
use Symfony\Component\HttpFoundation\Response;
if (!defined('_PS_VERSION_')) {
exit;
}
/**
* Responsible of "Improve > Modules > Modules Catalog" page display.
*/
class ModuleCatalogController extends PrestaShopAdminController
{
public const CONTROLLER_NAME = 'ADMINMODULESSF';
/**
* @var Toolbar
*/
private $addonsToolbar;
/**
* @var ContextBuilder
*/
private $contextBuilder;
/**
* @var PsAccounts
*/
private $psAccountsFacade;
/**
* @var Installer
*/
private $psAccountsInstaller;
public function __construct(
Toolbar $addonsToolbar,
ContextBuilder $contextBuilder,
PsAccounts $psAccountsFacade,
Installer $psAccountsInstaller,
) {
$this->addonsToolbar = $addonsToolbar;
$this->contextBuilder = $contextBuilder;
$this->psAccountsFacade = $psAccountsFacade;
$this->psAccountsInstaller = $psAccountsInstaller;
}
#[AdminSecurity(
"is_granted('read', request.get('_legacy_controller')) && is_granted('update', request.get('_legacy_controller')) && is_granted('create', request.get('_legacy_controller')) && is_granted('delete', request.get('_legacy_controller'))"
)]
public function indexAction(): Response
{
$extraParams = \ps_mbo::getCdcMediaUrl();
/*********************
* PrestaShop Account *
* *******************/
$urlAccountsCdn = '';
try {
$accountsService = $this->psAccountsFacade->getPsAccountsService();
} catch (\PrestaShop\PsAccountsInstaller\Installer\Exception\InstallerException $e) {
// Seems the module is not here, try to install it
$this->psAccountsInstaller->install();
try {
$accountsService = $this->psAccountsFacade->getPsAccountsService();
} catch (\Exception $e) {
// Installation seems to not work properly
$accountsService = null;
ErrorHelper::reportError($e);
}
}
if (null !== $accountsService) {
try {
\Media::addJsDef([
'contextPsAccounts' => $this->psAccountsFacade->getPsAccountsPresenter()
->present('ps_mbo'),
]);
// Retrieve the PrestaShop Account CDN
$urlAccountsCdn = $accountsService->getAccountsCdn();
} catch (\Exception $e) {
ErrorHelper::reportError($e);
}
}
return $this->render(
'@Modules/ps_mbo/views/templates/admin/controllers/module_catalog/catalog.html.twig',
[
'layoutHeaderToolbarBtn' => $this->addonsToolbar->getToolbarButtons(),
'layoutTitle' => $this->trans('Marketplace', [], 'Modules.Mbo.Modulescatalog'),
'requireAddonsSearch' => true,
'requireBulkActions' => false,
'showContentHeader' => true,
'enableSidebar' => true,
'help_link' => $this->generateSidebarLink('AdminModules'),
'requireFilterStatus' => false,
'level' => $this->authorizationLevel(static::CONTROLLER_NAME),
'shop_context' => $this->contextBuilder->getViewContext(),
'urlAccountsCdn' => $urlAccountsCdn,
'errorMessage' => $this->trans(
'You do not have permission to add this.',
[],
'Admin.Notifications.Error'
),
] + $extraParams
);
}
/**
* Responsible for displaying error block when CDC cannot be loaded.
*
* @return Response
*/
public function cdcErrorAction(): Response
{
return $this->render(
'@Modules/ps_mbo/views/templates/admin/controllers/module_catalog/cdc-error.html.twig'
);
}
/**
* Checks if the attributes are granted against the current authentication token and optionally supplied object.
*
* @param string $controller name of the controller that token is tested against
*
* @return int
*
* @throws \LogicException
*/
private function authorizationLevel($controller)
{
if ($this->isGranted(Permission::DELETE, $controller)) {
return Permission::LEVEL_DELETE;
}
if ($this->isGranted(Permission::CREATE, $controller)) {
return Permission::LEVEL_CREATE;
}
if ($this->isGranted(Permission::UPDATE, $controller)) {
return Permission::LEVEL_UPDATE;
}
if ($this->isGranted(Permission::READ, $controller)) {
return Permission::LEVEL_READ;
}
return 0;
}
}

View File

@@ -0,0 +1,80 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 3.0
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/
declare(strict_types=1);
namespace PrestaShop\Module\Mbo\Controller\Admin;
use PrestaShop\Module\Mbo\Service\View\ContextBuilder;
use PrestaShopBundle\Controller\Admin\PrestaShopAdminController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\ServiceUnavailableHttpException;
if (!defined('_PS_VERSION_')) {
exit;
}
/**
* Responsible of render json data for ajax display of Recommended Modules.
*/
class ModuleRecommendedController extends PrestaShopAdminController
{
/**
* @return JsonResponse|RedirectResponse
*/
public function indexAction(
Request $request,
ContextBuilder $contextBuilder,
): Response {
$response = new JsonResponse();
try {
$tabClassName = $request->get('tabClassName');
if (null === $tabClassName) { // In case the recommended modules page is requested without giving tab context, we redirect to Modules catalog page
$routeParams = [];
$query = \Tools::getValue('bo_query');
if (false !== $query && !empty(trim($query))) {
$routeParams['keyword'] = trim($query);
}
return $this->redirectToRoute('admin_mbo_catalog_module', $routeParams);
}
$context = $contextBuilder->getRecommendedModulesContext($tabClassName);
$context['recommendation_format'] = $request->get('recommendation_format');
$response->setData([
'content' => $this->renderView(
'@Modules/ps_mbo/views/templates/admin/controllers/module_catalog/recommended-modules.html.twig',
[
'shop_context' => $context,
]
),
]);
} catch (ServiceUnavailableHttpException $exception) {
$response->setData([
'content' => $this->renderView('@Modules/ps_mbo/views/templates/admin/error.html.twig'),
]);
$response->setStatusCode($exception->getStatusCode());
$response->headers->add($exception->getHeaders());
}
return $response;
}
}

View File

@@ -0,0 +1,75 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 3.0
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/
declare(strict_types=1);
namespace PrestaShop\Module\Mbo\Controller\Admin;
use PrestaShop\Module\Mbo\Addons\Provider\LinksProvider;
use PrestaShop\Module\Mbo\Service\ExternalContentProvider\ExternalContentProviderInterface;
use PrestaShopBundle\Controller\Admin\PrestaShopAdminController;
use PrestaShopBundle\Security\Attribute\AdminSecurity;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\ServiceUnavailableHttpException;
if (!defined('_PS_VERSION_')) {
exit;
}
/**
* Responsible of "Improve > Design > Themes Catalog" page display.
*/
class ThemeCatalogController extends PrestaShopAdminController
{
#[AdminSecurity("is_granted('read', request.get('_legacy_controller'))")]
public function indexAction(
Request $request,
LinksProvider $linksProvider,
ExternalContentProviderInterface $externalContentProvider,
): Response {
$response = new Response();
try {
$response->setContent($this->renderView(
'@Modules/ps_mbo/views/templates/admin/controllers/theme_catalog/addons_store.html.twig',
[
'pageContent' => $externalContentProvider->getContent(
$linksProvider->getSelectionLink()
),
'layoutHeaderToolbarBtn' => [],
'layoutTitle' => $this->trans('Themes Catalog', [], 'Modules.Mbo.Themescatalog'),
'requireAddonsSearch' => true,
'requireBulkActions' => false,
'showContentHeader' => true,
'enableSidebar' => true,
'help_link' => $this->generateSidebarLink($request->get('_legacy_controller')),
'requireFilterStatus' => false,
'level' => $this->getAuthorizationLevel($request->get('_legacy_controller')),
]
));
} catch (ServiceUnavailableHttpException $exception) {
$response->setContent($this->renderView('@Modules/ps_mbo/views/templates/admin/error.html.twig'));
$response->setStatusCode($exception->getStatusCode());
$response->headers->add($exception->getHeaders());
}
return $response;
}
}

View File

@@ -0,0 +1,28 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 3.0
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
header('Location: ../');
exit;

View File

@@ -0,0 +1,28 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 3.0
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
header('Location: ../');
exit;

View File

@@ -0,0 +1,86 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 3.0
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/
declare(strict_types=1);
namespace PrestaShop\Module\Mbo\DependencyInjection;
if (!defined('_PS_VERSION_')) {
exit;
}
/**
* Class responsible for returning cache directory path.
*/
class CacheDirectoryProvider
{
/**
* @var string PrestaShop version
*/
private $psVersion;
/**
* @var string PrestaShop path
*/
private $psPath;
/**
* @var bool PrestaShop Debug Mode
*/
private $psIsDebugMode;
public function __construct(string $psVersion, string $psPath, bool $psIsDebugMode)
{
$this->psVersion = $psVersion;
$this->psPath = $psPath;
$this->psIsDebugMode = $psIsDebugMode;
}
public function getPath(): string
{
if (defined('_PS_CACHE_DIR_')) {
return constant('_PS_CACHE_DIR_');
}
$path = '/var/cache/' . $this->getEnvName();
if (version_compare($this->psVersion, '1.7.0.0', '<')) {
$path = '/cache';
} elseif (version_compare($this->psVersion, '1.7.4.0', '<')) {
$path = '/app/cache/' . $this->getEnvName();
}
return $this->psPath . $path;
}
public function isWritable(): bool
{
return is_writable($this->getPath());
}
public function isReadable(): bool
{
return is_readable($this->getPath());
}
private function getEnvName(): string
{
return $this->psIsDebugMode ? 'dev' : 'prod';
}
}

View File

@@ -0,0 +1,110 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 3.0
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/
declare(strict_types=1);
namespace PrestaShop\Module\Mbo\DependencyInjection;
use Symfony\Component\Config\ConfigCache;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\Config\Loader\LoaderResolver;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\Dumper\PhpDumper;
use Symfony\Component\DependencyInjection\Loader\PhpFileLoader;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
if (!defined('_PS_VERSION_')) {
exit;
}
class ContainerProvider
{
/**
* @var string Module Name
*/
private $moduleName;
/**
* @var string Module Local Path
*/
private $moduleLocalPath;
/**
* @var CacheDirectoryProvider
*/
private $cacheDirectory;
public function __construct(
string $moduleName,
string $moduleLocalPath,
CacheDirectoryProvider $cacheDirectory,
) {
$this->moduleName = $moduleName;
$this->moduleLocalPath = $moduleLocalPath;
$this->cacheDirectory = $cacheDirectory;
}
public function get(string $containerName): ContainerInterface
{
$containerClassName = ucfirst($this->moduleName)
. ucfirst($containerName)
. 'Container'
;
$containerFilePath = $this->cacheDirectory->getPath() . '/' . $containerClassName . '.php';
$containerConfigCache = new ConfigCache($containerFilePath, _PS_MODE_DEV_);
if ($containerConfigCache->isFresh()) {
require_once $containerFilePath;
return new $containerClassName();
}
$containerBuilder = new ContainerBuilder();
$containerBuilder->set(
$this->moduleName . '.cache.directory',
$this->cacheDirectory
);
$moduleConfigPath = $this->moduleLocalPath
. 'config/services/'
;
$fileLocator = new FileLocator($moduleConfigPath);
$loader = new YamlFileLoader($containerBuilder, $fileLocator);
$loader->setResolver(new LoaderResolver([
new PhpFileLoader($containerBuilder, $fileLocator),
new XmlFileLoader($containerBuilder, $fileLocator),
]));
$loader->load('addons.yml');
$loader->load('distribution.yml');
$loader->load('accounts.yml');
$loader->load('handler.yml');
$loader->load('api/distribution.yml');
$containerBuilder->compile(true);
$dumper = new PhpDumper($containerBuilder);
$containerConfigCache->write(
$dumper->dump(['class' => $containerClassName]),
$containerBuilder->getResources()
);
return $containerBuilder;
}
}

View File

@@ -0,0 +1,84 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 3.0
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/
declare(strict_types=1);
namespace PrestaShop\Module\Mbo\DependencyInjection;
use Symfony\Component\DependencyInjection\ContainerInterface;
if (!defined('_PS_VERSION_')) {
exit;
}
class ServiceContainer
{
/**
* @var string Module Name
*/
private $moduleName;
/**
* @var string Module Local Path
*/
private $moduleLocalPath;
/**
* @var ContainerInterface
*/
private $container;
public function __construct(
string $moduleName,
string $moduleLocalPath,
) {
$this->moduleName = $moduleName;
$this->moduleLocalPath = $moduleLocalPath;
}
public function getService(string $serviceName): ?object
{
if (null === $this->container) {
$this->initContainer();
}
return $this->container->get($serviceName);
}
/**
* Instantiate a new ContainerProvider
*/
private function initContainer(): void
{
$cacheDirectory = new CacheDirectoryProvider(
_PS_VERSION_,
_PS_ROOT_DIR_,
_PS_MODE_DEV_
);
$containerProvider = new ContainerProvider($this->moduleName, $this->moduleLocalPath, $cacheDirectory);
$this->container = $containerProvider->get(
defined('_PS_ADMIN_DIR_')
|| defined('PS_INSTALLATION_IN_PROGRESS')
|| PHP_SAPI === 'cli' ?
'admin'
: 'front'
);
}
}

View File

@@ -0,0 +1,28 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 3.0
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
header('Location: ../');
exit;

View File

@@ -0,0 +1,231 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 3.0
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/
declare(strict_types=1);
namespace PrestaShop\Module\Mbo\Distribution;
use Doctrine\Common\Cache\CacheProvider;
use PrestaShop\Module\Mbo\Exception\ClientRequestException;
use PrestaShop\Module\Mbo\Helpers\Config;
use PrestaShop\PrestaShop\Core\Context\CountryContext;
use PrestaShop\PrestaShop\Core\Context\LanguageContext;
use Psr\Http\Client\ClientExceptionInterface;
use Psr\Http\Client\ClientInterface;
use Psr\Http\Message\RequestFactoryInterface;
if (!defined('_PS_VERSION_')) {
exit;
}
class BaseClient
{
public const HTTP_METHOD_GET = 'GET';
public const HTTP_METHOD_POST = 'POST';
protected string $apiUrl;
/**
* @var ClientInterface
*/
protected $httpClient;
/**
* @var RequestFactoryInterface
*/
protected RequestFactoryInterface $requestFactory;
/**
* @var CacheProvider
*/
protected $cacheProvider;
/**
* @var array<string, string>
*/
protected $queryParameters = [];
/**
* @var array<int, string>
*/
protected $possibleQueryParameters = [
'format',
'method',
'action',
'shop_uuid',
'shop_url',
'isoLang',
'shopVersion',
'ps_version',
'iso_lang',
'iso_code',
'accounts_token',
'addons_username',
'addons_pwd',
'catalogUrl',
];
/**
* @var array<string, string>
*/
protected $headers = [];
public function __construct(
string $apiUrl,
ClientInterface $httpClient,
RequestFactoryInterface $requestFactory,
CacheProvider $cacheProvider,
protected readonly LanguageContext $languageContext,
protected readonly CountryContext $countryContext,
) {
$this->apiUrl = $apiUrl;
$this->httpClient = $httpClient;
$this->requestFactory = $requestFactory;
$this->cacheProvider = $cacheProvider;
}
/**
* In case you reuse the Client, you may want to clean the previous parameters.
*/
public function reset(): void
{
$this->queryParameters = [];
$this->headers = [];
}
/**
* @param array $params
*
* @return $this
*/
public function setQueryParams(array $params): self
{
$filteredParams = array_intersect_key($params, array_flip($this->possibleQueryParameters));
$this->queryParameters = array_merge($this->queryParameters, $filteredParams);
return $this;
}
/**
* @param array $headers
*
* @return $this
*/
public function setHeaders(array $headers): self
{
$this->headers = array_merge($this->headers, $headers);
return $this;
}
/**
* @param string $jwt
*
* @return $this
*/
public function setBearer(string $jwt): self
{
return $this->setHeaders(['Authorization' => 'Bearer ' . $jwt]);
}
protected function mergeShopDataWithParams(array $params): array
{
return array_merge([
'uuid' => Config::getShopMboUuid(),
'shop_url' => Config::getShopUrl(),
'admin_path' => sprintf('/%s/', trim(str_replace(_PS_ROOT_DIR_, '', _PS_ADMIN_DIR_), '/')),
'mbo_version' => \ps_mbo::VERSION,
'ps_version' => _PS_VERSION_,
], $params);
}
/**
* Process the request with the current parameters, given the $method, and return the $attribute from
* the response body, or the default fallback value $default.
*
* @param string $uri
* @param array $options
* @param string $method
* @param mixed $default
*
* @return mixed
*
* @throws ClientExceptionInterface
*/
protected function processRequestAndDecode(
string $uri,
string $method = self::HTTP_METHOD_GET,
array $options = [],
$default = [],
) {
$response = json_decode($this->processRequest($uri, $method, $options));
if (JSON_ERROR_NONE !== json_last_error()) {
return (object) $default;
}
return $response;
}
/**
* Process the request with the current parameters, given the $method, return the body as string
*
* @param string $uri
* @param string $method
* @param array $options
*
* @return string
*
* @throws ClientExceptionInterface
* @throws ClientRequestException
*/
protected function processRequest(
string $uri = '',
string $method = self::HTTP_METHOD_GET,
array $options = [],
): string {
$queryString = !empty($this->queryParameters) ? '?' . http_build_query($this->queryParameters) : '';
$request = $this->requestFactory->createRequest($method, $this->apiUrl . '/api/' . ltrim($uri, '/') . $queryString);
if (empty($this->headers['Content-Type'])) {
$this->headers['Accept'] = 'application/json';
$this->headers['Content-Type'] = 'application/json';
}
foreach ($this->headers as $name => $value) {
$request = $request->withHeader($name, $value);
}
if (!empty($options['form_params'])) {
if ($this->headers['Content-Type'] === 'application/x-www-form-urlencoded') {
$request = $request->withBody($this->createStream(urlencode(serialize($options['form_params']))));
} else {
$request = $request->withBody($this->createStream(json_encode($options['form_params'])));
}
}
$response = $this->httpClient->sendRequest($request);
if ($response->getStatusCode() < 200 || $response->getStatusCode() >= 300) {
throw new ClientRequestException($response->getReasonPhrase(), $response->getStatusCode());
}
return $response->getBody()->getContents();
}
private function createStream(string $content): \Psr\Http\Message\StreamInterface
{
$psr17Factory = new \Nyholm\Psr7\Factory\Psr17Factory();
return $psr17Factory->createStream($content);
}
}

View File

@@ -0,0 +1,61 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 3.0
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/
declare(strict_types=1);
namespace PrestaShop\Module\Mbo\Distribution;
use PrestaShop\Module\Mbo\Helpers\ErrorHelper;
if (!defined('_PS_VERSION_')) {
exit;
}
class Client extends BaseClient
{
/**
* Get a new key from Distribution API.
*
* @return \stdClass
*/
public function retrieveNewKey(): \stdClass
{
return $this->processRequestAndDecode('shops/get-pub-key');
}
/**
* Send a tracking to the API
* Send it asynchronously to avoid blocking process for this feature
*
* @param array $eventData
*/
public function trackEvent(array $eventData): void
{
try {
$this->processRequestAndDecode(
'shops/events',
self::HTTP_METHOD_POST,
['form_params' => $eventData]
);
} catch (\Throwable $e) {
// Do nothing if the tracking fails
ErrorHelper::reportError($e);
}
}
}

View File

@@ -0,0 +1,117 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 3.0
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/
declare(strict_types=1);
namespace PrestaShop\Module\Mbo\Distribution\Config;
use Doctrine\DBAL\Query\QueryException;
use PrestaShop\Module\Mbo\Distribution\Config\Appliers\Factory as AppliersFactory;
use PrestaShop\Module\Mbo\Distribution\Config\Exception\InvalidConfigException;
if (!defined('_PS_VERSION_')) {
exit;
}
final class Applier
{
/**
* @var AppliersFactory
*/
private $appliersFactory;
public function __construct(AppliersFactory $appliersFactory)
{
$this->appliersFactory = $appliersFactory;
}
/**
* This method will receive an array of config objects and apply them.
*
* @param Config[] $configCollection
* @param string $psVersion
* @param string $mboVersion
*
* @throws QueryException
* @throws InvalidConfigException
*/
public function apply(array $configCollection, string $psVersion, string $mboVersion)
{
foreach ($configCollection as $config) {
if ($this->canBeApplied($config, $psVersion, $mboVersion)) {
$this->applyConfig($config);
}
}
}
/**
* This method will determinate if the config given can be applied depending on the psVersion and mboVersion.
*
* @param Config $config
* @param string $psVersion
* @param string $mboVersion
*
* @return bool
*/
private function canBeApplied(Config $config, string $psVersion, string $mboVersion): bool
{
return $this->versionCompareWithSemVer($psVersion, $config->getPsVersion(), '==')
&& $this->versionCompareWithSemVer($mboVersion, $config->getMboVersion(), '==')
&& true !== $config->isApplied();
}
/**
* @param Config $config
*
* @throws InvalidConfigException|QueryException
*/
private function applyConfig(Config $config): void
{
$applier = $this->appliersFactory->get($config->getConfigKey());
if (null === $applier) {
return;
}
if ($applier->apply($config) && null !== $config->getConfigId()) {
$sql = [];
$sql[] = 'UPDATE `' . _DB_PREFIX_ . 'mbo_api_config` SET `applied` = 1 WHERE `id_mbo_api_config`=' . $config->getConfigId();
$db = \Db::getInstance();
foreach ($sql as $query) {
if ($db->execute($query) === false) {
throw new QueryException($db->getMsgError());
}
}
}
}
private function versionCompareWithSemVer(string $a, string $b, string $operator)
{
$a = explode('.', $a);
$b = explode('.', $b);
$minSize = count($a) <= count($b) ? count($a) : count($b);
$versionA = implode('.', array_slice($a, 0, $minSize));
$versionB = implode('.', array_slice($b, 0, $minSize));
return version_compare($versionA, $versionB, $operator);
}
}

View File

@@ -0,0 +1,39 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 3.0
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/
declare(strict_types=1);
namespace PrestaShop\Module\Mbo\Distribution\Config\Appliers;
use PrestaShop\Module\Mbo\Distribution\Config\Config;
use PrestaShop\Module\Mbo\Distribution\Config\Exception\InvalidConfigException;
if (!defined('_PS_VERSION_')) {
exit;
}
interface ConfigApplierInterface
{
public function supports(string $configKey): bool;
/**
* @throws InvalidConfigException
*/
public function apply(Config $config): bool;
}

View File

@@ -0,0 +1,59 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 3.0
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/
declare(strict_types=1);
namespace PrestaShop\Module\Mbo\Distribution\Config\Appliers;
if (!defined('_PS_VERSION_')) {
exit;
}
final class Factory
{
/**
* @var ConfigApplierInterface[]
*/
private $configAppliers = [];
public function __construct(array $configAppliers)
{
foreach ($configAppliers as $configApplier) {
if ($configApplier instanceof ConfigApplierInterface) {
$this->configAppliers[] = $configApplier;
}
}
}
/**
* @param string $configKey
*
* @return ConfigApplierInterface|null
*/
public function get(string $configKey): ?ConfigApplierInterface
{
foreach ($this->configAppliers as $configApplier) {
if ($configApplier->supports($configKey)) {
return $configApplier;
}
}
return null;
}
}

View File

@@ -0,0 +1,79 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 3.0
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/
namespace PrestaShop\Module\Mbo\Distribution\Config\Appliers;
use PrestaShop\Module\Mbo\Distribution\Config\Config;
use PrestaShop\Module\Mbo\Distribution\Config\Exception\InvalidConfigException;
if (!defined('_PS_VERSION_')) {
exit;
}
class ModuleSelectionMenuConfigApplier implements ConfigApplierInterface
{
public function supports(string $configKey): bool
{
return $configKey === 'module_selection_menu_link';
}
/**
* @throws InvalidConfigException
*/
public function apply(Config $config): bool
{
$moduleSelectionMenu = $this->getModuleSelectionMenu();
if (null === $moduleSelectionMenu) {
return true;
}
$configValue = $config->getConfigValue();
if ('hide' === $configValue) {
return $this->applyDown($moduleSelectionMenu);
} elseif ('show' === $configValue) {
return $this->applyUp($moduleSelectionMenu);
} else {
throw new InvalidConfigException(sprintf('%s is not a valid config value', $configValue));
}
}
private function getModuleSelectionMenu()
{
$tab = \Tab::getInstanceFromClassName('AdminPsMboSelection');
return \Validate::isLoadedObject($tab) ? $tab : null;
}
private function applyUp(\Tab $moduleSelectionMenu): bool
{
$moduleSelectionMenu->enabled = true;
$moduleSelectionMenu->active = true;
return $moduleSelectionMenu->save();
}
private function applyDown(\Tab $moduleSelectionMenu): bool
{
$moduleSelectionMenu->enabled = false;
$moduleSelectionMenu->active = false;
return $moduleSelectionMenu->save();
}
}

View File

@@ -0,0 +1,79 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 3.0
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/
namespace PrestaShop\Module\Mbo\Distribution\Config\Appliers;
use PrestaShop\Module\Mbo\Distribution\Config\Config;
use PrestaShop\Module\Mbo\Distribution\Config\Exception\InvalidConfigException;
if (!defined('_PS_VERSION_')) {
exit;
}
class ThemeCatalogMenuConfigApplier implements ConfigApplierInterface
{
public function supports(string $configKey): bool
{
return $configKey === 'theme_catalog_menu_link';
}
/**
* @throws InvalidConfigException
*/
public function apply(Config $config): bool
{
$themeCatalogMenu = $this->getThemeCatalogMenu();
if (null === $themeCatalogMenu) {
return true;
}
$configValue = $config->getConfigValue();
if ('hide' === $configValue) {
return $this->applyDown($themeCatalogMenu);
} elseif ('show' === $configValue) {
return $this->applyUp($themeCatalogMenu);
} else {
throw new InvalidConfigException(sprintf('%s is not a valid config value', $configValue));
}
}
private function getThemeCatalogMenu()
{
$tab = \Tab::getInstanceFromClassName('AdminPsMboTheme');
return \Validate::isLoadedObject($tab) ? $tab : null;
}
private function applyUp(\Tab $themeCatalogMenu): bool
{
$themeCatalogMenu->enabled = true;
$themeCatalogMenu->active = true;
return $themeCatalogMenu->save();
}
private function applyDown(\Tab $themeCatalogMenu): bool
{
$themeCatalogMenu->enabled = false;
$themeCatalogMenu->active = false;
return $themeCatalogMenu->save();
}
}

View File

@@ -0,0 +1,28 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 3.0
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
header('Location: ../');
exit;

View File

@@ -0,0 +1,84 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 3.0
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/
declare(strict_types=1);
namespace PrestaShop\Module\Mbo\Distribution\Config\Command;
if (!defined('_PS_VERSION_')) {
exit;
}
class ConfigChangeCommand
{
/**
* @var array
*/
private $config;
/**
* This is the current PS version in the instance.
*
* @var string
*/
private $psVersion;
/**
* This is the current MBO module version in the instance.
*
* @var string
*/
private $mboVersion;
/**
* @param array $config
* @param string $psVersion
* @param string $mboVersion
*/
public function __construct(array $config, string $psVersion, string $mboVersion)
{
$this->config = $config;
$this->psVersion = $psVersion;
$this->mboVersion = $mboVersion;
}
/**
* @return array
*/
public function getConfig(): array
{
return $this->config;
}
/**
* @return string
*/
public function getPsVersion(): string
{
return $this->psVersion;
}
/**
* @return string
*/
public function getMboVersion(): string
{
return $this->mboVersion;
}
}

View File

@@ -0,0 +1,69 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 3.0
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/
declare(strict_types=1);
namespace PrestaShop\Module\Mbo\Distribution\Config\Command;
if (!defined('_PS_VERSION_')) {
exit;
}
class VersionChangeApplyConfigCommand
{
/**
* This is the current PS version in the instance. May be just upgraded.
*
* @var string
*/
private $psVersion;
/**
* This is the current MBO module version in the instance. May be just upgraded.
*
* @var string
*/
private $mboVersion;
/**
* @param string $psVersion
* @param string $mboVersion
*/
public function __construct(string $psVersion, string $mboVersion)
{
$this->psVersion = $psVersion;
$this->mboVersion = $mboVersion;
}
/**
* @return string
*/
public function getPsVersion(): string
{
return $this->psVersion;
}
/**
* @return string
*/
public function getMboVersion(): string
{
return $this->mboVersion;
}
}

View File

@@ -0,0 +1,28 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 3.0
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
header('Location: ../');
exit;

View File

@@ -0,0 +1,55 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 3.0
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/
declare(strict_types=1);
namespace PrestaShop\Module\Mbo\Distribution\Config\CommandHandler;
use PrestaShop\Module\Mbo\Distribution\Config\Applier;
use PrestaShop\Module\Mbo\Distribution\Config\Command\ConfigChangeCommand;
use PrestaShop\Module\Mbo\Distribution\Config\Factory;
if (!defined('_PS_VERSION_')) {
exit;
}
final class ConfigChangeCommandHandler
{
/**
* @var Factory
*/
private $configFactory;
/**
* @var Applier
*/
private $configApplier;
public function __construct(Factory $configFactory, Applier $configApplier)
{
$this->configFactory = $configFactory;
$this->configApplier = $configApplier;
}
public function handle(ConfigChangeCommand $command): void
{
$collection = $this->configFactory->buildAndSave($command->getConfig());
$this->configApplier->apply($collection, $command->getPsVersion(), $command->getMboVersion());
}
}

Some files were not shown because too many files have changed in this diff Show More