This commit is contained in:
2026-05-10 21:32:38 +02:00
parent d6f5706a9f
commit 80a3cb3814
10 changed files with 1219 additions and 0 deletions

View File

@@ -0,0 +1,158 @@
# Architecture
**Analysis Date:** 2026-05-10
## Pattern Overview
**Overall:** PrestaShop 1.7.8.x MVC e-commerce monolith with hook-based module extensibility and a Symfony admin layer.
**Key Characteristics:**
- Front-office: legacy PrestaShop dispatcher + Smarty templates
- Back-office (admin): Symfony 4-style kernel (`app/AppKernel.php`) at non-standard path `iadmin/`
- Hook-based publish/subscribe extension model — modules attach to named hooks
- Core class overrides via `override/classes/` (monkey-patch layer)
- Multi-domain (`drmaterac.pl` + `lulandia.pl`) served from a single install via `.htaccess` rules
## Layers
**Entry / Routing Layer:**
- Purpose: Route HTTP requests via mod_rewrite to dispatcher / Symfony kernel
- Contains: `.htaccess` (429 lines), `index.php` (front), `iadmin/index.php` (back-office)
- Used by: All HTTP requests
- Key class: `classes/Dispatcher.php` (overridden in `override/classes/Dispatcher.php`)
**Controller Layer:**
- Purpose: Handle request-specific logic (cart, product, checkout, search)
- Contains: `controllers/front/*.php` (CartController, OrderController, ProductController…)
- Base classes: `classes/controller/FrontController.php`, `ModuleFrontController.php`, `AdminController.php`, `ProductListingFrontController.php`
- Overrides: `override/classes/controller/FrontController.php`, `override/classes/controller/ProductListingFrontController.php`
**Model / Domain Layer (ObjectModel):**
- Purpose: ORM-style domain entities (Product, Cart, Order, Customer, Category…)
- Contains: `classes/Product.php`, `classes/Cart.php`, `classes/Order.php`, `classes/Customer.php`, etc.
- Base: `classes/ObjectModel.php`
- Overrides: 31 files in `override/classes/` (e.g. `override/classes/Product.php` adds Google Shopping fields; `override/classes/Cart.php` extends `getOrderTotal()` for ets_promotion pricing)
**Module Layer (extensibility):**
- Purpose: Optional features attached via hooks; first-class extension mechanism
- Contains: `modules/*/` (50+ installed modules, ~138 entries on disk)
- Custom in this project: `modules/crosssellpro/` (Cross Sell PRO carousel), `modules/caraty/`
- Active third-party: `modules/paynow/`, `modules/santandercredit/`, `modules/empikmarketplace/`, `modules/dpdpoland/`, `modules/pdgoogleanalytycs4pro/`, `modules/fbpixel/`, `modules/ekomiratingsandreviews/`, `modules/ceneo_trustedreviews/`, etc.
**Theme / Presentation Layer:**
- Purpose: Page templates and visual assets
- Contains: `themes/leo_gstore/` (active child theme, Leo Gstore v1.4)
- Templates: `themes/leo_gstore/templates/*.tpl` (layout, cart, checkout, product…)
- Per-module overrides: `themes/leo_gstore/modules/<module>/views/templates/hook/*.tpl`
- Assets: `themes/leo_gstore/assets/cache/` (bundled JS/CSS)
**Cross-cutting / Hook System:**
- `classes/Hook.php` + `override/classes/Hook.php` (PageCache module integration)
- Hooks dispatch from controllers/templates → fire each registered module's `hook<Name>()` method
- Registration table: `ps_hook_module`
## Data Flow
**HTTP Request lifecycle (front-office):**
1. Apache receives request → `.htaccess` rewrite rule (line ~353) routes non-physical URLs to `index.php`
2. `Dispatcher::getInstance()->dispatch()` resolves URL to a controller (e.g. `CartController`)
3. Controller `init()` / `initContent()` runs; sets up `Context` (cart, customer, shop, language)
4. Controller dispatches display hooks (`displayHeader`, `displayShoppingCartFooter`, `actionFrontControllerSetMedia`, etc.)
5. Each subscribing module's `hook<Name>()` method runs and may render Smarty partials
6. Smarty renders the theme template (`themes/leo_gstore/templates/cart.tpl`) injecting hook output
7. Response (HTML + CSS + JS) returned to client
**Admin lifecycle:**
- `iadmin/index.php` boots `app/AppKernel.php` (Symfony) — `iadmin/` is the renamed admin folder (security-by-obscurity)
- Symfony routes to controllers; falls back to legacy `Dispatcher` if Symfony routing fails (`iadmin/index.php` line ~93)
**API:**
- `webservice/dispatcher.php` — REST endpoint reached via `.htaccess` line ~323
**State Management:**
- Per-request state held in `classes/Context.php` singleton (`$context->cart`, `$context->customer`, `$context->shop`, `$context->controller`, `$context->language`)
- Persistent state in MySQL (`materac_*` tables)
- Caching configured but disabled (Memcached driver inactive)
## Key Abstractions
**Module:**
- Purpose: Attachable feature unit registering hook callbacks
- Examples: `modules/crosssellpro/crosssellpro.php`, `modules/paynow/paynow.php`, `modules/fbpixel/fbpixel.php`
- Pattern: subclasses `Module` (`classes/Module.php`); `install()` registers hooks; `hook<Name>()` methods are invoked dynamically
**ObjectModel:**
- Purpose: ORM base for domain entities — handles `add()`, `update()`, `delete()`, hydration, validation
- Examples: `classes/Product.php`, `classes/Cart.php`, `classes/Order.php`
- Pattern: declarative `$definition` array describes table + fields
**Hook (publish/subscribe):**
- Purpose: Decouple core from extensions
- Examples: `displayShoppingCartFooter`, `displayCheckoutSummaryTop`, `displayHeader`, `actionFrontControllerSetMedia`
- Pattern: `Hook::exec('hookName', ...)` dispatches; `$module->hookHookName(...)` receives
**Context:**
- Purpose: Per-request global container (cart, customer, shop, language, controller)
- Pattern: Singleton — `Context::getContext()`
**Override:**
- Purpose: Patch core class behavior without editing core
- Examples: `override/classes/Cart.php` (custom totals), `override/classes/Hook.php` (PageCache hooks)
- Pattern: Files in `override/classes/<X>.php` override `classes/<X>.php` via PrestaShop autoloader
## Entry Points
**Front-office:**
- Location: `index.php` (root) — invoked implicitly via `.htaccess`
- Triggers: Any non-physical URL on the public domains
- Responsibilities: Bootstrap → Dispatcher → controller dispatch
**Back-office:**
- Location: `iadmin/index.php` (custom-named admin folder)
- Triggers: Admin URL access
- Responsibilities: Boot `AppKernel`, fall back to legacy dispatcher when needed
**Web service:**
- Location: `webservice/dispatcher.php`
- Triggers: REST API calls
**Custom Cross-Sell module:**
- Location: `modules/crosssellpro/crosssellpro.php`
- Hooks registered (in `install()`): `displayShoppingCartFooter`, `displayCheckoutSummaryTop`, `displayHeader`, `actionFrontControllerSetMedia`
- Key methods: `hookDisplayShoppingCartFooter()`, `hookDisplayCheckoutSummaryTop()`, `buildCrossSellProducts()`, `collectAccessoryIds()`, `getCombinationFlags()`, `presentProducts()`
- Templates: `modules/crosssellpro/views/templates/hook/cartCrossSell.tpl`, `…/checkoutCrossSell.tpl`
- Front assets: `modules/crosssellpro/views/css/cartCrossSell.css`, `modules/crosssellpro/views/js/cartCrossSell.js`
## Error Handling
**Strategy:** PrestaShop legacy approach — `Tools::displayError()`, exceptions logged to `iadmin/errors.log` and `errors.log` (root). Exceptions in hooks may silently degrade module output.
**Patterns:**
- `try/catch` rare in custom code; `import-product.php` lacks transaction wrapping/rollback
- Mail send paths in `buy-by-phone.php` minimally handle PHPMailer failures
- `errors.log` at repo root + `iadmin/errors.log` (5 MB historical) — both inside webroot (concern, see `concerns.md`)
## Cross-Cutting Concerns
**Logging:**
- File-based: `errors.log` (root), `iadmin/errors.log`
- Symfony Monolog wired in admin via `MonologBundle`
**Caching:**
- `pagecache` module
- Memcached configured but disabled (`app/config/parameters.php`)
- Smarty compiled templates: `themes/leo_gstore/cache/`
**Authentication:**
- Customer auth: PrestaShop core (cookies + DB)
- Admin auth: Symfony Security via `iadmin/`
**Image handling:**
- WebP delivery via `.htaccess` content negotiation + `modules/x13webp/`
- Lazy-loading image override (`pshowlazyimg` override)
---
*Architecture analysis: 2026-05-10*
*Update when major patterns change*

