Add .gitignore and .htaccess for security

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
info@myprestarocks
2025-12-31 19:40:07 +01:00
commit d288c7af66
8 changed files with 438 additions and 0 deletions

14
.gitignore vendored Normal file
View File

@@ -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/

10
.htaccess Normal file
View File

@@ -0,0 +1,10 @@
# Apache 2.2
<IfModule !mod_authz_core.c>
Order deny,allow
Deny from all
</IfModule>
# Apache 2.4
<IfModule mod_authz_core.c>
Require all denied
</IfModule>

20
composer.json Normal file
View File

@@ -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"
}
}

147
src/AjaxResponseTrait.php Normal file
View File

@@ -0,0 +1,147 @@
<?php
/**
* Ajax Response Trait - Standardized JSON responses for PrestaShop modules
*
* Provides consistent AJAX response methods with proper headers and hook integration.
*
* Usage:
* use MyPrestaRocks\Compatibility\AjaxResponseTrait;
*
* class YourModule extends Module {
* use AjaxResponseTrait;
* }
*
* @author mypresta.rocks <info@mypresta.rocks>
* @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);
}
}

View File

@@ -0,0 +1,31 @@
<?php
/**
* Module Compatibility Trait - All-in-one compatibility trait for regular modules
*
* Combines AjaxResponseTrait and ModuleTemplateTrait for convenient single-use import.
* For payment modules, use PaymentModuleCompatibilityTrait instead.
*
* Usage:
* use MyPrestaRocks\Compatibility\ModuleCompatibilityTrait;
*
* class YourModule extends Module {
* use ModuleCompatibilityTrait;
* }
*
* @author mypresta.rocks <info@mypresta.rocks>
* @copyright Copyright (c) mypresta.rocks
* @license MIT
*/
namespace MyPrestaRocks\Compatibility;
if (!defined('_PS_VERSION_')) {
exit;
}
trait ModuleCompatibilityTrait
{
use AjaxResponseTrait;
use ModuleTemplateTrait;
}

110
src/ModuleTemplateTrait.php Normal file
View File

@@ -0,0 +1,110 @@
<?php
/**
* Module Template Trait - Simplified Smarty template rendering for PrestaShop modules
*
* Provides convenient methods for rendering Smarty templates within modules.
*
* Usage:
* use MyPrestaRocks\Compatibility\ModuleTemplateTrait;
*
* class YourModule extends Module {
* use ModuleTemplateTrait;
* }
*
* @author mypresta.rocks <info@mypresta.rocks>
* @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;
}
}

View File

@@ -0,0 +1,31 @@
<?php
/**
* Payment Module Compatibility Trait - All-in-one compatibility trait for payment modules
*
* Combines all compatibility traits including the PaymentModule carrier fix.
*
* Usage:
* use MyPrestaRocks\Compatibility\PaymentModuleCompatibilityTrait;
*
* class YourPaymentModule extends PaymentModule {
* use PaymentModuleCompatibilityTrait;
* }
*
* @author mypresta.rocks <info@mypresta.rocks>
* @copyright Copyright (c) mypresta.rocks
* @license MIT
*/
namespace MyPrestaRocks\Compatibility;
if (!defined('_PS_VERSION_')) {
exit;
}
trait PaymentModuleCompatibilityTrait
{
use AjaxResponseTrait;
use ModuleTemplateTrait;
use PaymentModuleTrait;
}

View File

@@ -0,0 +1,75 @@
<?php
/**
* Payment Module Trait - Fixes for PrestaShop PaymentModule bugs
*
* Fixes PrestaShop core bugs related to PaymentModule installation.
*
* Issue: When carriers are edited, PrestaShop creates new carrier records
* with the same id_reference. The core addCheckboxCarrierRestrictionsForModule()
* method doesn't deduplicate, causing duplicate key errors on payment module install.
*
* Usage:
* use MyPrestaRocks\Compatibility\PaymentModuleTrait;
*
* class YourPaymentModule extends PaymentModule {
* use PaymentModuleTrait;
* }
*
* @author mypresta.rocks <info@mypresta.rocks>
* @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;
}
}