# MTEngine Changelog

All notable changes to MTEngine are documented here.
Format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.1/).

---

## [1.0.1] — 2026-04-06

### Fixed (Critical)
- `core/system/Version.php`: `CURRENT` updated from `1.0.0` to `1.0.1` — version was
  inconsistent across package, README, and CHANGELOG
- `plugins/SafeUpdater/Services/SafeUpdaterService.php`: `CURLOPT_SSL_VERIFYPEER`
  set to `true` and `CURLOPT_SSL_VERIFYHOST` to `2` — previously disabled SSL
  verification during package downloads, enabling MITM attacks on update flow
- `core/system/Router.php`: Added missing `POST /admin/footer/links/delete/{id}` route
  linked to existing `FooterController::deleteLink()` — UI showed delete button but
  requests returned 404
- `core/app/Controllers/FooterController.php`: Added `Csrf::check()` to `deleteLink()`
- `core/system/Router.php`: Removed duplicate `GET /orders/prepare-dhd/{id}` and
  duplicate `POST /orders/send-dhd/{id}` routes
- `core/system/Router.php`: `GET /shipments/control/run` converted to `POST`
- `admin/templates/shipping/control.php`: fetch call updated to POST with CSRF

### Fixed (Architecture — DB queries in templates)
- `admin/templates/login.php`: replaced `db()` call with `Settings` model
- `admin/templates/forgot_password.php`: replaced `db()` call with `Settings` model
- `admin/templates/header.php`: replaced raw `db()` calls with `Settings` and
  `AdminNotificationModel` — also added `getLatest()` method to `AdminNotificationModel`
- `admin/templates/dashboard.php`: replaced `db()` call with `ProductModel`
- `admin/templates/integrations/index.php`: replaced `db()` calls with `Settings` model
- `admin/templates/customers/marketing.php`: wrapped bare `db()` call in try/catch
- `admin/templates/products/create.php` and `edit.php`: replaced repeated AI
  `db()` queries with `AiService::fromProvider()` and `AiService::fromActiveProvider()`

### Added
- `core/app/Services/AiService::getDialect()` accessor
- `core/app/Models/AdminNotificationModel::getLatest(int $limit)` method
- `core/app/Models/WhatsAppMessageModel`: new model with `getPaginated()` and
  `getStats30d()` — extracted from template logic
- `themes/matjari/theme.json`: populated with name, version, RTL flag, supported
  sections, and locales (was empty `{}`)

### Fixed (Architecture — continued)
- `admin/templates/settings/whatsapp_logs/index.php`: all `db()` calls replaced
  with `WhatsAppMessageModel`
- `admin/templates/customers/marketing.php`: bare `db()` wrapped in try/catch

---

## [1.0.1] — 2026-04-06

### Security
- Converted state-changing admin GET routes to POST with CSRF protection:
  `/admin/languages/default/{id}`, `/admin/theme/active-product/set/{id}`,
  `/admin/settings/gsheets/disconnect`, `/admin/integrations/toggle`
- Removed duplicate GET `/admin/pages/delete/{id}` route (POST-only now)
- Added CSRF check to `PluginController::install()`, `enable()`, `disable()`
- Added `X-Content-Type-Options`, `X-Frame-Options`, and `Referrer-Policy`
  security headers via `.htaccess`
- Blocked direct web access to `.env`, `settings.json`, `composer.json`, and
  related sensitive files via `.htaccess`

### Changed (License System)
- `PING_TTL` raised from 30 seconds to 600 seconds (10 minutes) — eliminates
  excessive license server requests on every page load
- `PING_GRACE` raised from 600 seconds to 259200 seconds (72 hours) — site
  stays operational during temporary license server outages
- `VERIFY_TTL_NORMAL` raised from 3600 to 43200 seconds (12 hours)
- `MAX_GRACE_VERIFY` raised from 7200 to 259200 seconds (72 hours)