137
.paul/codebase/concerns.md Normal file
View File

@@ -0,0 +1,137 @@
# Codebase Concerns
**Analysis Date:** 2026-05-10
> Severity legend: **CRITICAL** (immediate fix), **HIGH** (next sprint), **MEDIUM** (planned), **LOW** (track).
## Critical Security
**Hardcoded credentials committed to repository — CRITICAL**
- Files: `buy-by-phone.php` (lines ~1128: reCAPTCHA secret `6LeJAUUsAAAAAIyCrwFMrsw9yQLPgnCWCOHPcjc8`, SMTP passwords `8njIZnAb`, `kM4BU_F_`), `app/config/parameters.php` (DB password `eRbZ]Ioh-0-2]fM+`, app secret `LStbzv3bfsMaq5dvsVR5wXt8dEpe63IAx2uOHffpbK9gy9x4EZsjWeRM`, cookie keys)
- Why: convenience during development — never moved to env vars
- Impact: full DB compromise, mail account takeover, session forgery if repo or backups leak; secrets remain valid in git history
- Fix: rotate ALL exposed credentials immediately → move to environment variables (or Docker secrets) → load via `getenv()` → purge from git history (`git filter-repo` / BFG) → add the file to `.gitignore`
**Diagnostic / phpinfo files exposed in webroot — CRITICAL**
- Files: `diag_20792_tmp.php` (2.6 KB), `diag_upload_tmp.php` (4.4 KB), `diag_fix_tmp.php` (14 B), `info.php` (14 B — almost certainly `<?php phpinfo();`)
- Why: ad-hoc debug scripts left after a 2026-03-05 incident (`feat: update diagnostic scripts and configuration for improved product data handling`, commit `194405bc`)
- Impact: `info.php` discloses full PHP environment to anyone; diag scripts run raw SQL with weak `$_GET['key'] !== 'diag2026'` gate (security through obscurity, easily bypassed)
- Fix: delete these 4 files now; if production deploy mirrors repo, also delete on server; consider reverting commit `194405bc`
**Unsanitized `$_POST` / `$_GET` in custom scripts — HIGH**
- Files: `buy-by-phone.php` lines 2958 (17 direct `$_POST[...]` accesses) and lines 8394 (POST values injected into HTML email body — XSS via mail / SMTP header injection); `category-description.php` line 7 (`$url = $_GET['url']`)
- Why: written outside PrestaShop conventions
- Impact: header injection, mail-relay abuse, stored XSS in support inboxes
- Fix: replace with `Tools::getValue('field', '')` (PrestaShop sanitizer) and escape on output (`htmlspecialchars`, `Tools::safeOutput`); validate email with `Validate::isEmail()`; add reCAPTCHA verification result check before send
**SQL string concatenation in custom scripts — HIGH**
- Files: `diag_20792_tmp.php` lines 2349, `diag_upload_tmp.php` lines 2250, `import-product.php` lines 6393
- Why: legacy PrestaShop pattern (`pSQL()` instead of bound parameters)
- Impact: any unsanitized input flowing into these queries is an injection vector
- Fix: delete diag scripts; refactor `import-product.php` to use `Db::getInstance()` with proper escaping or Doctrine DBAL parameterized queries; never concatenate user input into SQL
**Database backups + 5 MB error log inside webroot — HIGH**
- Files: `iadmin/backups/` (e.g. `1698930967-7683b7af.sql.bz2`), `iadmin/errors.log` (5 MB, 26 months of PHP errors), `errors.log` (root)
- Why: standard PrestaShop default location; `iadmin/` is the renamed admin folder (still under DocumentRoot)
- Impact: SQL dumps directly downloadable if URL guessed; error log discloses paths and module internals
- Fix: move backups outside DocumentRoot or `Deny from all` via `.htaccess`; rotate logs to `/var/log/...`; add `errors.log`, `*.log`, `iadmin/backups/`, `iadmin/errors.log` to `.gitignore`
## Tech Debt
**Backup and obsolete files committed — HIGH**
- Files: `backup_before_patch/As4SearchEngine.php.backup` (160 KB), `backup_before_patch/As4SearchEngineDb.php.backup` (3.4 KB), `backup_before_patch/pm_advancedsearch4.php.backup` (279 KB)
- Why: manual snapshot taken before patching the search engine module
- Impact: bloats repo, confuses code search, may shadow current implementation in IDEs
- Fix: delete the directory; rely on `git log` for history; add `*.backup`, `backup_before_patch/` to `.gitignore`
**Oversized root-level scripts — MEDIUM**
- Files: `import-product.php` (813 lines / 32 KB — fetches and applies external XML feed), `buy-by-phone.php` (122 lines — manual PHPMailer + reCAPTCHA)
- Why: written as single-file utilities
- Impact: hard to test, no transaction safety in importer, no logging of import results
- Fix approach: extract `import-product.php` into a proper PrestaShop module or CLI command (`bin/console`-style), wrap DB writes in transactions, add structured logging; convert `buy-by-phone.php` into a `ModuleFrontController`
**429-line `.htaccess` with hardcoded SEO redirects — MEDIUM**
- File: `.htaccess` (~100 manual `RewriteRule ^...$ /s/.../...?` redirects, lines 14370+)
- Impact: not maintainable, regex conflicts likely, no audit trail
- Fix: migrate to a database-driven redirect table + module that emits 301s in `hookActionDispatcher` (or use `modules/x13linkrewrite/`)
**107 undocumented core overrides — MEDIUM**
- Files: `override/classes/` (31 files — `Cart.php`, `CartRule.php`, `Product.php`, `Hook.php`, `Dispatcher.php`, `controller/FrontController.php`, `controller/ProductListingFrontController.php`, …) plus 76 more under `override/`
- Why: each accumulated over time to extend core behavior
- Impact: every PrestaShop upgrade requires manual override review; no inline doc states why each override exists
- Fix: add a header comment to each override stating purpose + ticket; consider moving simple cases to module hooks
**Stale stack — PrestaShop 1.7.8.11 / PHP 7.4 — MEDIUM**
- File: `app/AppKernel.php` line 35 (`const VERSION = '1.7.8.11'`)
- Why: production stability
- Impact: missing security patch 1.7.8.12; PHP 7.4 reached EOL 2022-11-28 (no upstream security fixes); core 1.7.x EOL since 2021-10
- Fix: apply 1.7.8.12 patch first (minor), then plan migration to PrestaShop 8.x or 9.x; PHP 8.2 already in runtime — verify all modules under PHP 8.x compat-checker
**`iadmin/autoupgrade/` contains `prestashop_1.7.5.1.zip` left in webroot — MEDIUM**
- Why: artifact of a past upgrade attempt
- Impact: downloadable archive exposing core source paths
- Fix: delete
## Performance
**Custom cross-sell module clean — no concerns**
- File: `modules/crosssellpro/crosssellpro.php`
- Uses `Product::getAccessoriesLight()` (efficient) + `Product::getProductsProperties()`; filters with `Shop::addSqlAssociation`; respects `DEFAULT_LIMIT = 12`
- No N+1 detected
**No application-level caching active**
- Memcached configured but disabled (`app/config/parameters.php`)
- Page cache module installed (`modules/pagecache/`) — verify it is enabled in production
- Fix: enable Memcached + verify pagecache configuration; add OPcache validation if not on
## Fragile Areas
**Override chain in `override/classes/`**
- Why fragile: `Cart::getOrderTotal()` extension depends on `ets_promotion` module presence (`override/classes/Cart.php` line ~55); `Hook::exec` extension depends on `pagecache` (`override/classes/Hook.php` line ~42)
- Common failures: removing one of those modules without cleaning up the override leaves dead branches
- Safe modification: read the override + the referenced module's hook surface together; never edit core `classes/` directly
**Recurring header-modification errors in admin**
- Source: `iadmin/errors.log` shows repeated errors at `AdminCartRulesController.php:121` (output before `header()` call)
- Trigger: editing cart rules in back-office
- Likely cause: a module emitting whitespace before `<?php` or echoing in `init()`
**MySQL "server has gone away" entries**
- Source: `iadmin/errors.log`
- Likely cause: long-running scripts (`import-product.php`?) exceeding `wait_timeout`
- Fix: increase `wait_timeout`/`mysql.connect_timeout` in PHP config or call `Db::getInstance()->disconnect()` + reconnect for long-running batch jobs
## Test Coverage Gaps
- **Custom modules have NO tests** — `modules/crosssellpro/`, `modules/caraty/`
- **No tests for `import-product.php`** (highest-risk script — bulk DB writes, no rollback)
- **No tests for `buy-by-phone.php`** (handles user input + sends mail)
- Risk: regressions land silently; manual browser testing is the only safety net
## Documentation Gaps
- No `README.md` at repo root (only PrestaShop's stock files)
- 31 core overrides — none have purpose comments
- `iadmin/errors.log` retains 26 months of error history but no rotation policy
## Deletion Candidates (Quick Wins)
```
diag_20792_tmp.php
diag_fix_tmp.php
diag_upload_tmp.php
info.php
errors.log
backup_before_patch/
iadmin/errors.log
iadmin/autoupgrade/prestashop_1.7.5.1.zip
themes/classic.zip # (verify needed)
themes/leo_gstore.zip
```
Add to `.gitignore`: `errors.log`, `*.log`, `*.backup`, `backup_before_patch/`, `iadmin/backups/`, `iadmin/errors.log`, `iadmin/autoupgrade/*.zip`.
---
*Concerns audit: 2026-05-10*
*Update as issues are fixed or new ones discovered*

View File

@@ -0,0 +1,150 @@
# Coding Conventions
**Analysis Date:** 2026-05-10
## Naming Patterns
**Files:**
- PHP class files: PascalCase matching class — `classes/Product.php`, `classes/Cart.php`
- Module entry file: lowercase, matches directory — `modules/crosssellpro/crosssellpro.php`
- Smarty templates: camelCase per module — `cartCrossSell.tpl`, `checkoutCrossSell.tpl`
- Asset files: camelCase — `cartCrossSell.css`, `cartCrossSell.js`
**Functions / Methods:**
- camelCase — `buildCrossSellProducts()`, `collectAccessoryIds()`, `getCombinationFlags()`, `presentProducts()` (`modules/crosssellpro/crosssellpro.php`)
- Hook handlers: literal prefix `hook` + PascalCase hook name — `hookDisplayShoppingCartFooter()`, `hookActionFrontControllerSetMedia()`
**Variables:**
- New custom code: `$camelCase``$cartProducts`, `$inCartProductIds`, `$productId`
- Legacy / DB-driven code: `$snake_case``$id_product`, `$id_cart`, `$id_shop` (mirrors PrestaShop column names; expected when interacting with ObjectModel data)
**Classes:**
- PascalCase, suffixed `Core` for PrestaShop core — `class ProductCore extends ObjectModel` (`classes/Product.php`)
- Module classes: PascalCase matching folder — `class Crosssellpro extends Module`
**Hook names (string identifiers):**
- camelCase, registered as strings — `displayShoppingCartFooter`, `displayHeader`, `actionFrontControllerSetMedia`
## Code Style
**PHP Formatting:**
- 4-space indentation (no tabs)
- PSR-12-compatible style for new custom code
- Opening braces on same line: `class Crosssellpro extends Module {`
- Single quotes preferred for strings
- Escape filter pattern in templates: `{$product.name|escape:'htmlall':'UTF-8'}`
**JavaScript Formatting:**
- 2-space indentation
- Legacy ES5 in custom module JS (`var` declarations) — `modules/crosssellpro/views/js/cartCrossSell.js`
- camelCase for functions/variables
- Data-attribute targeting: `[data-crosssellpro-block="1"]`
**Smarty:**
- `{$variable|filter:'arg':'arg'}` for output with escaping
- `{if}…{/if}`, `{foreach}…{/foreach}`, `{assign}` block syntax
**Linting:**
- No root-level `.php-cs-fixer.php` or `phpstan.neon`
- Per-module PHPStan/PHP-CS-Fixer config available in some PrestaShop modules (`modules/<m>/tests/phpstan/phpstan.neon`)
## Import Organization
**PHP:**
- `<?php` opener with no namespace declared in custom modules (PrestaShop legacy autoloader convention)
- `use` statements for Symfony / vendor classes when needed
- No enforced ordering — follow existing file's pattern
**JS:**
- Vanilla DOM manipulation in custom module JS — no module bundler
- Theme JS bundled in `themes/leo_gstore/assets/cache/`
## Error Handling
**Patterns:**
- PrestaShop convention: input via `Tools::getValue('key')` (sanitizes; preferred over raw `$_GET`/`$_POST`)
- DB queries via `Db::getInstance()` with `pSQL()` for sanitization (legacy pattern; not parameterized)
- `try/catch` rare — exceptions surfaced via `Tools::displayError()` or logged
- Hook methods generally return rendered output; failure typically silent (returns empty string)
**Custom-code observations:**
- `import-product.php` has minimal error handling, no transaction wrapping
- `buy-by-phone.php` accesses `$_POST` directly without `Tools::getValue()` — see `concerns.md`
## Logging
**Framework:**
- Symfony Monolog (admin layer) via `app/AppKernel.php`
- File logs at `errors.log` (root) and `iadmin/errors.log`
- No structured/contextual logging in custom code
**Patterns:**
- PrestaShop core `PrestaShopLogger::addLog()` — used in some modules
- Custom code prefers `error_log()` or no logging
## Comments
**When to Comment:**
- PHPDoc blocks above public methods (recommended)
- Inline `//` for non-obvious business logic
**PHPDoc style (used in custom module):**
```php
/**
* Builds presented products list from accessories of products currently in cart.
*
* @return array
*/
protected function buildCrossSellProducts()
```
Parameter form:
```php
/**
* @param int[] $productIds
* @return array<int, bool>
*/
protected function getCombinationFlags(array $productIds)
```
Property form:
```php
/** @var int Manufacturer identifier */
public $id_manufacturer;
```
**TODO Comments:**
- Format varies (`// TODO`, `// FIXME`, `// HACK`) — found in core, rare in custom code
## Function Design
**Size:**
- Module hook methods kept short; logic delegated to private helpers (e.g. `buildCrossSellProducts()`, `presentProducts()`)
- Long legacy scripts exist (`import-product.php` 813 lines) — anti-pattern
**Parameters:**
- Hook method signatures match PrestaShop dispatcher: `hookXxx(array $params)` where `$params` carries context
## Module Design (PrestaShop-specific)
**Module class skeleton:**
- Constructor sets `$this->name`, `$this->tab`, `$this->version`, `$this->author`, `$this->bootstrap`, then `parent::__construct()`
- `install()` calls `parent::install()` and chains `$this->registerHook(...)` calls
- `uninstall()` calls `parent::uninstall()` (cleans hook table)
- Hook handlers named `hook<HookName>()`
**Template overrides:**
- Module ships defaults in `modules/<m>/views/templates/hook/`
- Theme overrides resolved at `themes/<theme>/modules/<m>/views/templates/hook/`
**Assets:**
- Registered in `hookActionFrontControllerSetMedia()` via `$this->context->controller->registerStylesheet()` / `registerJavascript()`
## Override Design
- Place in `override/classes/<ClassName>.php` or `override/classes/controller/<Name>.php`
- Extend the core class verbatim and add/override methods only
- Document the *why* of each override (currently undocumented in this project — see `concerns.md`)
---
*Convention analysis: 2026-05-10*
*Update when patterns change*

117
.paul/codebase/db_schema.md Normal file
View File

@@ -0,0 +1,117 @@
# Database Schema
**Analysis Date:** 2026-05-10
## Database Engine
- **MySQL with InnoDB** — `app/config/parameters.php`
- **Schema:** `admin_drmaterac`
- **Table prefix:** `materac_` (referenced as `ps_*` in PrestaShop convention; effective name is `materac_<name>`)
Examples below use the `ps_` prefix per PrestaShop documentation; in this install they map to `materac_*`.
## ORM / Migration Approach
- **Front-office:** PrestaShop `ObjectModel` (in `classes/ObjectModel.php`). Each domain class declares a `$definition` array with `table`, `primary`, `multilang`, `fields`. `add()` / `update()` / `delete()` operate on the corresponding row.
- **Admin / Symfony:** Doctrine ORM bundled (`DoctrineBundle` in `app/AppKernel.php`).
- **Migrations:** PrestaShop has no first-class migration tool. Schema is shipped with core in `install/` SQL dumps; modules create tables in their `install()` method.
## Custom Tables Created by This Project
**The custom Cross Sell PRO module creates NO custom tables.**
- `modules/crosssellpro/crosssellpro.php::install()` only registers hooks (`displayShoppingCartFooter`, `displayCheckoutSummaryTop`, `displayHeader`, `actionFrontControllerSetMedia`)
- No `sql/` directory; no `Db::getInstance()->execute("CREATE TABLE ...")` call
- All cross-sell relationships read from PrestaShop core table `ps_accessory`
Other custom code:
- `import-product.php` updates existing `ps_product*` tables from external XML feed — does not create tables
- `buy-by-phone.php` does not touch the database
- `modules/caraty/` — investigate when relevant; not analyzed in detail
## Custom Columns Added Via Override
`override/classes/Product.php` extends `ps_product` / `ps_product_lang` with Google Shopping fields (declared in the `$definition` array — actual ALTER TABLE must have been run manually or via a separate install script):
- `in_google_shopping` — BOOL, shop-scoped
- `product_name_google_shopping` — STRING, lang-scoped
- `product_short_desc_google_shopping` — HTML, lang-scoped
- `custom_label_0``custom_label_4` — STRING, lang-scoped
These are unrelated to the cross-sell module.
## PrestaShop Core Tables Touched by Cross Sell PRO
| Table | Purpose | How module uses it |
|---|---|---|
| `ps_product` | Product master data | Filtered by `active = 1` and shop visibility |
| `ps_accessory` | Related-product associations | Read via `Product::getAccessoriesLight($id_lang, $id_product)` |
| `ps_product_attribute` | Product variants/combinations | `getCombinationFlags()` checks if a product has combinations (decides direct add-to-cart vs link to product page) |
| `ps_cart` | Cart entity | Reached via `$context->cart` |
| `ps_cart_product` | Items currently in cart | `Cart::getProducts(true)` excludes already-in-cart products from cross-sell list |
| `ps_product_lang` | Multilingual product names/descriptions | Used by `ProductListingPresenter` |
| `ps_product_shop` | Shop-specific visibility | `WHERE product_shop.visibility IN ('both', 'catalog')` |
| `ps_image` | Product images | Cover image fetched by `ImageRetriever` |
| `ps_category_lang` | Category names | Used in product presentation |
| `ps_feature`, `ps_feature_value`, `ps_product_feature` | Product features | May appear in presented data |
| `ps_hook_module` | Hook registrations | Written by `parent::install()` when module installs |
## Key Queries (in `modules/crosssellpro/crosssellpro.php`)
**Combination check** (lines ~237243):
```sql
SELECT p.id_product, COUNT(pa.id_product_attribute) AS combinations
FROM `ps_product` p
LEFT JOIN `ps_product_attribute` pa ON (pa.id_product = p.id_product)
WHERE p.id_product IN (...)
GROUP BY p.id_product
```
**Visibility filter** (lines ~264275):
```sql
SELECT p.id_product
FROM `ps_product` p
[Shop::addSqlAssociation]
WHERE p.id_product IN (...)
AND p.active = 1
AND product_shop.visibility IN ('both', 'catalog')
ORDER BY FIELD(p.id_product, ...)
```
## Relationships (Cross-Sell Data Path)
```
ps_cart (id_cart)
└─1:N─ ps_cart_product (id_product)
└─1:1─ ps_product (id_product)
└─1:N─ ps_accessory (id_product_2)
└─1:1─ ps_product [the cross-sell candidate]
├─1:N─ ps_product_attribute (combinations)
├─1:N─ ps_image (cover)
└─1:N─ ps_product_lang (name, description)
```
## Module Install / Uninstall
**Install**`modules/crosssellpro/crosssellpro.php` lines ~3744:
```php
return parent::install()
&& $this->registerHook('displayShoppingCartFooter')
&& $this->registerHook('displayCheckoutSummaryTop')
&& $this->registerHook('displayHeader')
&& $this->registerHook('actionFrontControllerSetMedia');
```
**Uninstall** — lines ~4648:
```php
return parent::uninstall();
```
`parent::install()` / `parent::uninstall()` write to / clean up `ps_module` and `ps_hook_module`.
## Backups
- Snapshots of the schema are present in `iadmin/backups/` (e.g. `1698930967-7683b7af.sql.bz2`) — **see `concerns.md`**: these are inside the webroot and should be moved out or denied via `.htaccess`.
---
*DB schema analysis: 2026-05-10*
*Update when adding/modifying tables, modules, or overrides that affect schema*

View File

@@ -0,0 +1,167 @@
# External Integrations
**Analysis Date:** 2026-05-10
## APIs & External Services
### Payment Gateways
- **PayNow (BLIK / bank transfer / card)** — `modules/paynow/paynow.php` (v1.6.35)
- SDK: `pay-now-php-sdk` (Guzzle HTTP, PSR-7)
- Auth: API key / signature (configured in module back-office)
- Functions: payment authorization, refund, GDPR
- Webhook: payment status notification with retry/stop logic
- **Santander eRaty (installment credit)** — `modules/santandercredit/santandercredit.php` (v5.5)
- PL-specific Santander Consumer Bank
- Calculator widget + simulator iframe
- Hooks: order state confirmation
- **Check Payment** — `modules/ps_checkpayment/`
- **Wire Transfer** — `modules/ps_wirepayment/`
### Marketplace
- **Empik Marketplace** — `modules/empikmarketplace/empikmarketplace.php` (v1.6.5)
- Guzzle HTTP client
- Symfony DI container
- Sync orders, products, inventory with Empik.com
- Cron controllers — `modules/empikmarketplace/controllers/cron/`
### Shipping & Logistics
- **DPD Polska** — `modules/dpdpoland/dpdpoland.php`
- Custom API integration — `modules/dpdpoland/config.api.php`
- Manifest, package, pickup webservices
- PDF label generation
### Reviews & Ratings
- **Ceneo.pl Trusted Reviews** — `modules/ceneo_trustedreviews/ceneo_trustedreviews.php` (v1.0.1)
- **eKomi Ratings & Reviews** — `modules/ekomiratingsandreviews/ekomiratingsandreviews.php`
- API: `https://plugins-dashboard.ekomiapps.de/api/v1/order`
- Validation: `https://api.ekomi.de/v3/getSettings`
- Cron-based export
- **eKomi SFF (Shop Feedback Form)** — `modules/ekomiSff/`
- **PrestaShop Product Comments** — `modules/productcomments/`
### Analytics & Tracking
- **Google Analytics 4 Pro** — `modules/pdgoogleanalytycs4pro/pdgoogleanalytycs4pro.php` (v1.0.2)
- SDK: `br33f/php-ga4-mp` (GA4 Measurement Protocol)
- Enhanced conversions with phone (uses `libphonenumber-for-php`)
- Refund tracking, Google Ads (AW) conversions, Merchant Center
- **Google Merchant Center Pro** — `modules/pdgooglemerchantcenterpro/`
- **BAE Commerce Tracking** — `modules/baecommercetracking/baecommercetracking.php` (v1.0.6)
- GA + Google Ads conversion + Facebook Pixel events
- **Facebook Conversion Pixel** — `modules/fbpixel/fbpixel.php` (v2.0.2)
- Purchase event injection at order confirmation
- **20+ PrestaShop stats modules** — `modules/dash*`, `modules/stats*`
### Customer Communication
- **Smartsupp Live Chat** — `modules/smartsupp/smartsupp.php`
### Spam / Bot Protection
- **Google reCAPTCHA** — `modules/eicaptcha/vendor/google/recaptcha/`
- Also referenced in `buy-by-phone.php` (key hardcoded — see `concerns.md`)
### Content / SEO
- **XML Feeds** — `modules/xmlfeeds/`
- **Google Sitemap** — `modules/gsitemap/`
- **Advanced SEO Pro** — `modules/arseopro/`
- **Link Rewriter** — `modules/x13linkrewrite/`
### Product / Content Management
- **App Page Builder** — `modules/appagebuilder/` (CodeMirror editor)
- **Cross-Sell Pro (custom)** — `modules/crosssellpro/`
- **Image Hover Effects** — `modules/ybc_productimagehover/`
- **WebP Optimization** — `modules/x13webp/` (uses `.htaccess` rewrite)
### GDPR / Security
- **GDPR Data Privacy** — `modules/ps_dataprivacy/`
- **Cookies Plus** — `modules/cookiesplus/`
- **Security Watcher** — `modules/securitywatcher/`
### External XML Feed (custom script)
- `import-product.php` fetches `https://amz.com.pl/bazy-produktow-export/produkty-amz.xml` for batch product updates
## Data Storage
**Database:**
- MySQL with InnoDB
- Connection: `app/config/parameters.php` (DB host, name, user, password — currently committed; see `concerns.md`)
- DB prefix: `materac_` (schema `admin_drmaterac`)
- ORM: PrestaShop `ObjectModel` (front-office), Doctrine (admin Symfony layer)
**File Storage:**
- Local filesystem (PrestaShop default)
- Image variants in `img/` (PrestaShop core layout)
- WebP variants generated by `modules/x13webp/`
**Caching:**
- Memcached configured but disabled — `app/config/parameters.php` (`ps_caching: CacheMemcached`, `ps_cache_enable: false`)
- `modules/pagecache/` — page-level cache module (uses override of `Hook.php`)
## Authentication & Identity
**Customer auth:**
- PrestaShop core (cookies + DB)
**Admin auth:**
- Symfony Security via `iadmin/` AppKernel
- Custom-named admin folder is *not* a security control (security through obscurity — see `concerns.md`)
**Service auth (per-integration):**
- API keys / OAuth tokens stored in PrestaShop `Configuration` (DB `ps_configuration` / `materac_configuration` table) per module
## Email / SMTP
- SMTP at `127.0.0.1:25``app/config/parameters.php`
- PrestaShop mail templates: `mails/{en,pl}/`
- Custom transactional: `buy-by-phone.php` uses bundled `phpmailer/` (with hardcoded SMTP credentials — see `concerns.md`)
## Multi-Domain Routing
- Two domains served from one install — `.htaccess`
- `drmaterac.pl` (primary)
- `lulandia.pl` (secondary)
- Per-domain image rewrite + WebP fallback rules
## Webhooks
**Incoming:**
- PayNow payment notifications — `modules/paynow/` controllers
- Empik Marketplace cron callbacks — `modules/empikmarketplace/controllers/cron/`
- eKomi cron-based order export
**Outgoing:**
- GA4 Measurement Protocol — `modules/pdgoogleanalytycs4pro/`
- Facebook Pixel events — `modules/fbpixel/`
- Empik order/inventory sync — `modules/empikmarketplace/`
## Environment Configuration
- All secrets currently in `app/config/parameters.php` (committed) — DB password, app secret, cookie keys
- No `.env` / `.env.example` mechanism in use
- **Rotation required** for any credential exposed in git history (see `concerns.md`)
## CI/CD & Deployment
- No CI workflows detected (no `.github/workflows/`, no `.gitlab-ci.yml`)
- Deploy method appears manual / FTP — daily Polish changelog at `changelog/YYYY-MM-DD.md`
- `iadmin/autoupgrade/` contains `prestashop_1.7.5.1.zip` — appears to be an upgrade payload left in webroot
---
*Integration audit: 2026-05-10*
*Update when adding/removing external services*

96
.paul/codebase/stack.md Normal file
View File

@@ -0,0 +1,96 @@
# Technology Stack
**Analysis Date:** 2026-05-10
## Languages
**Primary:**
- PHP 7.4 / 8.2.x runtime — All application code (PrestaShop targets PHP 7.4; current server reports 8.2.12)
- JavaScript (ES5/ES6 transpiled) — Theme assets and module front-end behavior
**Secondary:**
- Smarty templates (`.tpl`) — Front-end rendering layer
- Twig templates — Symfony admin layer
- Sass/SCSS — Theme styling (`themes/leo_gstore/config.rb` Compass config)
- HTML5/CSS3 — Presentation
- SQL (MySQL) — Data layer
## Runtime
**Environment:**
- PHP (declared 7.4 in PrestaShop core; current PHP-CLI 8.2.12) — `app/AppKernel.php`
- Apache web server with `mod_rewrite``.htaccess` (429 lines of rewrite rules)
- MySQL with InnoDB — `app/config/parameters.php`
**Package Manager:**
- Composer (PHP) — `composer.lock` present at root
- npm (JS, theme-only) — `themes/package-lock.json`
## Frameworks
**Core:**
- PrestaShop 1.7.8.11 — `app/AppKernel.php` line 35
- Symfony (FrameworkBundle, SecurityBundle, TwigBundle, MonologBundle, SwiftmailerBundle, DoctrineBundle) — `app/AppKernel.php`
- Doctrine ORM — used for admin/back-office Symfony layer
- Smarty — front-office template engine — `config/smarty.config.inc.php`, `config/smartyfront.config.inc.php`
**Testing:**
- PHPUnit (~5.7) — present only in select PrestaShop modules (`modules/ps_facetedsearch/`, `modules/gamification/`)
- Mockery — `modules/ps_facetedsearch/tests/php/`
- PHPStan via `prestashop/php-dev-tools``modules/*/tests/phpstan/phpstan.neon` (40+ modules)
- **No root-level test setup; custom modules have no tests**
**Build/Dev:**
- Babel 7.0.0-rc.1 — `themes/package-lock.json`
- Compass/Sass — `themes/leo_gstore/config.rb`
- Pre-built theme bundles — `themes/leo_gstore/assets/cache/*.js`, `themes/leo_gstore/assets/cache/*.css`
## Key Dependencies
**Critical (PHP):**
- Guzzle HTTP client (v6/v7) — used by paynow, empikmarketplace, smartsupp
- Symfony components (Cache, DI, YAML, FileSystem) — core + modules
- PSR-7/PSR-11/PSR-15 interfaces — HTTP messaging
- `pay-now-php-sdk` — payment integration in `modules/paynow/`
- `br33f/php-ga4-mp` — GA4 Measurement Protocol — `modules/pdgoogleanalytycs4pro/`
- `libphonenumber-for-php` — phone validation (GA4 enhanced conversions)
- `google/recaptcha``modules/eicaptcha/vendor/google/recaptcha/`
**Critical (JS):**
- jQuery — bundled in theme (`themes/leo_gstore/assets/cache/bottom-*.js`)
- Slick carousel — `themes/leo_gstore/assets/js/custom.js`
- Moment.js — `js/date.js`
- CodeMirror — `modules/advancedpopupcreator/`
## Configuration
**Environment:**
- Database/secrets in `app/config/parameters.php` (plain PHP — **secrets currently committed**, see concerns.md)
- Symfony YAML — `app/config/config.yml`, `app/config/parameters.yml`
- Constants: `config/defines.inc.php` (paths, `_PS_ADMIN_DIR_` etc.)
- Bootstrap: `config/bootstrap.php`, `config/config.inc.php`
- Locale: pl-PL — `app/config/parameters.php`
- DB prefix: `materac_` (schema `admin_drmaterac`)
**Build:**
- Theme assets: pre-bundled to `themes/leo_gstore/assets/cache/`
- No root `composer.json` build pipeline; modules ship pre-built
## Platform Requirements
**Development:**
- Apache + PHP 7.4+ + MySQL stack
- Composer for PHP dependencies
- Node/npm only required for theme rebuild
**Production:**
- Shared/dedicated hosting with Apache + mod_rewrite
- SMTP transport configured to `127.0.0.1:25``app/config/parameters.php`
- Memcached configured but disabled (`ps_caching: CacheMemcached`, `ps_cache_enable: false`)
- WebP image delivery via `.htaccess` content negotiation + `modules/x13webp/`
- Multi-domain: serves both `drmaterac.pl` and `lulandia.pl` from same install — `.htaccess`
---
*Stack analysis: 2026-05-10*
*Update after major dependency changes*

201
.paul/codebase/structure.md Normal file
View File

@@ -0,0 +1,201 @@
# Codebase Structure
**Analysis Date:** 2026-05-10
## Directory Layout
```
drmaterac.pl/
├── index.php # Front-office entry (invoked via .htaccess rewrite)
├── .htaccess # 429-line rewrite rules (multi-domain routing, SEO redirects)
├── autoload.php # PSR-4 autoloader bootstrap
├── composer.lock # PHP dependency lock
├── app/ # Symfony AppKernel + admin config
│ ├── AppKernel.php # PrestaShop 1.7.8.11 kernel
│ ├── AppCache.php
│ └── config/ # parameters.php, parameters.yml, config.yml, services/
├── classes/ # PrestaShop core classes (ObjectModel-based)
│ ├── Product.php, Cart.php, Order.php, Customer.php, …
│ ├── Dispatcher.php, Hook.php, Context.php, ObjectModel.php
│ ├── controller/ # FrontController, AdminController, ModuleFrontController…
│ └── db/, cache/ # Database + caching primitives
├── controllers/ # Page controllers (dispatch targets)
│ ├── front/ # CartController, OrderController, ProductController…
│ └── admin/ # Legacy admin (Symfony preferred via iadmin/)
├── modules/ # All installed modules (~138 entries; 50+ active)
│ ├── crosssellpro/ # CUSTOM — Cross Sell PRO carousel module
│ │ ├── crosssellpro.php
│ │ └── views/{templates/hook,css,js}/
│ ├── caraty/ # CUSTOM — site-specific module
│ ├── paynow/, santandercredit/, ps_checkpayment/, ps_wirepayment/ # Payments
│ ├── empikmarketplace/, dpdpoland/ # Marketplace + shipping
│ ├── pdgoogleanalytycs4pro/, fbpixel/, baecommercetracking/ # Analytics
│ ├── ekomiratingsandreviews/, ceneo_trustedreviews/, ekomiSff/ # Reviews
│ └── pagecache/, leo*/, ets_megamenu/, advancedpopupcreator/, …
├── override/ # Core class overrides (107 files; 31 in override/classes/)
│ └── classes/ # Cart.php, CartRule.php, Product.php, Hook.php, Dispatcher.php,
│ # FrontController.php, ProductListingFrontController.php …
├── themes/ # Theme layer
│ ├── leo_gstore/ # ACTIVE child theme (Leo Gstore v1.4)
│ │ ├── templates/ # *.tpl (layout, cart, checkout, product, …)
│ │ ├── modules/ # Per-module template overrides
│ │ ├── assets/cache/ # Pre-bundled JS/CSS
│ │ └── config.xml, config.rb
│ └── _libraries/, classic.zip, leo_gstore.zip
├── config/ # Bootstrap + Smarty config (NOT app/config/)
│ ├── config.inc.php, defines.inc.php, bootstrap.php
│ └── smarty*.config.inc.php
├── src/ # Custom PSR-4 namespaced code
│ └── Core/Localization/Locale.php
├── translations/{en,pl}/ # Localization files
├── mails/{en,pl}/ # Email templates
├── js/ # Global JavaScript (jquery/, date.js)
├── vendor/ # Composer dependencies
├── webservice/dispatcher.php # REST API dispatcher
├── iadmin/ # CUSTOM-NAMED admin folder (5 MB errors.log inside)
│ ├── index.php # Symfony admin entry
│ ├── backups/, filemanager/, autoupgrade/, api/, export/, import/
│ └── errors.log # ⚠ committed log (see concerns.md)
├── changelog/ # Daily Polish changelog markdown files
├── docs/ # Project docs
├── localization/ # Localization data
├── scripts/, calculate/ # Utility scripts
├── backup_before_patch/ # ⚠ committed *.backup files (see concerns.md)
├── buy-by-phone.php # Form handler — sends order-by-phone email
├── import-product.php # 813-line XML feed importer
├── category-description.php # Category description endpoint
├── diag_*_tmp.php, info.php # ⚠ diagnostic files (see concerns.md)
└── errors.log # PHP error log (committed; should be gitignored)
```
## Directory Purposes
**`classes/`**
- Purpose: PrestaShop core domain layer (ObjectModel + service classes)
- Key files: `Product.php`, `Cart.php`, `Hook.php`, `Dispatcher.php`, `Context.php`, `ObjectModel.php`
- Subdirectories: `controller/` (controller base classes), `db/`, `cache/`
**`controllers/front/`**
- Purpose: Front-office page controllers — dispatch targets matched by URL
- Key files: `CartController.php`, `OrderController.php`, `ProductController.php`, `CategoryController.php`
**`modules/`**
- Purpose: Extension layer — all features beyond core live here as hook-registered modules
- Key custom: `modules/crosssellpro/crosssellpro.php` (cross-sell carousel)
- Key third-party: payment (paynow, santandercredit), shipping (dpdpoland), marketplace (empikmarketplace), analytics (pdgoogleanalytycs4pro, fbpixel), reviews (ekomi*, ceneo_*)
**`modules/crosssellpro/`** — CUSTOM module (Pyziak Jacek)
- `crosssellpro.php` — module class (~335 lines, v1.1.6)
- `views/templates/hook/cartCrossSell.tpl` — cart carousel
- `views/templates/hook/checkoutCrossSell.tpl` — checkout carousel
- `views/css/cartCrossSell.css` — styles (~159 lines)
- `views/js/cartCrossSell.js` — carousel + AJAX add-to-cart (~99 lines)
**`override/classes/`**
- Purpose: Monkey-patch PrestaShop core without editing core
- Key files: `Cart.php`, `CartRule.php`, `Product.php`, `Hook.php`, `Dispatcher.php`, `controller/FrontController.php`, `controller/ProductListingFrontController.php`
**`themes/leo_gstore/`**
- Purpose: Active child theme (`config.xml`)
- Templates: `templates/*.tpl` (page-level), `modules/<module>/views/templates/hook/*.tpl` (module override)
- Assets: pre-bundled in `assets/cache/`
**`app/`**
- Purpose: Symfony kernel + admin config (NOT to be confused with `config/`)
- Key files: `AppKernel.php`, `config/parameters.php` (DB credentials — currently committed), `config/config.yml`
**`config/`**
- Purpose: Legacy PrestaShop bootstrap + Smarty configuration
- Key files: `config.inc.php`, `defines.inc.php`, `bootstrap.php`, `smarty.config.inc.php`
**`iadmin/`**
- Purpose: Renamed back-office (PrestaShop default is `admin/`)
- Note: contains `errors.log` (5 MB), `backups/` (SQL dumps), `autoupgrade/` payload — investigate cleanup
## Key File Locations
**Entry Points:**
- `index.php` — front-office (rewrite target)
- `iadmin/index.php` — back-office (Symfony AppKernel)
- `webservice/dispatcher.php` — REST API
**Configuration:**
- `app/config/parameters.php` — DB / mail / secrets (PHP)
- `app/config/parameters.yml` — Symfony parameters
- `app/config/config.yml` — Symfony service config
- `config/defines.inc.php` — path constants
- `.htaccess` — Apache rewrite rules (multi-domain)
**Core Logic:**
- Custom cross-sell: `modules/crosssellpro/crosssellpro.php`
- Custom site logic: `modules/caraty/caraty.php`
- Domain models: `classes/Product.php`, `classes/Cart.php`, `classes/Order.php`
- Core overrides: `override/classes/`
**Testing:**
- No project-level test suite
- Examples (third-party module tests): `modules/ps_facetedsearch/tests/`, `modules/gamification/tests/`
**Documentation:**
- `AGENTS.md` (root)
- `docs/` (project docs)
- `changelog/YYYY-MM-DD.md` (daily Polish changelog)
## Naming Conventions
**Files:**
- PHP classes: `PascalCase.php` (e.g. `classes/Product.php`)
- Module entry file: lowercase matching directory (`modules/crosssellpro/crosssellpro.php`)
- Smarty templates: `kebab-case.tpl` or `camelCase.tpl` per module (`cartCrossSell.tpl`)
- Asset files: `camelCase.{js,css}` per module
- Templates in theme module overrides: match hook names (`displayShoppingCartFooter.tpl` style not always followed; module-defined name common)
**Directories:**
- Module folders: lowercase, no underscores preferred (`crosssellpro`, `paynow`, `dpdpoland`)
- Snake_case allowed for compound names (`empikmarketplace`, `ets_megamenu`, `leo_gstore`)
**Special Patterns:**
- Module hook handler: `hook<HookNamePascalCase>()` in module class — e.g. `hookDisplayShoppingCartFooter()`
- Hook registration uses camelCase string: `$this->registerHook('displayShoppingCartFooter')`
- Theme module template path: `themes/<theme>/modules/<module>/views/templates/hook/<name>.tpl`
## Where to Add New Code
**New Module / Feature:**
- Primary: `modules/<module-name>/<module-name>.php` (extends `Module`)
- Templates: `modules/<module-name>/views/templates/hook/<hookName>.tpl`
- Theme override: `themes/leo_gstore/modules/<module-name>/views/templates/hook/<hookName>.tpl`
- CSS/JS: `modules/<module-name>/views/css/`, `modules/<module-name>/views/js/`
**Core class extension:**
- Place in `override/classes/<ClassName>.php` extending the core class — NOT inside `classes/`
**New page controller (rare; usually a module):**
- `controllers/front/<Name>Controller.php`
**New translation:**
- `translations/{en,pl}/<context>.{xlf,php}`
**New PSR-4 utility:**
- `src/<Namespace>/<Class>.php` (autoloader configured in `composer.lock`)
## Special Directories
**`backup_before_patch/`**
- Purpose: Manual backup of search engine module pre-patch
- Source: Manual snapshot
- Committed: Yes (should be removed — see `concerns.md`)
**`themes/leo_gstore/cache/`**
- Purpose: Compiled Smarty templates
- Source: Auto-generated at runtime
- Committed: Should be gitignored (verify)
**`iadmin/backups/`**
- Purpose: PrestaShop database backups (e.g. `1698930967-7683b7af.sql.bz2`)
- Committed: Yes (security risk — see `concerns.md`)
---
*Structure analysis: 2026-05-10*
*Update when directory structure changes*

View File

@@ -0,0 +1,22 @@
# Technical Changelog
**Started:** 2026-05-10
Chronological log of technical changes (architecture decisions, refactors, dependency upgrades, schema changes, security fixes). Populated over time by `/paul:apply` and ad-hoc edits.
Format per entry:
```
## YYYY-MM-DD — short title
- What changed (file paths)
- Why
- Impact / migration notes
```
---
## 2026-05-10 — Initial codebase mapping
- Created `.paul/codebase/` (stack, architecture, structure, conventions, testing, integrations, concerns, db_schema, tech_changelog)
- Why: bootstrap PAUL workflow; document the PrestaShop 1.7.8.11 install + custom Cross Sell PRO module before further work
- Impact: future phases reference these documents instead of re-exploring the codebase
---

134
.paul/codebase/testing.md Normal file
View File

@@ -0,0 +1,134 @@
# Testing Patterns
**Analysis Date:** 2026-05-10
## Test Framework
**Runner:**
- PHPUnit (~5.7) — present only in select PrestaShop-shipped modules, NOT at project root
- Mockery — used by `modules/ps_facetedsearch/`
**Assertion Library:**
- PHPUnit built-in assertions
- Mockery for mocks: `Mockery::mock(Configuration::class)`
**Run Commands:**
- **No project-level test command exists.** Custom modules (`modules/crosssellpro/`, `modules/caraty/`) have no test suites.
- For modules that ship tests:
```bash
cd modules/ps_facetedsearch && php vendor/bin/phpunit -c tests/php/phpunit.xml
cd modules/gamification && php vendor/bin/phpunit
```
## Test File Organization
**Location:**
- Per-module: `modules/<module>/tests/`
- No root-level `tests/` directory
- No collocated tests
**Examples (third-party PrestaShop modules):**
- `modules/ps_facetedsearch/tests/php/FacetedSearch/Filters/BlockTest.php`
- `modules/gamification/tests/unit/AdviceTest.php`
- `modules/gamification/tests/functional/`
**Naming:**
- `*Test.php` suffix
- PSR-4 namespace under `Tests\`
**Custom modules in this project:**
- `modules/crosssellpro/`**NO tests directory**
- `modules/caraty/`**NO tests directory**
## Test Structure
Example from `modules/ps_facetedsearch/tests/php/FacetedSearch/Filters/BlockTest.php`:
```php
namespace PrestaShop\Module\FacetedSearch\Tests\Filters;
use Mockery;
use Mockery\Adapter\Phpunit\MockeryTestCase;
class BlockTest extends MockeryTestCase
{
protected function setUp()
{
$mock = Mockery::mock(Configuration::class);
$mock->shouldReceive('get')->andReturnUsing(function ($arg) { /* … */ });
}
}
```
PHPUnit config example (`modules/gamification/phpunit.xml`):
```xml
<phpunit bootstrap="tests/autoload.php">
<testsuites>
<testsuite name="Unit tests"><directory>tests/unit/</directory></testsuite>
<testsuite name="Functional tests"><directory>tests/functional/</directory></testsuite>
</testsuites>
<logging>
<log type="coverage-clover" target="build/clover.xml"/>
</logging>
</phpunit>
```
## Mocking
**Framework:**
- Mockery (in modules that test, e.g. `ps_facetedsearch`)
**Pattern:**
```php
$mock = Mockery::mock(Configuration::class);
$mock->shouldReceive('get')->andReturn('value');
```
## Static Analysis
**PHPStan:**
- Configured in 40+ PrestaShop modules via `prestashop/php-dev-tools`
- Standard config — `modules/<module>/tests/phpstan/phpstan.neon`:
```neon
includes:
- %currentWorkingDirectory%/vendor/prestashop/php-dev-tools/phpstan/ps-module-extension.neon
parameters:
paths:
- ../../<module>.php
level: 5
```
- **No project-root PHPStan config.**
**PHP-CS-Fixer:**
- Some module `composer.json` scripts reference it: `"lint": ["php-cs-fixer fix --no-interaction --dry-run --diff"]`
- **No project-root config.**
## Coverage
- No project-wide coverage target
- `modules/gamification/phpunit.xml` writes Clover output to `build/clover.xml`
- No CI enforcement detected
## Test Types
**Unit tests:** Present in some PS modules (`modules/gamification/tests/unit/`).
**Functional tests:** Present in `modules/gamification/tests/functional/`, `modules/ps_facetedsearch/tests/php/`.
**E2E:** None detected.
## Inferred Testing Strategy for This Project
Custom code (`modules/crosssellpro/`, `modules/caraty/`, `import-product.php`, `buy-by-phone.php`) is tested **manually** in browser:
- Verify cross-sell carousel renders on cart page (`/cart`) and checkout summary
- Verify "add to cart" works for products without combinations
- Verify products with combinations link to product detail page
- Smoke-test admin via `iadmin/`
- No regression suite — every change is risk
**If adding tests in the future:**
- Use PrestaShop's `prestashop/php-dev-tools` package per-module pattern
- Place under `modules/crosssellpro/tests/` mirroring PS conventions
- Mock `Db`, `Context`, `Module` core dependencies via Mockery
---
*Testing analysis: 2026-05-10*
*Update when test patterns change*

View File

@@ -0,0 +1,37 @@
{"ts":"2026-05-10T17:57:04Z","tool":"Bash","cmd":"mkdir -p .paul/codebase\",\"description\":\"Create codebase directory\"},\"tool_response\":{\"stdout\":\"\",\"stderr\":\"\",\"interrupted\":false,\"isImage\":false,\"noOutputExpected\":true},\"tool_us","cwd":"/c/visual studio code/projekty/drmaterac.pl"}
{"ts":"2026-05-10T17:57:36Z","tool":"Bash","cmd":"cd /c/visual\\\\ studio\\\\ code/projekty/drmaterac.pl && find . -maxdepth 1 -type f -o -type d | head -40\"},\"tool_response\":{\"stdout\":\".\\n./.claude\\n./.git\\n./.htaccess\\n./.idea\\n./.paul\\","cwd":"/c/visual studio code/projekty/drmaterac.pl"}
{"ts":"2026-05-10T17:57:41Z","tool":"Bash","cmd":"cd /c/visual\\\\ studio\\\\ code/projekty/drmaterac.pl && ls -la modules/ | head -60\"},\"tool_response\":{\"stdout\":\"total 1069\\ndrwxr-xr-x 1 jacek 197121 0 maj 10 19:37 .\\ndrwxr-xr-x 1 jacek","cwd":"/c/visual studio code/projekty/drmaterac.pl"}
{"ts":"2026-05-10T17:57:46Z","tool":"Bash","cmd":"cd /c/visual\\\\ studio\\\\ code/projekty/drmaterac.pl && find modules/crosssellpro -type f | head -50\"},\"tool_response\":{\"stdout\":\"modules/crosssellpro/crosssellpro.php\\nmodules/crosssellpro/v","cwd":"/c/visual studio code/projekty/drmaterac.pl"}
{"ts":"2026-05-10T17:57:51Z","tool":"Bash","cmd":"cd /c/visual\\\\ studio\\\\ code/projekty/drmaterac.pl && ls -la | grep -E \"index.php|iadmin\" && ls -la themes/\"},\"tool_response\":{\"stdout\":\"drwxr-xr-x 1 jacek 197121 0 maj 10 19:37 iadmin","cwd":"/c/visual studio code/projekty/drmaterac.pl"}
{"ts":"2026-05-10T17:57:56Z","tool":"Bash","cmd":"cd /c/visual\\\\ studio\\\\ code/projekty/drmaterac.pl && ls -la themes/leo_gstore/ | head -40\"},\"tool_response\":{\"stdout\":\"total 176\\ndrwxr-xr-x 1 jacek 197121 0 maj 10 19:38 .\\ndrwxr-xr","cwd":"/c/visual studio code/projekty/drmaterac.pl"}
{"ts":"2026-05-10T17:58:01Z","tool":"Bash","cmd":"cd /c/visual\\\\ studio\\\\ code/projekty/drmaterac.pl && find override -type f | head -30\"},\"tool_response\":{\"stdout\":\"override/.htaccess\\noverride/classes/assets/index.php\\noverride/classes/","cwd":"/c/visual studio code/projekty/drmaterac.pl"}
{"ts":"2026-05-10T17:58:08Z","tool":"Bash","cmd":"wc -l \"C:\\\\visual studio code\\\\projekty\\\\drmaterac.pl\\\\import-product.php\" \"C:\\\\visual studio code\\\\projekty\\\\drmaterac.pl\\\\buy-by-phone.php\" \"C:\\\\visual studio code\\\\projekty","cwd":"/c/visual studio code/projekty/drmaterac.pl"}
{"ts":"2026-05-10T17:58:12Z","tool":"Bash","cmd":"cd /c/visual\\\\ studio\\\\ code/projekty/drmaterac.pl && ls -la *.php | head -20\"},\"tool_response\":{\"stdout\":\"-rw-r--r-- 1 jacek 197121 4511 maj 10 19:37 buy-by-phone.php\\n-rw-r--r-- 1 jacek ","cwd":"/c/visual studio code/projekty/drmaterac.pl"}
{"ts":"2026-05-10T17:58:20Z","tool":"Bash","cmd":"cd /c/visual\\\\ studio\\\\ code/projekty/drmaterac.pl && find . -name \"Dispatcher.php\" -type f | head -5 && find . -name \"*FrontController*\" -type f | head -5\"},\"tool_response\":{\"stdout\":\".","cwd":"/c/visual studio code/projekty/drmaterac.pl"}
{"ts":"2026-05-10T17:58:26Z","tool":"Bash","cmd":"cd /c/visual\\\\ studio\\\\ code/projekty/drmaterac.pl && ls -la app/ && echo -e \"\\\\n--- iadmin structure ---\" && ls -la iadmin/ | head -30\"},\"tool_response\":{\"stdout\":\"total 33\\ndrwxr-xr-x","cwd":"/c/visual studio code/projekty/drmaterac.pl"}
{"ts":"2026-05-10T17:58:32Z","tool":"Bash","cmd":"cd /c/visual\\\\ studio\\\\ code/projekty/drmaterac.pl && ls -la classes/ | head -50\"},\"tool_response\":{\"stdout\":\"total 2801\\ndrwxr-xr-x 1 jacek 197121 0 maj 10 19:37 .\\ndrwxr-xr-x 1 jace","cwd":"/c/visual studio code/projekty/drmaterac.pl"}
{"ts":"2026-05-10T17:58:37Z","tool":"Bash","cmd":"cd /c/visual\\\\ studio\\\\ code/projekty/drmaterac.pl && ls -la src/ && find classes -name \"Product.php\" -o -name \"ObjectModel.php\" | head -5\"},\"tool_response\":{\"stdout\":\"total 8\\ndrwxr-xr","cwd":"/c/visual studio code/projekty/drmaterac.pl"}
{"ts":"2026-05-10T17:58:41Z","tool":"Bash","cmd":"cd /c/visual\\\\ studio\\\\ code/projekty/drmaterac.pl && find modules/crosssellpro -name \"*.sql\" -o -name \"sql*\" -type f\"},\"tool_response\":{\"stdout\":\"\",\"stderr\":\"\",\"interrupted\":fals","cwd":"/c/visual studio code/projekty/drmaterac.pl"}
{"ts":"2026-05-10T17:58:47Z","tool":"Bash","cmd":"wc -l \"C:\\\\visual studio code\\\\projekty\\\\drmaterac.pl\\\\.htaccess\"\"},\"tool_response\":{\"stdout\":\"429 C:\\\\visual studio code\\\\projekty\\\\drmaterac.pl\\\\.htaccess\",\"stderr\":\"\",\","cwd":"/c/visual studio code/projekty/drmaterac.pl"}
{"ts":"2026-05-10T17:58:47Z","tool":"Bash","cmd":"cd /c/visual\\\\ studio\\\\ code/projekty/drmaterac.pl && find themes -path \"*crosssellpro*\" -type f\"},\"tool_response\":{\"stdout\":\"\",\"stderr\":\"\",\"interrupted\":false,\"isImage\":false,\"n","cwd":"/c/visual studio code/projekty/drmaterac.pl"}
{"ts":"2026-05-10T17:58:52Z","tool":"Bash","cmd":"cd /c/visual\\\\ studio\\\\ code/projekty/drmaterac.pl && find src -type f && echo -e \"\\\\n--- Override summary ---\" && find override/classes -type f | wc -l\"},\"tool_response\":{\"stdout\":\"src/","cwd":"/c/visual studio code/projekty/drmaterac.pl"}
{"ts":"2026-05-10T17:59:01Z","tool":"Bash","cmd":"php -v 2>/dev/null || echo \"PHP check not available\"\"},\"tool_response\":{\"stdout\":\"PHP 8.2.12 (cli) (built: Oct 24 2023 21:15:15) (ZTS Visual C++ 2019 x64)\\nCopyright (c) The PHP Group\\nZend ","cwd":"/c/visual studio code/projekty/drmaterac.pl"}
{"ts":"2026-05-10T17:59:06Z","tool":"Bash","cmd":"cd /c/visual\\\\ studio\\\\ code/projekty/drmaterac.pl && ls -la config/ && echo -e \"\\\\n--- Controllers structure ---\" && ls -la controllers/ | head -30\"},\"tool_response\":{\"stdout\":\"total 11","cwd":"/c/visual studio code/projekty/drmaterac.pl"}
{"ts":"2026-05-10T17:59:12Z","tool":"Bash","cmd":"cd /c/visual\\\\ studio\\\\ code/projekty/drmaterac.pl && find . -maxdepth 1 -name \"*.php\" -type f | xargs ls -la\"},\"tool_response\":{\"stdout\":\"-rw-r--r-- 1 jacek 197121 4511 maj 10 19:37 ./bu","cwd":"/c/visual studio code/projekty/drmaterac.pl"}
{"ts":"2026-05-10T17:59:24Z","tool":"Bash","cmd":"cd /c/visual\\\\ studio\\\\ code/projekty/drmaterac.pl && grep -n \"getAccessories\" classes/Product.php | head -10\"},\"tool_response\":{\"stdout\":\"4587: public static function getAccessoriesLig","cwd":"/c/visual studio code/projekty/drmaterac.pl"}
{"ts":"2026-05-10T17:59:54Z","tool":"Bash","cmd":"cd \"C:\\\\visual studio code\\\\projekty\\\\drmaterac.pl\" && git log --oneline diag_20792_tmp.php diag_fix_tmp.php diag_upload_tmp.php info.php 2>/dev/null | head -10\"},\"tool_response\":{\"stdout\","cwd":"/c/visual studio code/projekty/drmaterac.pl"}
{"ts":"2026-05-10T17:59:57Z","tool":"Bash","cmd":"cd \"C:\\\\visual studio code\\\\projekty\\\\drmaterac.pl\" && git log --oneline --all | head -20\"},\"tool_response\":{\"stdout\":\"d6f5706a update\\na69616e8 update\\nbe3965e2 update\\n4a2135cf upda","cwd":"/c/visual studio code/projekty/drmaterac.pl"}
{"ts":"2026-05-10T18:00:05Z","tool":"Bash","cmd":"wc -l \"C:\\\\visual studio code\\\\projekty\\\\drmaterac.pl\\\\override\\\\classes\\\\Cart.php\" \"C:\\\\visual studio code\\\\projekty\\\\drmaterac.pl\\\\override\\\\classes\\\\Hook.php\"\"},\"tool_","cwd":"/c/visual studio code/projekty/drmaterac.pl"}
{"ts":"2026-05-10T18:00:36Z","tool":"Bash","cmd":"cd \"C:\\\\visual studio code\\\\projekty\\\\drmaterac.pl\" && git log --all --oneline --grep=\"diag\\\\|diagnostic\" 2>/dev/null\"},\"tool_response\":{\"stdout\":\"194405bc feat: update diagnostic sc","cwd":"/c/visual studio code/projekty/drmaterac.pl"}
{"ts":"2026-05-10T18:00:41Z","tool":"Bash","cmd":"wc -l \"C:\\\\visual studio code\\\\projekty\\\\drmaterac.pl\\\\.htaccess\" \"C:\\\\visual studio code\\\\projekty\\\\drmaterac.pl\\\\iadmin\\\\.htaccess\"\"},\"tool_response\":{\"stdout\":\" 429 C:\","cwd":"/c/visual studio code/projekty/drmaterac.pl"}
{"ts":"2026-05-10T18:00:45Z","tool":"Bash","cmd":"cd \"C:\\\\visual studio code\\\\projekty\\\\drmaterac.pl\" && git show 194405bc --stat 2>/dev/null | head -20\"},\"tool_response\":{\"stdout\":\"commit 194405bce0ceb5f263c9934c8a0d666ce7b629dd\\nAuth","cwd":"/c/visual studio code/projekty/drmaterac.pl"}
{"ts":"2026-05-10T18:01:31Z","tool":"Bash","cmd":"for f in stack architecture structure conventions testing integrations concerns db_schema tech_changelog; do echo \"===$f===\"; cat \"C:/Users/jacek/.claude/paul-framework/templates/codebase/$f.md\"; ","cwd":"/c/visual studio code/projekty/drmaterac.pl"}
{"ts":"2026-05-10T18:02:10Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\drmaterac.pl\\\\.paul\\\\codebase\\\\stack.md","cwd":"/c/visual studio code/projekty/drmaterac.pl"}
{"ts":"2026-05-10T18:02:59Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\drmaterac.pl\\\\.paul\\\\codebase\\\\architecture.md","cwd":"/c/visual studio code/projekty/drmaterac.pl"}
{"ts":"2026-05-10T18:03:50Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\drmaterac.pl\\\\.paul\\\\codebase\\\\structure.md","cwd":"/c/visual studio code/projekty/drmaterac.pl"}
{"ts":"2026-05-10T18:04:23Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\drmaterac.pl\\\\.paul\\\\codebase\\\\conventions.md","cwd":"/c/visual studio code/projekty/drmaterac.pl"}
{"ts":"2026-05-10T18:04:46Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\drmaterac.pl\\\\.paul\\\\codebase\\\\testing.md","cwd":"/c/visual studio code/projekty/drmaterac.pl"}
{"ts":"2026-05-10T18:05:28Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\drmaterac.pl\\\\.paul\\\\codebase\\\\integrations.md","cwd":"/c/visual studio code/projekty/drmaterac.pl"}
{"ts":"2026-05-10T18:06:35Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\drmaterac.pl\\\\.paul\\\\codebase\\\\concerns.md","cwd":"/c/visual studio code/projekty/drmaterac.pl"}
{"ts":"2026-05-10T18:07:07Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\drmaterac.pl\\\\.paul\\\\codebase\\\\db_schema.md","cwd":"/c/visual studio code/projekty/drmaterac.pl"}
{"ts":"2026-05-10T18:07:12Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\drmaterac.pl\\\\.paul\\\\codebase\\\\tech_changelog.md","cwd":"/c/visual studio code/projekty/drmaterac.pl"}