From d288c7af66ec010e75b438e57d66a339baa1e1d0 Mon Sep 17 00:00:00 2001 From: "info@myprestarocks" Date: Wed, 31 Dec 2025 19:40:07 +0100 Subject: [PATCH] Add .gitignore and .htaccess for security MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- .gitignore | 14 +++ .htaccess | 10 ++ composer.json | 20 ++++ src/AjaxResponseTrait.php | 147 ++++++++++++++++++++++++ src/ModuleCompatibilityTrait.php | 31 +++++ src/ModuleTemplateTrait.php | 110 ++++++++++++++++++ src/PaymentModuleCompatibilityTrait.php | 31 +++++ src/PaymentModuleTrait.php | 75 ++++++++++++ 8 files changed, 438 insertions(+) create mode 100644 .gitignore create mode 100644 .htaccess create mode 100644 composer.json create mode 100644 src/AjaxResponseTrait.php create mode 100644 src/ModuleCompatibilityTrait.php create mode 100644 src/ModuleTemplateTrait.php create mode 100644 src/PaymentModuleCompatibilityTrait.php create mode 100644 src/PaymentModuleTrait.php diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..97c1e1e --- /dev/null +++ b/.gitignore @@ -0,0 +1,14 @@ +# IDE +.idea/ +.vscode/ +*.sublime-* + +# OS +.DS_Store +Thumbs.db + +# Node (if any build tools) +node_modules/ + +# Composer dev dependencies (if installed locally) +vendor/ diff --git a/.htaccess b/.htaccess new file mode 100644 index 0000000..3de9e40 --- /dev/null +++ b/.htaccess @@ -0,0 +1,10 @@ +# Apache 2.2 + + Order deny,allow + Deny from all + + +# Apache 2.4 + + Require all denied + diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..b1c72df --- /dev/null +++ b/composer.json @@ -0,0 +1,20 @@ +{ + "name": "myprestarocks/prestashop-compatibility", + "description": "Shared compatibility traits for PrestaShop modules - AJAX responses, templates, and core fixes", + "type": "library", + "license": "MIT", + "authors": [ + { + "name": "mypresta.rocks", + "email": "info@mypresta.rocks" + } + ], + "autoload": { + "psr-4": { + "MyPrestaRocks\\Compatibility\\": "src/" + } + }, + "require": { + "php": ">=7.1" + } +} diff --git a/src/AjaxResponseTrait.php b/src/AjaxResponseTrait.php new file mode 100644 index 0000000..3f22497 --- /dev/null +++ b/src/AjaxResponseTrait.php @@ -0,0 +1,147 @@ + + * @copyright Copyright (c) mypresta.rocks + * @license MIT + */ + +namespace MyPrestaRocks\Compatibility; + +if (!defined('_PS_VERSION_')) { + exit; +} + +trait AjaxResponseTrait +{ + /** + * Send a JSON response and exit + * + * Primary method for AJAX responses. Parameters ordered by frequency of use: + * - success: always needed + * - data: usually needed + * - message: optional, only when you need to show user feedback + * + * @param bool $success Whether the operation succeeded + * @param array $data Additional data to return + * @param string|null $message Optional message for user feedback + * @return void (exits) + */ + public function ajaxResponse($success, $data = [], $message = null) + { + $response = [ + 'success' => $success, + ]; + + if ($message !== null) { + $response['message'] = $message; + $response['confirmations'] = $message; + } + + if (!empty($data)) { + $response = array_merge($response, $data); + } + + $this->outputJsonResponse($response); + } + + /** + * Send a success response + * + * @param array $data Data to return + * @param string|null $message Optional success message + * @return void (exits) + */ + public function ajaxSuccess($data = [], $message = null) + { + $this->ajaxResponse(true, $data, $message); + } + + /** + * Send an error response + * + * @param string|null $message Error message + * @param array $data Additional error data + * @return void (exits) + */ + public function ajaxError($message = null, $data = []) + { + $this->ajaxResponse(false, $data, $message); + } + + /** + * Render standardized JSON response for AJAX calls + * + * @deprecated Use ajaxResponse(), ajaxSuccess(), or ajaxError() instead + * Kept for backward compatibility with existing code + * + * @param string $message Message to display + * @param bool $success Whether the operation succeeded + * @param array $data Additional data to return + * @return void (exits) + */ + public function renderMessage($message, $success = true, $data = []) + { + $this->ajaxResponse($success, $data, $message); + } + + /** + * Output JSON response with proper headers and hooks + * + * @param array $response Response array to encode + * @return void (exits) + */ + protected function outputJsonResponse($response) + { + $jsonResponse = json_encode($response); + + $controller = get_class($this); + $method = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS)[2]['function'] ?? 'unknown'; + + \Hook::exec('actionAjaxDieBefore', ['controller' => $controller, 'method' => $method, 'value' => $jsonResponse]); + \Hook::exec('actionBeforeAjaxDie' . $controller . $method, ['value' => $jsonResponse]); + \Hook::exec('actionAjaxDie' . $controller . $method . 'Before', ['value' => $jsonResponse]); + + header('Content-Type: application/json'); + header('Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0'); + + echo $jsonResponse; + exit(); + } + + /** + * Send HTTP error response code + * + * @param int $code HTTP status code (default 404) + * @return void + */ + public function respondInvalid($code = 404) + { + http_response_code($code); + } + + /** + * Send HTTP error response with JSON body + * + * @param int $code HTTP status code + * @param string|null $message Error message + * @param array $data Additional data + * @return void (exits) + */ + public function respondWithError($code, $message = null, $data = []) + { + http_response_code($code); + $this->ajaxError($message, $data); + } +} diff --git a/src/ModuleCompatibilityTrait.php b/src/ModuleCompatibilityTrait.php new file mode 100644 index 0000000..36bab76 --- /dev/null +++ b/src/ModuleCompatibilityTrait.php @@ -0,0 +1,31 @@ + + * @copyright Copyright (c) mypresta.rocks + * @license MIT + */ + +namespace MyPrestaRocks\Compatibility; + +if (!defined('_PS_VERSION_')) { + exit; +} + +trait ModuleCompatibilityTrait +{ + use AjaxResponseTrait; + use ModuleTemplateTrait; +} diff --git a/src/ModuleTemplateTrait.php b/src/ModuleTemplateTrait.php new file mode 100644 index 0000000..07ce512 --- /dev/null +++ b/src/ModuleTemplateTrait.php @@ -0,0 +1,110 @@ + + * @copyright Copyright (c) mypresta.rocks + * @license MIT + */ + +namespace MyPrestaRocks\Compatibility; + +if (!defined('_PS_VERSION_')) { + exit; +} + +trait ModuleTemplateTrait +{ + /** + * Render a Smarty template from the module's views/templates directory + * + * @param string $template Template filename without extension + * @param string $subfolder Subfolder within views/templates/ (with trailing slash if not empty) + * @param array $variables Variables to assign to template + * @return string Rendered template HTML + */ + public function renderTemplate($template, $subfolder = '', $variables = []) + { + $templatePath = $this->getLocalPath() . 'views/templates/' . $subfolder . $template . '.tpl'; + + if (!file_exists($templatePath)) { + return ''; + } + + if (!empty($variables)) { + $this->context->smarty->assign([$this->name => $variables]); + } + + return $this->context->smarty->fetch($templatePath); + } + + /** + * Render a front office template + * + * @param string $template Template filename without extension + * @param array $variables Variables to assign to template + * @return string Rendered template HTML + */ + public function renderFrontTemplate($template, $variables = []) + { + return $this->renderTemplate($template, 'front/', $variables); + } + + /** + * Render an admin template + * + * @param string $template Template filename without extension + * @param array $variables Variables to assign to template + * @return string Rendered template HTML + */ + public function renderAdminTemplate($template, $variables = []) + { + return $this->renderTemplate($template, 'admin/', $variables); + } + + /** + * Render a hook template (from hooks/ subfolder) + * + * @param string $template Template filename without extension + * @param array $variables Variables to assign to template + * @return string Rendered template HTML + */ + public function renderHookTemplate($template, $variables = []) + { + return $this->renderTemplate($template, 'hook/', $variables); + } + + /** + * Assign variables to Smarty and return for chaining + * + * @param array $variables Variables to assign + * @return $this + */ + public function assignTemplateVars($variables) + { + $this->context->smarty->assign($variables); + return $this; + } + + /** + * Assign module-namespaced variables to prevent conflicts + * + * @param array $variables Variables to assign under module namespace + * @return $this + */ + public function assignModuleVars($variables) + { + $this->context->smarty->assign([$this->name => $variables]); + return $this; + } +} diff --git a/src/PaymentModuleCompatibilityTrait.php b/src/PaymentModuleCompatibilityTrait.php new file mode 100644 index 0000000..d1276c0 --- /dev/null +++ b/src/PaymentModuleCompatibilityTrait.php @@ -0,0 +1,31 @@ + + * @copyright Copyright (c) mypresta.rocks + * @license MIT + */ + +namespace MyPrestaRocks\Compatibility; + +if (!defined('_PS_VERSION_')) { + exit; +} + +trait PaymentModuleCompatibilityTrait +{ + use AjaxResponseTrait; + use ModuleTemplateTrait; + use PaymentModuleTrait; +} diff --git a/src/PaymentModuleTrait.php b/src/PaymentModuleTrait.php new file mode 100644 index 0000000..cd999e7 --- /dev/null +++ b/src/PaymentModuleTrait.php @@ -0,0 +1,75 @@ + + * @copyright Copyright (c) mypresta.rocks + * @license MIT + */ + +namespace MyPrestaRocks\Compatibility; + +if (!defined('_PS_VERSION_')) { + exit; +} + +trait PaymentModuleTrait +{ + /** + * Override core method to fix duplicate carrier reference bug + * + * This method overrides the buggy core PaymentModule method that fails + * when carriers with duplicate id_reference exist (which happens when + * carriers are edited in the back office). + * + * @param array $shops Shop IDs to process (uses all shops if empty) + * @return bool + */ + public function addCheckboxCarrierRestrictionsForModule(array $shops = []) + { + if (!$shops) { + $shops = \Shop::getShops(true, null, true); + } + + $carriers = \Carrier::getCarriers( + (int) \Context::getContext()->language->id, + false, + false, + false, + null, + \Carrier::ALL_CARRIERS + ); + + // Deduplicate by id_reference to avoid duplicate key errors + $uniqueReferences = []; + foreach ($carriers as $carrier) { + $uniqueReferences[$carrier['id_reference']] = true; + } + + foreach ($shops as $idShop) { + foreach (array_keys($uniqueReferences) as $idReference) { + \Db::getInstance()->execute( + 'INSERT IGNORE INTO `' . _DB_PREFIX_ . 'module_carrier` + (`id_module`, `id_shop`, `id_reference`) + VALUES (' . (int) $this->id . ', ' . (int) $idShop . ', ' . (int) $idReference . ')' + ); + } + } + + return true; + } +}