Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ Adding DependencyBuilder #5

Merged
merged 11 commits into from
Dec 5, 2023
255 changes: 255 additions & 0 deletions src/DependencyBuilder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,255 @@
<?php

namespace Prestashop\ModuleLibMboInstaller;

use Symfony\Component\Routing\Router;

class DependencyBuilder
{
const DEPENDENCY_FILENAME = 'ps_dependencies.json';
intraordinaire marked this conversation as resolved.
Show resolved Hide resolved
const GET_PARAMETER = 'mbo_action_needed';

/**
* @var \ModuleCore
*/
protected $module;

/**
* @var Router
*/
protected $router;

/**
* @param \ModuleCore $module
*
* @throws \Exception
*/
public function __construct($module)
{
$this->module = $module;
$this->buildRouter();
}

/**
* Handle dependencies behavior and return dependencies data array to be given to the CDC
*
* @return array{
* "module_display_name": string,
* "module_name": string,
* "module_version": string,
* "ps_version": string,
* "php_version": string,
* "locale": string,
* "dependencies": array{}|array{ps_mbo: array<string, bool|string|int>}
* }
*
* @throws \Exception
*/
public function handleDependencies()
{
$this->handleMboInstallation();

return $this->buildDependenciesContext();
}

/**
* Install or enable the MBO depending on the action requested
*
* @return void
*/
protected function handleMboInstallation()
{
if (!isset($_GET[self::GET_PARAMETER]) || !$this->isMboNeeded()) {
return;
}

$mboStatus = (new Presenter())->present();
$installer = new Installer(_PS_VERSION_);

if ($mboStatus['isInstalled'] && $mboStatus['isEnabled']) {
return;
}

$data = [Installer::MODULE_NAME => [
'status' => true,
]];

try {
if (!$mboStatus['isInstalled']) {
$installer->installModule();
} elseif (!$mboStatus['isEnabled']) {
$installer->enableModule();
}
} catch (\Exception $e) {
$data[Installer::MODULE_NAME] = [
'status' => false,
'msg' => $e->getMessage(),
];
}

// This call is done in ajax by the CDC, bypass the normal return
header('Content-type: application/json');
echo json_encode($data);
exit;
}

/**
* Build the dependencies data array to be given to the CDC
*
* @return array{
* "module_display_name": string,
* "module_name": string,
* "module_version": string,
* "ps_version": string,
* "php_version": string,
* "locale": string,
* "dependencies": array{}|array{ps_mbo: array<string, bool|string|int>}
* }
*
* @throws \Exception
*/
protected function buildDependenciesContext()
{
$data = [
'module_display_name' => (string) $this->module->displayName,
'module_name' => (string) $this->module->name,
'module_version' => (string) $this->module->version,
'ps_version' => (string) _PS_VERSION_,
'php_version' => (string) PHP_VERSION,
'dependencies' => [],
];

$context = \ContextCore::getContext();
if ($context !== null && $context->employee !== null) {
$locale = \DbCore::getInstance()->getValue('SELECT `locale` FROM `' . _DB_PREFIX_ . 'lang` WHERE `id_lang`=' . pSQL((string) $context->employee->id_lang));
}

if (empty($locale)) {
$locale = 'en-GB';
}

$data['locale'] = (string) $locale;

$dependencyFile = $this->module->getLocalPath() . self::DEPENDENCY_FILENAME;
if (!file_exists($dependencyFile)) {
throw new \Exception(self::DEPENDENCY_FILENAME . ' file is not found in ' . $this->module->getLocalPath());
}

if ($fileContent = file_get_contents($dependencyFile)) {
$dependenciesContent = json_decode($fileContent, true);
}
if (!isset($dependenciesContent) || json_last_error() != JSON_ERROR_NONE) {
throw new \Exception(self::DEPENDENCY_FILENAME . ' file may be malformatted.');
}

if (!is_array($dependenciesContent) || empty($dependenciesContent['dependencies']) || !is_array($dependenciesContent['dependencies'])) {
$mboDependencyData = $this->addMboInDependencies();

if ($mboDependencyData) {
$data['dependencies'][Installer::MODULE_NAME] = $mboDependencyData;
}

return $data;
}

if ($this->isMboNeeded() && !isset($dependenciesContent['dependencies'][Installer::MODULE_NAME])) {
$dependenciesContent['dependencies'][] = [
'name' => Installer::MODULE_NAME,
'id' => Installer::MODULE_ID,
sowbiba marked this conversation as resolved.
Show resolved Hide resolved
];
}

foreach ($dependenciesContent['dependencies'] as $dependency) {
$dependencyData = \DbCore::getInstance()->getRow('SELECT `id_module`, `active`, `version` FROM `' . _DB_PREFIX_ . 'module` WHERE `name` = "' . pSQL((string) $dependency['name']) . '"');

$data['dependencies'][$dependency['name']] = array_merge($dependency, $this->buildRoutesForModule($dependency['name']));
if (!$dependencyData) {
$data['dependencies'][$dependency['name']]['installed'] = false;
continue;
}
$data['dependencies'][$dependency['name']] = array_merge($data['dependencies'][$dependency['name']], [
'installed' => true,
'enabled' => isset($dependencyData['active']) && (bool) $dependencyData['active'],
'current_version' => isset($dependencyData['version']) ? $dependencyData['version'] : null,
]);
}

return $data;
}

/**
* @param string $moduleName
*
* @return array<string, string>
*/
protected function buildRoutesForModule($moduleName)
{
$urls = [];
foreach (['install', 'enable', 'upgrade'] as $action) {
$urls[$action] = $this->router->generate('admin_module_manage_action', [
'action' => $action,
'module_name' => $moduleName,
]);
}

return $urls;
}

/**
* @return void
*
* @throws \Exception
*/
protected function buildRouter()
{
global $kernel;
if (!$kernel instanceof \AppKernel) {
throw new \Exception('Unable to retrieve Symfony AppKernel.');
}

$container = $kernel->getContainer();
if (!$container instanceof \Symfony\Component\DependencyInjection\ContainerInterface) {
throw new \Exception('Unable to retrieve Symfony container.');
}

$router = $container->get('router');
if (!$router instanceof Router) {
throw new \Exception('Unable to retrieve Symfony router.');
}
$this->router = $router;
}

/**
* @return array<string,bool|string|int>|null
*/
protected function addMboInDependencies()
{
if (!$this->isMboNeeded()) {
return null;
}

$mboStatus = (new Presenter())->present();

if ((bool) $mboStatus['isEnabled']) {
return null;
}

$mboRoutes = $this->buildRoutesForModule(Installer::MODULE_NAME);

return array_merge([
'current_version' => (string) $mboStatus['version'],
'installed' => (bool) $mboStatus['isInstalled'],
'enabled' => false,
'id' => Installer::MODULE_ID,
sowbiba marked this conversation as resolved.
Show resolved Hide resolved
'name' => Installer::MODULE_NAME,
], $mboRoutes);
}

/**
* @return bool
*/
protected function isMboNeeded()
{
return version_compare(_PS_VERSION_, '1.7.5', '>=');
}
}
19 changes: 19 additions & 0 deletions src/Installer.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use GuzzleHttp\Psr7\Request;
use Prestashop\ModuleLibGuzzleAdapter\ClientFactory;
use Prestashop\ModuleLibGuzzleAdapter\Interfaces\ClientExceptionInterface;
use Prestashop\ModuleLibGuzzleAdapter\Interfaces\HttpClientInterface;
use PrestaShop\PrestaShop\Core\Addon\Module\ModuleManagerBuilder;

Expand All @@ -30,6 +31,8 @@ class Installer

/**
* @param string $prestashopVersion
*
* @throws \Exception
*/
public function __construct($prestashopVersion)
{
Expand All @@ -47,6 +50,8 @@ public function __construct($prestashopVersion)
* Installs ps_mbo module
*
* @return bool
*
* @throws ClientExceptionInterface
*/
public function installModule()
{
Expand All @@ -59,10 +64,24 @@ public function installModule()
return $this->moduleManagerBuilder->build()->install(self::MODULE_NAME);
}

/**
* Enable ps_mbo module
*
* @return bool
*
* @throws \Exception
*/
public function enableModule()
{
return $this->moduleManagerBuilder->build()->enable(self::MODULE_NAME);
}

/**
* Downloads ps_mbo module source from addons, store it and returns the file name
*
* @return string
*
* @throws \Exception|ClientExceptionInterface
*/
private function downloadModule()
{
Expand Down