### SEO
- `hreflang` locale changed from `ar` to `ar-DZ` for accurate Algeria targeting
- Added `X-Robots-Tag: noindex, nofollow` HTTP header for `/order/success`,
  `/lp-order-form/`, `/cron/`, and `/license/` paths via `App::applyXRobotsTag()`
- `robots.txt` default rules extended: added `Disallow: /public/` and
  `Crawl-delay` directives for AhrefsBot and SemrushBot

### Fixed
- Admin templates updated to match POST-only routes:
  - `languages/index.php`: set-default link → POST form
  - `theme/active_product.php`: set-product link → POST form
  - `settings/gsheets_oauth.php`: disconnect link → POST form

---

## [1.0.1] — 2026-04-06

### Security
- Added CSRF token verification to `admin/assets/ajax/generate_product_description.php`
  (previously had auth guard but no CSRF check, inconsistent with the rest of the admin security layer)
- Introduced `core/system/HtmlSanitizer.php` — centralized HTML sanitization class
  that strips dangerous tags, event handlers, and javascript: URIs while preserving
  safe formatting markup
- Applied `HtmlSanitizer::sanitize()` to product description raw HTML output in
  `themes/matjari/templates/product.php` — prevents Stored XSS via product editor
- Applied `HtmlSanitizer::sanitize()` to AI landing page public view output in
  `AiLandingPageController::publicView()` — prevents Stored XSS via AI-generated content
- Applied `HtmlSanitizer::sanitize()` to custom HTML blocks in the landing page
  builder section renderer

### Fixed
- **Critical SEO fix**: Removed `ErrorDocument 404 /` and `ErrorDocument 403 /` from
  root `.htaccess`. These directives were causing Apache to serve the homepage body
  with a 200 status code for all 404/403 requests, creating soft-404s and harming
  search engine crawl quality. Replaced with `index.php?error=404/403` passthrough.
- `OrderLifecycleEventModel::ensureTable()` now uses a static guard correctly —
  schema migration runs at most once per PHP process instead of on every model instantiation

### Added
- `OrderModel::getPaginated(int $page, int $perPage, array $filters)` — paginated
  order retrieval with optional status and search filters for the admin order list
- `ProductModel::getPaginated(int $page, int $perPage, array $filters)` — paginated
  product retrieval with optional search, category, and status filters
- `install/upgrade_v1.0.0_to_v1.0.1.sql` — idempotent database migration script
  for upgrading from v1.0.0
- `README.md` — full product documentation with installation guide, requirements,
  directory structure, feature list, and troubleshooting
- `UPGRADE.md` — step-by-step upgrade instructions
- `CHANGELOG.md` — this file

### Changed
- `OrderController::index()` now uses `getPaginated()` instead of `getAll()`,
  passing `$totalPages`, `$totalOrders`, and `$currentPage` to the template
- `ProductController::index()` now uses `getPaginated()` instead of `getAll()`,
  passing pagination metadata to the template
- `OrderController::success()` now calls `SeoManager::set(['robots' => 'noindex, nofollow'])`
  to prevent the order confirmation page from being indexed
- `LandingOrderFormController` now outputs `<meta name="robots" content="noindex, nofollow">`
  to prevent the embedded checkout iframe page from being indexed
- `admin/assets/ajax/generate_product_description.php` now returns HTTP 422 with
  translated error message on validation failure instead of 200 with error flag

---

## [1.0.0] — Initial Release

- Full e-commerce engine for Arab/Algerian market
- Product management with EditorJS rich content editor
- AI-powered landing page builder (Google Gemini integration)
- Order management with full lifecycle tracking
- Multi-carrier shipping: Yalidine, ZR Express, Maystro, DHD, Ecotrack
- SEO toolkit: meta tags, Open Graph, Twitter Cards, JSON-LD schema, XML sitemap
- GTM, GA4, Facebook Pixel, TikTok Pixel integrations
- WhatsApp order notifications and tracking
- Google Sheets order export
- Full Arabic/English UI with RTL support
- Role-based team management
- Plugin system
- Theme system with live customizer
- Database backup and restore
- License-based activation system
